bundleLocationToBundleMap = new HashMap<>();
BundleContext frameworkBundleContext;
Framework felixFramework;
@@ -69,20 +72,6 @@ public class BundleHost {
//
}
- private static GhidraBundle newGhidraBundle(BundleHost bundleHost, ResourceFile bundlePath,
- boolean enabled, boolean systemBundle) {
- switch (GhidraBundle.getType(bundlePath)) {
- case SourceDir:
- return new GhidraSourceBundle(bundleHost, bundlePath, enabled, systemBundle);
- case Jar:
- return new GhidraJarBundle(bundleHost, bundlePath, enabled, systemBundle);
- case BndScript:
- default:
- break;
- }
- return new GhidraPlaceholderBundle(bundleHost, bundlePath, enabled, systemBundle);
- }
-
/**
* stop the framework.
*/
@@ -91,35 +80,18 @@ public class BundleHost {
}
/**
- * If there is currently a bundle managed with path {@code bundlePath}, return its {@link GhidraBundle},
- * otherwise return {@code null}.
- *
- * @param bundlePath the bundlePath of the sought bundle
- * @return a {@link GhidraBundle} or {@code null}
- */
- public GhidraBundle getExistingGhidraBundle(ResourceFile bundlePath) {
- GhidraBundle bundle = bundlePathToBundleMap.get(bundlePath);
- if (bundle == null) {
- Msg.showError(this, null, "ghidra bundle cache",
- "getExistingGhidraBundle expected a GhidraBundle created at " + bundlePath +
- " but none was found");
- }
- return bundle;
- }
-
- /**
- * If a {@link GhidraBundle} hasn't already been added for {@bundlePath}, add it now as a
+ * If a {@link GhidraBundle} hasn't already been added for {@bundleFile}, add it now as a
* non-system bundle.
*
- * Enable the bundle.
+ * Enable the bundle.
*
- * @param bundlePath the path to the bundle to (add and) enable
+ * @param bundleFile the bundle file to (add and) enable
* @return false if the bundle was already enabled
*/
- public boolean enablePath(ResourceFile bundlePath) {
- GhidraBundle bundle = bundlePathToBundleMap.get(bundlePath);
+ public boolean enable(ResourceFile bundleFile) {
+ GhidraBundle bundle = fileToBundleMap.get(bundleFile);
if (bundle == null) {
- bundle = add(bundlePath, true, false);
+ bundle = add(bundleFile, true, false);
return true;
}
return enable(bundle);
@@ -155,18 +127,49 @@ public class BundleHost {
return false;
}
+ /**
+ * If there is currently a bundle managed with file {@code bundleFile},
+ * return its {@link GhidraBundle}, otherwise return {@code null}.
+ *
+ * @param bundleFile the bundleFile of the sought bundle
+ * @return a {@link GhidraBundle} or {@code null}
+ */
+ public GhidraBundle getExistingGhidraBundle(ResourceFile bundleFile) {
+ GhidraBundle bundle = fileToBundleMap.get(bundleFile);
+ if (bundle == null) {
+ Msg.showError(this, null, "ghidra bundle cache",
+ "getExistingGhidraBundle expected a GhidraBundle created at " + bundleFile +
+ " but none was found");
+ }
+ return bundle;
+ }
+
+ private static GhidraBundle createGhidraBundle(BundleHost bundleHost, ResourceFile bundleFile,
+ boolean enabled, boolean systemBundle) {
+ switch (GhidraBundle.getType(bundleFile)) {
+ case SOURCE_DIR:
+ return new GhidraSourceBundle(bundleHost, bundleFile, enabled, systemBundle);
+ case JAR:
+ return new GhidraJarBundle(bundleHost, bundleFile, enabled, systemBundle);
+ case BND_SCRIPT:
+ default:
+ break;
+ }
+ return new GhidraPlaceholderBundle(bundleHost, bundleFile, enabled, systemBundle);
+ }
+
/**
* Create a new GhidraBundle and add to the list of managed bundles
*
- * @param bundlePath the bundle's path
+ * @param bundleFile the bundle file
* @param enabled if the new bundle should be enabled
* @param systemBundle if the new bundle is a system bundle
* @return a new GhidraBundle
*/
- public GhidraBundle add(ResourceFile bundlePath, boolean enabled, boolean systemBundle) {
- GhidraBundle bundle = newGhidraBundle(this, bundlePath, enabled, systemBundle);
- bundlePathToBundleMap.put(bundlePath, bundle);
- bundleLocationToBundleMap.put(bundle.getBundleLocation(), bundle);
+ public GhidraBundle add(ResourceFile bundleFile, boolean enabled, boolean systemBundle) {
+ GhidraBundle bundle = createGhidraBundle(this, bundleFile, enabled, systemBundle);
+ fileToBundleMap.put(bundleFile, bundle);
+ bundleLocationToBundleMap.put(bundle.getLocationIdentifier(), bundle);
fireBundleAdded(bundle);
return bundle;
}
@@ -175,19 +178,20 @@ public class BundleHost {
* Create new GhidraBundles and add to the list of managed bundles. All GhidraBundles created
* with the same {@code enabled} and {@code systemBundle} values.
*
- * @param bundlePaths a list of bundle paths
+ * @param bundleFiles a list of bundle files
* @param enabled if the new bundle should be enabled
* @param systemBundle if the new bundle is a system bundle
*/
- public void add(List bundlePaths, boolean enabled, boolean systemBundle) {
- Map newBundleMap = bundlePaths.stream()
+ public void add(List bundleFiles, boolean enabled, boolean systemBundle) {
+ Map newBundleMap = bundleFiles.stream()
.collect(Collectors.toUnmodifiableMap(Function.identity(),
- bundlePath -> newGhidraBundle(BundleHost.this, bundlePath, enabled, systemBundle)));
- bundlePathToBundleMap.putAll(newBundleMap);
+ bundleFile -> createGhidraBundle(BundleHost.this, bundleFile, enabled,
+ systemBundle)));
+ fileToBundleMap.putAll(newBundleMap);
bundleLocationToBundleMap.putAll(newBundleMap.values()
.stream()
- .collect(
- Collectors.toUnmodifiableMap(GhidraBundle::getBundleLocation, Function.identity())));
+ .collect(Collectors.toUnmodifiableMap(GhidraBundle::getLocationIdentifier,
+ Function.identity())));
fireBundlesAdded(newBundleMap.values());
}
@@ -196,10 +200,10 @@ public class BundleHost {
*
* @param bundles the bundles to add
*/
- public void add(List bundles) {
+ private void add(List bundles) {
for (GhidraBundle bundle : bundles) {
- bundlePathToBundleMap.put(bundle.getPath(), bundle);
- bundleLocationToBundleMap.put(bundle.getBundleLocation(), bundle);
+ fileToBundleMap.put(bundle.getFile(), bundle);
+ bundleLocationToBundleMap.put(bundle.getLocationIdentifier(), bundle);
}
fireBundlesAdded(bundles);
}
@@ -207,11 +211,11 @@ public class BundleHost {
/**
* Remove a bundle from the list of managed bundles.
*
- * @param bundlePath the path of the bundle to remove
+ * @param bundleFile the file of the bundle to remove
*/
- public void removeBundlePath(ResourceFile bundlePath) {
- GhidraBundle bundle = bundlePathToBundleMap.remove(bundlePath);
- bundleLocationToBundleMap.remove(bundle.getBundleLocation());
+ public void remove(ResourceFile bundleFile) {
+ GhidraBundle bundle = fileToBundleMap.remove(bundleFile);
+ bundleLocationToBundleMap.remove(bundle.getLocationIdentifier());
fireBundleRemoved(bundle);
}
@@ -220,9 +224,9 @@ public class BundleHost {
*
* @param bundleLocation the location id of the bundle to remove
*/
- public void removeBundleLoc(String bundleLocation) {
+ public void remove(String bundleLocation) {
GhidraBundle bundle = bundleLocationToBundleMap.remove(bundleLocation);
- bundlePathToBundleMap.remove(bundle.getPath());
+ fileToBundleMap.remove(bundle.getFile());
fireBundleRemoved(bundle);
}
@@ -232,8 +236,8 @@ public class BundleHost {
* @param bundle the bundle to remove
*/
public void remove(GhidraBundle bundle) {
- bundlePathToBundleMap.remove(bundle.getPath());
- bundleLocationToBundleMap.remove(bundle.getBundleLocation());
+ fileToBundleMap.remove(bundle.getFile());
+ bundleLocationToBundleMap.remove(bundle.getLocationIdentifier());
fireBundleRemoved(bundle);
}
@@ -244,8 +248,8 @@ public class BundleHost {
*/
public void remove(Collection bundles) {
for (GhidraBundle bundle : bundles) {
- bundlePathToBundleMap.remove(bundle.getPath());
- bundleLocationToBundleMap.remove(bundle.getBundleLocation());
+ fileToBundleMap.remove(bundle.getFile());
+ bundleLocationToBundleMap.remove(bundle.getLocationIdentifier());
}
fireBundlesRemoved(bundles);
}
@@ -257,17 +261,16 @@ public class BundleHost {
/**
* Try to install a bundle.
*
- *
* @param bundle the bundle to install
* @return the OSGi bundle returned by the framework
- * @throws GhidraBundleException when install fails
+ * @throws GhidraBundleException if install fails
*/
public Bundle install(GhidraBundle bundle) throws GhidraBundleException {
try {
- return frameworkBundleContext.installBundle(bundle.getBundleLocation());
+ return frameworkBundleContext.installBundle(bundle.getLocationIdentifier());
}
catch (BundleException e) {
- throw new GhidraBundleException(bundle.getBundleLocation(),
+ throw new GhidraBundleException(bundle.getLocationIdentifier(),
"installing from bundle location", e);
}
}
@@ -296,16 +299,16 @@ public class BundleHost {
* @return all the bundles
*/
public Collection getGhidraBundles() {
- return bundlePathToBundleMap.values();
+ return fileToBundleMap.values();
}
/**
- * return paths of currently managed bundles
+ * return the list of currently managed bundle files
*
- * @return all the bundle paths
+ * @return all the bundle files
*/
- public Collection getBundlePaths() {
- return bundlePathToBundleMap.keySet();
+ public Collection getBundleFiles() {
+ return fileToBundleMap.keySet();
}
void dumpLoadedBundles() {
@@ -319,28 +322,29 @@ public class BundleHost {
/**
* Attempt to resolve a list of BundleRequirements with active Bundle capabilities.
*
- * @param reqs list of requirements -- satisfied requirements are removed as capabiliites are found
- * @return the list of BundeWiring objects correpsonding to matching capabilities
+ * @param requirements list of requirements -- satisfied requirements are removed as
+ * capabilities are found
+ * @return list of {@link BundleWiring} objects corresponding to matching capabilities
*/
- public List resolve(List reqs) {
+ public List resolve(List requirements) {
// enumerate active bundles, looking for capabilities meeting our requirements
List bundleWirings = new ArrayList<>();
for (Bundle bundle : frameworkBundleContext.getBundles()) {
if (bundle.getState() == Bundle.ACTIVE) {
- BundleWiring bw = bundle.adapt(BundleWiring.class);
+ BundleWiring bundleWiring = bundle.adapt(BundleWiring.class);
boolean keeper = false;
- for (BundleCapability cap : bw.getCapabilities(null)) {
- Iterator it = reqs.iterator();
- while (it.hasNext()) {
- BundleRequirement req = it.next();
- if (req.matches(cap)) {
- it.remove();
+ for (BundleCapability capability : bundleWiring.getCapabilities(null)) {
+ Iterator requirementsIterator = requirements.iterator();
+ while (requirementsIterator.hasNext()) {
+ BundleRequirement requirement = requirementsIterator.next();
+ if (requirement.matches(capability)) {
+ requirementsIterator.remove();
keeper = true;
}
}
}
if (keeper) {
- bundleWirings.add(bw);
+ bundleWirings.add(bundleWiring);
}
}
}
@@ -351,7 +355,7 @@ public class BundleHost {
* Attempt to resolve {@code requirements} against the currently active bundles.
*
* @param requirements a list of {@link BundleRequirement} objects
- * @return true if all of the requiremetns can be resolved
+ * @return true if all of the requirements can be resolved
*/
public boolean canResolveAll(Collection requirements) {
LinkedList tmpRequirements = new LinkedList<>(requirements);
@@ -359,27 +363,6 @@ public class BundleHost {
return tmpRequirements.isEmpty();
}
- private class FelixStderrLogger extends Logger {
- @Override
- protected void doLog(int level, String message, Throwable throwable) {
- System.err.printf("felixlogger: %s %s\n", message, throwable);
- }
-
- @Override
- protected void doLogOut(int level, String message, Throwable throwable) {
- System.err.printf("felixlogger: %s %s\n", message, throwable);
- }
-
- @SuppressWarnings("rawtypes")
-
- @Override
- protected void doLog(final Bundle bundle, final ServiceReference sr, final int level,
- final String message, final Throwable throwable) {
- System.err.printf("felixlogger: %s %s %s\n", bundle, message, throwable);
- }
-
- }
-
protected String buildExtraSystemPackages() {
Set packages = new HashSet<>();
OSGiUtils.getPackagesFromClasspath(packages);
@@ -397,7 +380,8 @@ public class BundleHost {
}
/**
- * A directory for use by Felix as a cache
+ * A directory for use by the OSGi framework as a cache
+ *
* @return the directory
*/
protected static Path getCacheDir() {
@@ -432,7 +416,7 @@ public class BundleHost {
config.put(FelixConstants.LOG_LEVEL_PROP, "1");
if (STDERR_DEBUGGING) {
config.put(FelixConstants.LOG_LEVEL_PROP, "999");
- config.put(FelixConstants.LOG_LOGGER_PROP, new FelixStderrLogger());
+ // config.put(FelixConstants.LOG_LOGGER_PROP, new org.apache.felix.framework.Logger() {...});
}
FrameworkFactory factory = new FrameworkFactory();
@@ -465,54 +449,6 @@ public class BundleHost {
});
}
- /**
- * add the {@code BundleListener} that notifies listeners of bundle activation changes
- */
- protected void addBundleListener() {
- final Bundle systemBundle = frameworkBundleContext.getBundle();
- frameworkBundleContext.addBundleListener(new BundleListener() {
- @Override
- public void bundleChanged(BundleEvent event) {
- Bundle osgiBundle = event.getBundle();
-
- // ignore events on the system bundle
- if (osgiBundle == systemBundle) {
- return;
- }
- if (STDERR_DEBUGGING) {
- String n = osgiBundle.getSymbolicName();
- String l = osgiBundle.getLocation();
- System.err.printf("%s %s from %s\n", OSGiUtils.getEventTypeString(event), n, l);
- }
- GhidraBundle bundle;
- switch (event.getType()) {
- case BundleEvent.STARTED:
- bundle = bundleLocationToBundleMap.get(osgiBundle.getLocation());
- if (bundle != null) {
- fireBundleActivationChange(bundle, true);
- }
- else {
- Msg.error(this, String.format("not a GhidraBundle: %s\n",
- osgiBundle.getLocation()));
- }
- break;
- case BundleEvent.UNINSTALLED:
- bundle = bundleLocationToBundleMap.get(osgiBundle.getLocation());
- if (bundle != null) {
- fireBundleActivationChange(bundle, false);
- }
- else {
- Msg.error(this, String.format("not a GhidraBundle: %s\n",
- osgiBundle.getLocation()));
- }
- break;
- default:
- break;
- }
- }
- });
- }
-
/**
* start the framework
*
@@ -534,7 +470,8 @@ public class BundleHost {
addDebuggingListeners();
}
- addBundleListener();
+ frameworkBundleContext
+ .addBundleListener(new MyBundleListener(frameworkBundleContext.getBundle()));
try {
felixFramework.start();
@@ -551,10 +488,16 @@ public class BundleHost {
if (felixFramework != null) {
try {
felixFramework.stop();
- felixFramework.waitForStop(5000);
+ // any bundles that linger after a few seconds might be the source
+ // of subtle problems, so wait for them to stop and report any problems.
+ FrameworkEvent event = felixFramework.waitForStop(5000);
+ if (event.getType() == FrameworkEvent.WAIT_TIMEDOUT) {
+ Msg.error(this, "Stopping OSGi framework timed out after 5 seconds.");
+ }
felixFramework = null;
}
catch (BundleException | InterruptedException e) {
+ Msg.error(this, "Failed to stop OSGi framework.");
e.printStackTrace();
}
}
@@ -578,8 +521,8 @@ public class BundleHost {
}
private static boolean anyMatch(Bundle bundle, int... bundleStates) {
- Integer s = bundle.getState();
- return IntStream.of(bundleStates).anyMatch(s::equals);
+ Integer bundleState = bundle.getState();
+ return IntStream.of(bundleStates).anyMatch(bundleState::equals);
}
private static void waitFor(Bundle bundle, int... bundleStates) throws InterruptedException {
@@ -607,9 +550,10 @@ public class BundleHost {
bundle.start();
}
catch (BundleException e) {
- GhidraBundleException gbe = new GhidraBundleException(bundle, "activating bundle", e);
- fireBundleException(gbe);
- throw gbe;
+ GhidraBundleException bundleException =
+ new GhidraBundleException(bundle, "activating bundle", e);
+ fireBundleException(bundleException);
+ throw bundleException;
}
waitFor(bundle, Bundle.ACTIVE);
}
@@ -690,7 +634,7 @@ public class BundleHost {
protected void activateAll(Collection bundles, TaskMonitor monitor,
PrintWriter console) {
List bundlesRemaining = new ArrayList<>(bundles);
-
+
monitor.setMaximum(bundlesRemaining.size());
while (!bundlesRemaining.isEmpty() && !monitor.isCancelled()) {
List resolvableBundles = bundlesRemaining.stream()
@@ -704,14 +648,14 @@ public class BundleHost {
else {
bundlesRemaining.removeAll(resolvableBundles);
}
-
+
for (GhidraBundle bundle : resolvableBundles) {
if (monitor.isCancelled()) {
break;
}
try {
bundle.build(console);
- activateSynchronously(bundle.getBundleLocation());
+ activateSynchronously(bundle.getLocationIdentifier());
}
catch (Exception e) {
e.printStackTrace(console);
@@ -820,31 +764,31 @@ public class BundleHost {
/**
* Restore the list of managed bundles from {@code saveState} and each bundle's state.
*
- * Bundles that had been active are reactivated.
+ * Bundles that had been active are reactivated.
*
- * note: This is done once on startup, AFTER system bundles have been added.
+ *
note: This is done once on startup after system bundles have been added.
*
* @param saveState the state object
* @param tool the tool
*/
public void restoreManagedBundleState(SaveState saveState, PluginTool tool) {
- String[] bundlePaths = saveState.getStrings(saveStateTagPath, new String[0]);
+ String[] bundleFiles = saveState.getStrings(SAVE_STATE_TAG_FILE, new String[0]);
boolean[] bundleIsEnabled =
- saveState.getBooleans(saveStateTagEnabled, new boolean[bundlePaths.length]);
+ saveState.getBooleans(SAVE_STATE_TAG_ENABLE, new boolean[bundleFiles.length]);
boolean[] bundleIsActive =
- saveState.getBooleans(saveStateTagActive, new boolean[bundlePaths.length]);
+ saveState.getBooleans(SAVE_STATE_TAG_ACTIVE, new boolean[bundleFiles.length]);
boolean[] bundleIsSystem =
- saveState.getBooleans(saveStateTagSystem, new boolean[bundlePaths.length]);
+ saveState.getBooleans(SAVE_STATE_TAG_SYSTEM, new boolean[bundleFiles.length]);
List newBundles = new ArrayList<>();
List bundlesToActivate = new ArrayList<>();
- for (int i = 0; i < bundlePaths.length; i++) {
- ResourceFile bundlePath = generic.util.Path.fromPathString(bundlePaths[i]);
+ for (int i = 0; i < bundleFiles.length; i++) {
+ ResourceFile bundleFile = generic.util.Path.fromPathString(bundleFiles[i]);
boolean isEnabled = bundleIsEnabled[i];
boolean isActive = bundleIsActive[i];
boolean isSystem = bundleIsSystem[i];
- GhidraBundle bundle = bundlePathToBundleMap.get(bundlePath);
+ GhidraBundle bundle = fileToBundleMap.get(bundleFile);
if (bundle != null) {
if (isEnabled != bundle.isEnabled()) {
bundle.setEnabled(isEnabled);
@@ -852,7 +796,7 @@ public class BundleHost {
}
if (isSystem != bundle.isSystemBundle()) {
bundle.systemBundle = isSystem;
- Msg.error(this, String.format("%s went from %system to %system", bundlePath,
+ Msg.error(this, String.format("%s went from %system to %system", bundleFile,
isSystem ? "not " : "", isSystem ? "" : "not "));
}
}
@@ -860,7 +804,8 @@ public class BundleHost {
// stored system bundles that weren't already initialized must be old, drop 'm.
}
else {
- newBundles.add(bundle = newGhidraBundle(this, bundlePath, isEnabled, isSystem));
+ bundle = createGhidraBundle(this, bundleFile, isEnabled, isSystem);
+ newBundles.add(bundle);
}
if (bundle != null && isActive) {
bundlesToActivate.add(bundle);
@@ -876,25 +821,76 @@ public class BundleHost {
* @param saveState the state object
*/
public void saveManagedBundleState(SaveState saveState) {
- int numBundles = bundlePathToBundleMap.size();
- String[] bundlePaths = new String[numBundles];
+ int numBundles = fileToBundleMap.size();
+ String[] bundleFiles = new String[numBundles];
boolean[] bundleIsEnabled = new boolean[numBundles];
boolean[] bundleIsActive = new boolean[numBundles];
boolean[] bundleIsSystem = new boolean[numBundles];
int index = 0;
- for (GhidraBundle bundle : bundlePathToBundleMap.values()) {
- bundlePaths[index] = generic.util.Path.toPathString(bundle.getPath());
+ for (GhidraBundle bundle : fileToBundleMap.values()) {
+ bundleFiles[index] = generic.util.Path.toPathString(bundle.getFile());
bundleIsEnabled[index] = bundle.isEnabled();
bundleIsActive[index] = bundle.isActive();
bundleIsSystem[index] = bundle.isSystemBundle();
++index;
}
- saveState.putStrings(saveStateTagPath, bundlePaths);
- saveState.putBooleans(saveStateTagEnabled, bundleIsEnabled);
- saveState.putBooleans(saveStateTagActive, bundleIsActive);
- saveState.putBooleans(saveStateTagSystem, bundleIsSystem);
+ saveState.putStrings(SAVE_STATE_TAG_FILE, bundleFiles);
+ saveState.putBooleans(SAVE_STATE_TAG_ENABLE, bundleIsEnabled);
+ saveState.putBooleans(SAVE_STATE_TAG_ACTIVE, bundleIsActive);
+ saveState.putBooleans(SAVE_STATE_TAG_SYSTEM, bundleIsSystem);
}
+ /**
+ * The {@code BundleListener} that notifies {@link BundleHostListener}s of bundle activation changes
+ */
+ private class MyBundleListener implements BundleListener {
+ private final Bundle systemBundle;
+
+ private MyBundleListener(Bundle systemBundle) {
+ this.systemBundle = systemBundle;
+ }
+
+ @Override
+ public void bundleChanged(BundleEvent event) {
+ Bundle osgiBundle = event.getBundle();
+
+ // ignore events on the system bundle
+ if (osgiBundle == systemBundle) {
+ return;
+ }
+ if (STDERR_DEBUGGING) {
+ String symbolicName = osgiBundle.getSymbolicName();
+ String locationIdentifier = osgiBundle.getLocation();
+ System.err.printf("%s %s from %s\n", OSGiUtils.getEventTypeString(event),
+ symbolicName, locationIdentifier);
+ }
+ GhidraBundle bundle;
+ switch (event.getType()) {
+ case BundleEvent.STARTED:
+ bundle = bundleLocationToBundleMap.get(osgiBundle.getLocation());
+ if (bundle != null) {
+ fireBundleActivationChange(bundle, true);
+ }
+ else {
+ Msg.error(this,
+ String.format("not a GhidraBundle: %s\n", osgiBundle.getLocation()));
+ }
+ break;
+ case BundleEvent.UNINSTALLED:
+ bundle = bundleLocationToBundleMap.get(osgiBundle.getLocation());
+ if (bundle != null) {
+ fireBundleActivationChange(bundle, false);
+ }
+ else {
+ Msg.error(this,
+ String.format("not a GhidraBundle: %s\n", osgiBundle.getLocation()));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatus.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatus.java
index d2e46d988d..c6432276f7 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatus.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatus.java
@@ -19,34 +19,38 @@ import generic.jar.ResourceFile;
import generic.util.Path;
/**
- * The BundleStatus class represents the runtime state and user preferences for OSGi bundles in Ghidra.
+ * The BundleStatus class represents the runtime state and user preferences for bundles.
*/
public class BundleStatus implements Comparable {
- final Path path;
- final GhidraBundle.Type type;
- final String bundleLocation;
+ private final GhidraBundle.Type type;
+ private final String location;
- boolean active = false;
- boolean busy = false;
+ private final ResourceFile file;
+ private final boolean readOnly;
+ private boolean enabled;
+ private boolean active = false;
+ private boolean busy = false;
- String summary;
+ private String summary;
- BundleStatus(ResourceFile path, boolean enabled, boolean readonly, String bundleLoc) {
- this.path = new Path(path, enabled, false, readonly);
- type = GhidraBundle.getType(getPath());
- this.bundleLocation = bundleLoc;
+ BundleStatus(ResourceFile bundleFile, boolean enabled, boolean readOnly, String bundleLoc) {
+ this.file = bundleFile;
+ type = GhidraBundle.getType(getFile());
+ this.location = bundleLoc;
+ this.enabled = enabled;
+ this.readOnly = readOnly;
}
@Override
public int compareTo(BundleStatus o) {
- return path.compareTo(o != null ? o.path : null);
+ return Path.toPathString(file).compareTo(o != null ? Path.toPathString(o.file) : null);
}
/**
* @return true if the bundle is enabled
*/
public boolean isEnabled() {
- return path.isEnabled();
+ return enabled;
}
/**
@@ -55,14 +59,14 @@ public class BundleStatus implements Comparable {
* @param isEnabled true to set status to enabled
*/
public void setEnabled(boolean isEnabled) {
- path.setEnabled(isEnabled);
+ this.enabled = isEnabled;
}
/**
* @return true if the bundle is read only
*/
public boolean isReadOnly() {
- return path.isReadOnly();
+ return readOnly;
}
/**
@@ -107,31 +111,31 @@ public class BundleStatus implements Comparable {
}
/**
- * @return the bundle's path
+ * @return the bundle file
*/
- public ResourceFile getPath() {
- return path.getPath();
+ public ResourceFile getFile() {
+ return file;
}
/**
- * @return true if the bundle's path exists
+ * @return true if the bundle file exists
*/
- public boolean pathExists() {
- return path.exists();
+ public boolean fileExists() {
+ return file.exists();
}
/**
- * @return the bundle's path as a string, using $USER and $GHIDRA_HOME when appropriate
+ * @return the bundle file path, using $USER and $GHIDRA_HOME when appropriate
*/
public String getPathAsString() {
- return path.getPathAsString();
+ return Path.toPathString(file);
}
/**
* @return the bundle's location identifier
*/
- public String getBundleLocation() {
- return bundleLocation;
+ public String getLocationIdentifier() {
+ return location;
}
void setBusy(boolean isBusy) {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusComponentProvider.java
index 9691c3fc7e..6e737d628b 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusComponentProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusComponentProvider.java
@@ -25,8 +25,7 @@ import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableColumn;
-import docking.ActionContext;
-import docking.action.*;
+import docking.action.builder.ActionBuilder;
import docking.util.AnimationUtils;
import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.filechooser.GhidraFileChooserMode;
@@ -79,32 +78,7 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
this.bundleHost = bundleHost;
this.bundleStatusTableModel = new BundleStatusTableModel(this, bundleHost);
- bundleStatusTableModel.addListener(new BundleStatusChangeRequestListener() {
- @Override
- public void bundleEnablementChangeRequest(BundleStatus status, boolean enabled) {
- GhidraBundle gb = bundleHost.getExistingGhidraBundle(status.getPath());
- if (gb instanceof GhidraPlaceholderBundle) {
- return;
- }
- if (enabled) {
- bundleHost.enable(gb);
- }
- else {
- if (status.isActive()) {
- startActivateDeactiveTask(status, false);
- }
- bundleHost.disable(gb);
- }
- }
-
- @Override
- public void bundleActivationChangeRequest(BundleStatus status, boolean newValue) {
- if (status.isEnabled()) {
- startActivateDeactiveTask(status, newValue);
- }
- }
-
- });
+ bundleStatusTableModel.addListener(new MyBundleStatusChangeRequestListener());
this.filter = new GhidraFileFilter() {
@Override
@@ -113,8 +87,8 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
}
@Override
- public boolean accept(File path, GhidraFileChooserModel model) {
- return GhidraBundle.getType(path) != GhidraBundle.Type.INVALID;
+ public boolean accept(File file, GhidraFileChooserModel model) {
+ return GhidraBundle.getType(file) != GhidraBundle.Type.INVALID;
}
};
this.fileChooser = null;
@@ -178,7 +152,7 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
BundleStatus status = (BundleStatus) data.getRowObject();
- Component x = super.getTableCellRendererComponent(data);
+ Component component = super.getTableCellRendererComponent(data);
if (status.isBusy()) {
cb.setVisible(false);
cb.setEnabled(false);
@@ -190,7 +164,7 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
cb.setEnabled(true);
setText("");
}
- return x;
+ return component;
}
});
@@ -199,69 +173,40 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
bundleStatusTable.getColumnModel().getColumn(bundleStatusTableModel.typeColumn.index);
FontMetrics fontmetrics = panel.getFontMetrics(panel.getFont());
column.setMaxWidth(10 +
- SwingUtilities.computeStringWidth(fontmetrics, GhidraBundle.Type.SourceDir.toString()));
+ SwingUtilities.computeStringWidth(fontmetrics, GhidraBundle.Type.SOURCE_DIR.toString()));
column =
bundleStatusTable.getColumnModel().getColumn(bundleStatusTableModel.pathColumn.index);
column.setCellRenderer(new GTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(GTableCellRenderingData data) {
- ResourceFile path = (ResourceFile) data.getValue();
- JLabel c = (JLabel) super.getTableCellRendererComponent(data);
- c.setText(Path.toPathString(path));
- GhidraBundle gb = bundleHost.getExistingGhidraBundle(path);
- if (gb == null || gb instanceof GhidraPlaceholderBundle || !path.exists()) {
- c.setForeground(Color.RED);
+ ResourceFile file = (ResourceFile) data.getValue();
+ JLabel label = (JLabel) super.getTableCellRendererComponent(data);
+ label.setText(Path.toPathString(file));
+ GhidraBundle bundle = bundleHost.getExistingGhidraBundle(file);
+ if (bundle == null || bundle instanceof GhidraPlaceholderBundle || !file.exists()) {
+ label.setForeground(Color.RED);
}
- return c;
+ return label;
}
});
}
private void addBundlesAction(String actionName, String description, Icon icon,
Runnable runnable) {
- DockingAction action = new DockingAction(actionName, this.getName()) {
- @Override
- public void actionPerformed(ActionContext context) {
- runnable.run();
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- return bundleStatusTable.getSelectedRows().length > 0;
- }
- };
- action.setPopupMenuData(new MenuData(new String[] { description }, icon, BUNDLE_GROUP));
- action.setToolBarData(new ToolBarData(icon, BUNDLE_GROUP));
- action.setDescription(description);
- action.setEnabled(false);
- getTool().addLocalAction(this, action);
-
- }
-
- private void addBundleListAction(String actionName, String name, String description, Icon icon,
- Runnable runnable) {
- DockingAction action = new DockingAction(actionName, this.getName()) {
- @Override
- public void actionPerformed(ActionContext context) {
- runnable.run();
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- return true;
- }
- };
- action.setPopupMenuData(new MenuData(new String[] { name }, icon, BUNDLE_LIST_GROUP));
- action.setToolBarData(new ToolBarData(icon, BUNDLE_LIST_GROUP));
- action.setDescription(description);
- action.setEnabled(true);
- getTool().addLocalAction(this, action);
+ new ActionBuilder(actionName, this.getName()).popupMenuPath(description)
+ .popupMenuIcon(icon)
+ .popupMenuGroup(BUNDLE_GROUP)
+ .toolBarIcon(icon)
+ .toolBarGroup(BUNDLE_GROUP)
+ .description(description)
+ .enabled(false)
+ .enabledWhen(context -> bundleStatusTable.getSelectedRows().length > 0)
+ .onAction(context -> runnable.run())
+ .buildAndInstallLocal(this);
}
private void createActions() {
- DockingAction action;
-
addBundlesAction("ActivateBundles", "Activate bundle(s)",
ResourceManager.loadImage("images/media-playback-start.png"), this::doActivateBundles);
@@ -271,47 +216,26 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
addBundlesAction("CleanBundles", "Clean bundle(s)",
ResourceManager.loadImage("images/erase16.png"), this::doClean);
- //
- action = new DockingAction("AddBundles", this.getName()) {
- @Override
- public void actionPerformed(ActionContext context) {
- showAddBundlesFileChooser();
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- return true;
- }
-
- };
Icon icon = ResourceManager.loadImage("images/Plus.png");
- action.setPopupMenuData(
- new MenuData(new String[] { "Add bundle(s)" }, icon, BUNDLE_LIST_GROUP));
- action.setToolBarData(new ToolBarData(icon, BUNDLE_LIST_GROUP));
- action.setDescription("Display file chooser to add bundles to list");
- action.setEnabled(true);
- getTool().addLocalAction(this, action);
+ new ActionBuilder("AddBundles", this.getName()).popupMenuPath("Add Bundle(s)")
+ .popupMenuIcon(icon)
+ .popupMenuGroup(BUNDLE_LIST_GROUP)
+ .toolBarIcon(icon)
+ .toolBarGroup(BUNDLE_LIST_GROUP)
+ .description("Display file chooser to add bundles to list")
+ .onAction(c -> showAddBundlesFileChooser())
+ .buildAndInstallLocal(this);
- //
icon = ResourceManager.loadImage("images/edit-delete.png");
- action = new DockingAction("RemoveBundles", this.getName()) {
- @Override
- public void actionPerformed(ActionContext context) {
- doRemoveBundles();
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- return bundleStatusTable.getSelectedRows().length > 0;
- }
-
- };
- action.setPopupMenuData(
- new MenuData(new String[] { "Remove bundle(s)" }, icon, BUNDLE_LIST_GROUP));
- action.setToolBarData(new ToolBarData(icon, BUNDLE_LIST_GROUP));
- action.setDescription("Remove selected bundle(s) from the list");
- action.setEnabled(true);
- getTool().addLocalAction(this, action);
+ new ActionBuilder("RemoveBundles", this.getName()).popupMenuPath("Remove bundle(s)")
+ .popupMenuIcon(icon)
+ .popupMenuGroup(BUNDLE_LIST_GROUP)
+ .toolBarIcon(icon)
+ .toolBarGroup(BUNDLE_LIST_GROUP)
+ .description("Remove selected bundle(s) from the list")
+ .enabledWhen(c -> bundleStatusTable.getSelectedRows().length > 0)
+ .onAction(c -> doRemoveBundles())
+ .buildAndInstallLocal(this);
}
/**
@@ -330,10 +254,10 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
private void doClean() {
int[] selectedModelRows = getSelectedModelRows();
boolean anythingCleaned = false;
- for (BundleStatus bs : bundleStatusTableModel.getRowObjects(selectedModelRows)) {
- anythingCleaned |= bundleHost.getExistingGhidraBundle(bs.getPath()).clean();
- if (!bs.getSummary().isEmpty()) {
- bs.setSummary("");
+ for (BundleStatus status : bundleStatusTableModel.getRowObjects(selectedModelRows)) {
+ anythingCleaned |= bundleHost.getExistingGhidraBundle(status.getFile()).clean();
+ if (!status.getSummary().isEmpty()) {
+ status.setSummary("");
anythingCleaned |= true;
}
}
@@ -350,25 +274,25 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
}
doDeactivateBundles();
+ // partition bundles into system (bundles.get(true)) and non-system (bundles.get(false)).
Map> bundles =
bundleStatusTableModel.getRowObjects(selectedModelRows)
.stream()
- .map(bs -> bundleHost.getExistingGhidraBundle(bs.getPath()))
- .collect(Collectors.partitioningBy(gb -> gb.isSystemBundle()));
+ .map(bs -> bundleHost.getExistingGhidraBundle(bs.getFile()))
+ .collect(Collectors.partitioningBy(GhidraBundle::isSystemBundle));
+
List systemBundles = bundles.get(true);
if (!systemBundles.isEmpty()) {
- StringBuilder sb = new StringBuilder();
- for (GhidraBundle gb : systemBundles) {
- bundleHost.disable(gb);
- sb.append(gb.getPath() + "\n");
+ StringBuilder stringBuilder = new StringBuilder();
+ for (GhidraBundle bundle : systemBundles) {
+ bundleHost.disable(bundle);
+ stringBuilder.append(bundle.getFile() + "\n");
}
Msg.showWarn(this, this.getComponent(), "Unabled to remove",
- "System bundles cannot be removed:\n" + sb.toString());
-
+ "System bundles cannot be removed:\n" + stringBuilder.toString());
}
bundleHost.remove(bundles.get(false));
-
}
private void showAddBundlesFileChooser() {
@@ -393,15 +317,15 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
}
String lastSelected = Preferences.getProperty(PREFENCE_LAST_SELECTED_BUNDLE);
if (lastSelected != null) {
- File f = new File(lastSelected);
- fileChooser.setSelectedFile(f);
+ File lastSelectedFile = new File(lastSelected);
+ fileChooser.setSelectedFile(lastSelectedFile);
}
}
else {
String lastSelected = Preferences.getProperty(PREFENCE_LAST_SELECTED_BUNDLE);
if (lastSelected != null) {
- File f = new File(lastSelected);
- fileChooser.setSelectedFile(f);
+ File lastSelectedFile = new File(lastSelected);
+ fileChooser.setSelectedFile(lastSelectedFile);
}
fileChooser.rescanCurrentDirectory();
}
@@ -417,104 +341,24 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
}
protected void doActivateBundles() {
- int[] selectedModelRows = getSelectedModelRows();
-
- new TaskLauncher(new Task("activating", true, true, false) {
- @Override
- public void run(TaskMonitor monitor) throws CancelledException {
- // suppress RowObjectSelectionManager repairs until after we're done
- bundleStatusTable.chill();
-
- List statuses =
- bundleStatusTableModel.getRowObjects(selectedModelRows)
- .stream()
- .filter(bs -> !bs.isActive())
- .collect(Collectors.toUnmodifiableList());
-
- List gbs = new ArrayList<>();
- for (BundleStatus bs : statuses) {
- GhidraBundle gb = bundleHost.getExistingGhidraBundle(bs.getPath());
- if (!(gb instanceof GhidraPlaceholderBundle)) {
- bs.setBusy(true);
- bundleHost.enable(gb);
- gbs.add(gb);
- }
- }
- notifyTableDataChanged();
-
- bundleHost.activateAll(gbs, monitor,
- getTool().getService(ConsoleService.class).getStdErr());
-
- boolean anybusy = false;
- for (BundleStatus bs : statuses) {
- if (bs.isBusy()) {
- anybusy = true;
- bs.setBusy(false);
- }
- }
- if (anybusy) {
- notifyTableDataChanged();
- }
-
- bundleStatusTable.thaw();
- }
- }, getComponent(), 1000);
+ new TaskLauncher(
+ new ActivateBundlesTask("activating", true, true, false, getSelectedModelRows()),
+ getComponent(), 1000);
}
protected void doDeactivateBundles() {
- ConsoleService console = getTool().getService(ConsoleService.class);
- int[] selectedModelRows = getSelectedModelRows();
-
- new TaskLauncher(new Task("deactivating", true, true, false) {
- @Override
- public void run(TaskMonitor monitor) throws CancelledException {
- List gbs = bundleStatusTableModel.getRowObjects(selectedModelRows)
- .stream()
- .filter(bs -> bs.isActive())
- .map(bs -> bundleHost.getExistingGhidraBundle(bs.getPath()))
- .collect(Collectors.toList());
-
- monitor.setMaximum(gbs.size());
- for (GhidraBundle gb : gbs) {
- try {
- bundleHost.deactivateSynchronously(gb.getBundleLocation());
- }
- catch (GhidraBundleException | InterruptedException e) {
- e.printStackTrace(console.getStdErr());
- }
- monitor.incrementProgress(1);
- }
- }
- }, getComponent(), 1000);
+ new TaskLauncher(
+ new DeactivateBundlesTask("deactivating", true, true, false, getSelectedModelRows()),
+ getComponent(), 1000);
}
- protected void startActivateDeactiveTask(BundleStatus status, boolean activate) {
+ protected void doActivateDeactivateBundle(BundleStatus status, boolean activate) {
status.setBusy(true);
notifyTableRowChanged(status);
- ConsoleService console = getTool().getService(ConsoleService.class);
-
- new TaskLauncher(new Task((activate ? "Activating" : "Deactivating ") + " bundle...") {
- @Override
- public void run(TaskMonitor monitor) throws CancelledException {
- try {
- GhidraBundle gb = bundleHost.getExistingGhidraBundle(status.getPath());
- if (activate) {
- gb.build(console.getStdErr());
- bundleHost.activateSynchronously(gb.getBundleLocation());
- }
- else { // deactivate
- bundleHost.deactivateSynchronously(gb.getBundleLocation());
- }
- }
- catch (Exception e) {
- e.printStackTrace(console.getStdErr());
- }
- finally {
- status.setBusy(false);
- notifyTableRowChanged(status);
- }
- }
- }, null, 1000);
+ new TaskLauncher(
+ new ActivateDeactivateBundleTask(
+ (activate ? "Activating" : "Deactivating ") + " bundle...", status, activate),
+ null, 1000);
}
private void notifyTableRowChanged(BundleStatus status) {
@@ -554,14 +398,159 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
/**
* This is for testing only! during normal execution, statuses are only added through BundleHostListener bundle(s) added events.
*
- * each path is marked editable and non-readonly
+ * each new bundle will be enabled and writable
*
- * @param bundlePaths the paths to use
+ * @param bundleFiles the files to use
*/
- public void setPathsForTesting(List bundlePaths) {
- bundleStatusTableModel.setModelData(bundlePaths.stream()
+ public void setBundleFilesForTesting(List bundleFiles) {
+ bundleStatusTableModel.setModelData(bundleFiles.stream()
.map(f -> new BundleStatus(f, true, false, null))
.collect(Collectors.toList()));
}
+ private class ActivateBundlesTask extends Task {
+ private final int[] selectedModelRows;
+
+ private ActivateBundlesTask(String title, boolean canCancel, boolean hasProgress,
+ boolean isModal, int[] selectedModelRows) {
+ super(title, canCancel, hasProgress, isModal);
+ this.selectedModelRows = selectedModelRows;
+ }
+
+ @Override
+ public void run(TaskMonitor monitor) throws CancelledException {
+ // suppress RowObjectSelectionManager repairs until after we're done
+ bundleStatusTable.chill();
+
+ List statuses = bundleStatusTableModel.getRowObjects(selectedModelRows)
+ .stream()
+ .filter(bs -> !bs.isActive())
+ .collect(Collectors.toUnmodifiableList());
+
+ List bundles = new ArrayList<>();
+ for (BundleStatus status : statuses) {
+ GhidraBundle bundle = bundleHost.getExistingGhidraBundle(status.getFile());
+ if (!(bundle instanceof GhidraPlaceholderBundle)) {
+ status.setBusy(true);
+ bundleHost.enable(bundle);
+ bundles.add(bundle);
+ }
+ }
+ notifyTableDataChanged();
+
+ bundleHost.activateAll(bundles, monitor,
+ getTool().getService(ConsoleService.class).getStdErr());
+
+ boolean anybusy = false;
+ for (BundleStatus status : statuses) {
+ if (status.isBusy()) {
+ anybusy = true;
+ status.setBusy(false);
+ }
+ }
+ if (anybusy) {
+ notifyTableDataChanged();
+ }
+
+ bundleStatusTable.thaw();
+ }
+ }
+
+ private class DeactivateBundlesTask extends Task {
+ private final int[] selectedModelRows;
+
+ private DeactivateBundlesTask(String title, boolean canCancel, boolean hasProgress,
+ boolean isModal, int[] selectedModelRows) {
+ super(title, canCancel, hasProgress, isModal);
+ this.selectedModelRows = selectedModelRows;
+ }
+
+ @Override
+ public void run(TaskMonitor monitor) throws CancelledException {
+ List bundles = bundleStatusTableModel.getRowObjects(selectedModelRows)
+ .stream()
+ .filter(bs -> bs.isActive())
+ .map(bs -> bundleHost.getExistingGhidraBundle(bs.getFile()))
+ .collect(Collectors.toList());
+
+ monitor.setMaximum(bundles.size());
+ for (GhidraBundle bundle : bundles) {
+ try {
+ bundleHost.deactivateSynchronously(bundle.getLocationIdentifier());
+ }
+ catch (GhidraBundleException | InterruptedException e) {
+ ConsoleService console = getTool().getService(ConsoleService.class);
+ e.printStackTrace(console.getStdErr());
+ }
+ monitor.incrementProgress(1);
+ }
+ }
+ }
+
+ /*
+ * Activating/deactivating a single bundle doesn't require resolving dependents,
+ * so this task is slightly different from the others.
+ */
+ private class ActivateDeactivateBundleTask extends Task {
+ private final BundleStatus status;
+ private final boolean activate;
+
+ private ActivateDeactivateBundleTask(String title, BundleStatus status, boolean activate) {
+ super(title);
+ this.status = status;
+ this.activate = activate;
+ }
+
+ @Override
+ public void run(TaskMonitor monitor) throws CancelledException {
+ ConsoleService console = getTool().getService(ConsoleService.class);
+ try {
+ GhidraBundle bundle = bundleHost.getExistingGhidraBundle(status.getFile());
+ if (activate) {
+ bundle.build(console.getStdErr());
+ bundleHost.activateSynchronously(bundle.getLocationIdentifier());
+ }
+ else { // deactivate
+ bundleHost.deactivateSynchronously(bundle.getLocationIdentifier());
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace(console.getStdErr());
+ }
+ finally {
+ status.setBusy(false);
+ notifyTableRowChanged(status);
+ }
+ }
+ }
+
+ /**
+ * Listener that responds to change requests from the {@link BundleStatusTableModel}.
+ */
+ private class MyBundleStatusChangeRequestListener implements BundleStatusChangeRequestListener {
+ @Override
+ public void bundleEnablementChangeRequest(BundleStatus status, boolean enabled) {
+ GhidraBundle bundle = bundleHost.getExistingGhidraBundle(status.getFile());
+ if (bundle instanceof GhidraPlaceholderBundle) {
+ return;
+ }
+ if (enabled) {
+ bundleHost.enable(bundle);
+ }
+ else {
+ if (status.isActive()) {
+ doActivateDeactivateBundle(status, false);
+ }
+ bundleHost.disable(bundle);
+ }
+ }
+
+ @Override
+ public void bundleActivationChangeRequest(BundleStatus status, boolean newValue) {
+ if (status.isEnabled()) {
+ doActivateDeactivateBundle(status, newValue);
+ }
+ }
+ }
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusTableModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusTableModel.java
index da06e93306..565b3ec4b1 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusTableModel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleStatusTableModel.java
@@ -16,46 +16,28 @@
package ghidra.app.plugin.core.osgi;
import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
+import javax.swing.SwingUtilities;
+import javax.swing.event.TableModelEvent;
+
import docking.widgets.table.AbstractSortedTableModel;
import docking.widgets.table.TableSortingContext;
import generic.jar.ResourceFile;
import ghidra.util.Msg;
+import ghidra.util.SystemUtilities;
+/**
+ * Model for {@link BundleStatus} objects.
+ */
public class BundleStatusTableModel extends AbstractSortedTableModel {
List columns = new ArrayList<>();
- class Column {
- final Class> clazz;
- final int index;
- final String name;
-
- Column(String name, Class> clazz) {
- this.name = name;
- this.index = columns.size();
- columns.add(this);
- this.clazz = clazz;
- }
-
- boolean editable(BundleStatus status) {
- return false;
- }
-
- Object getValue(BundleStatus status) {
- return null;
- }
-
- void setValue(BundleStatus status, Object aValue) {
- throw new RuntimeException(name + " is not editable!");
- }
-
- }
-
Column enabledColumn = new Column("Enabled", Boolean.class) {
@Override
boolean editable(BundleStatus status) {
- return status.pathExists();
+ return status.fileExists();
}
@Override
@@ -71,7 +53,7 @@ public class BundleStatusTableModel extends AbstractSortedTableModel bundleLocToStatusMap = new HashMap<>();
private BundleHostListener bundleHostListener;
- private ArrayList bundleStatusListeners = new ArrayList<>();
+ private List bundleStatusListeners =
+ new CopyOnWriteArrayList<>();
private List statuses;
- protected class MyBundleHostListener implements BundleHostListener {
- @Override
- public void bundleBuilt(GhidraBundle bundle, String summary) {
- BundleStatus status = getStatus(bundle);
- status.setSummary(summary);
- int row = getRowIndex(status);
- fireTableRowsUpdated(row, row);
- }
-
- @Override
- public void bundleActivationChange(GhidraBundle bundle, boolean newActivation) {
- BundleStatus status = getStatus(bundle);
- int row = getRowIndex(status);
- status.setBusy(false);
- if (newActivation) {
- status.setActive(true);
- }
- else {
- status.setActive(false);
- }
- fireTableRowsUpdated(row, row);
- }
-
- @Override
- public void bundleAdded(GhidraBundle bundle) {
- addNewStatus(bundle);
- }
-
- @Override
- public void bundlesAdded(Collection bundles) {
- int index = statuses.size();
- for (GhidraBundle bundle : bundles) {
- addNewStatusNoFire(bundle);
- }
- fireTableRowsInserted(index, bundles.size() - 1);
- }
-
- @Override
- public void bundleRemoved(GhidraBundle bundle) {
- BundleStatus status = getStatus(bundle);
- removeStatus(status);
- }
-
- @Override
- public void bundlesRemoved(Collection bundles) {
- List toRemove = bundles.stream()
- .map(BundleStatusTableModel.this::getStatus)
- .collect(Collectors.toUnmodifiableList());
- removeStatuses(toRemove);
- }
-
- @Override
- public void bundleEnablementChange(GhidraBundle bundle, boolean newEnablement) {
- BundleStatus status = getStatus(bundle);
- status.setEnabled(newEnablement);
- int row = getRowIndex(status);
- fireTableRowsUpdated(row, row);
- }
-
- @Override
- public void bundleException(GhidraBundleException exception) {
- BundleStatus status = getStatusFromLoc(exception.getBundleLocation());
- status.setSummary(exception.getMessage());
- int row = getRowIndex(status);
- fireTableRowsUpdated(row, row);
- }
- }
-
BundleStatusTableModel(BundleStatusComponentProvider provider, BundleHost bundleHost) {
super();
this.provider = provider;
@@ -195,7 +110,8 @@ public class BundleStatusTableModel extends AbstractSortedTableModel BundleStatusTableModel.super.fireTableChanged(e1));
+ }
+
private void addNewStatusNoFire(GhidraBundle bundle) {
- BundleStatus status = new BundleStatus(bundle.getPath(), bundle.isEnabled(),
- bundle.isSystemBundle(), bundle.getBundleLocation());
+ BundleStatus status = new BundleStatus(bundle.getFile(), bundle.isEnabled(),
+ bundle.isSystemBundle(), bundle.getLocationIdentifier());
if (statuses.contains(status)) {
throw new RuntimeException(
- "Bundle status manager already contains " + bundle.getPath().toString());
+ "Bundle status manager already contains " + bundle.getFile().toString());
}
status.setActive(bundle.isActive());
- bundleLocToStatusMap.put(status.getBundleLocation(), status);
+ bundleLocToStatusMap.put(status.getLocationIdentifier(), status);
statuses.add(status);
}
@@ -247,10 +173,10 @@ public class BundleStatusTableModel extends AbstractSortedTableModel getColumnClass(int columnIndex) {
+ public Class> getColumnClass(int columnIndex) {
return getColumn(columnIndex).clazz;
}
@@ -341,15 +267,13 @@ public class BundleStatusTableModel extends AbstractSortedTableModelWhen the user requests a change to the status of a bundle, each listener is called.
*
* @param listener the listener to add
*/
public void addListener(BundleStatusChangeRequestListener listener) {
- synchronized (bundleStatusListeners) {
- if (!bundleStatusListeners.contains(listener)) {
- bundleStatusListeners.add(listener);
- }
+ if (!bundleStatusListeners.contains(listener)) {
+ bundleStatusListeners.add(listener);
}
}
@@ -359,24 +283,18 @@ public class BundleStatusTableModel extends AbstractSortedTableModel data, TableSortingContext sortingContext) {
+ // notify accesses listeners, must be done on swing thread
+ SystemUtilities.assertThisIsTheSwingThread("Must be called on the Swing thread");
if (sortingContext.isUnsorted()) {
// this is the 'no sort' state
@@ -409,22 +329,23 @@ public class BundleStatusTableModel extends AbstractSortedTableModel proxy = new Comparator() {
- Comparator p = sortingContext.getComparator();
+ // wrap the assigned comparator to detect if the order changes
+ boolean[] changed = { false };
+ Comparator wrapper = new Comparator() {
+ Comparator comparator = sortingContext.getComparator();
@Override
public int compare(BundleStatus o1, BundleStatus o2) {
- int v = p.compare(o1, o2);
- if (v < 0) {
- change[0] = true;
+ int result = comparator.compare(o1, o2);
+ if (result < 0) {
+ changed[0] = true;
}
- return v;
+ return result;
}
};
- Collections.sort(data, proxy);
+ Collections.sort(data, wrapper);
sortCompleted(sortingContext);
- if (change[0]) {
+ if (changed[0]) {
notifyModelSorted(false);
}
}
@@ -435,8 +356,106 @@ public class BundleStatusTableModel extends AbstractSortedTableModel bundles) {
+ int index = statuses.size();
+ for (GhidraBundle bundle : bundles) {
+ addNewStatusNoFire(bundle);
+ }
+ fireTableRowsInserted(index, bundles.size() - 1);
+ }
+
+ @Override
+ public void bundleRemoved(GhidraBundle bundle) {
+ BundleStatus status = getStatus(bundle);
+ removeStatus(status);
+ }
+
+ @Override
+ public void bundlesRemoved(Collection bundles) {
+ List toRemove = bundles.stream()
+ .map(BundleStatusTableModel.this::getStatus)
+ .collect(Collectors.toUnmodifiableList());
+ removeStatuses(toRemove);
+ }
+
+ @Override
+ public void bundleEnablementChange(GhidraBundle bundle, boolean newEnablement) {
+ BundleStatus status = getStatus(bundle);
+ status.setEnabled(newEnablement);
+ int row = getRowIndex(status);
+ fireTableRowsUpdated(row, row);
+ }
+
+ @Override
+ public void bundleException(GhidraBundleException exception) {
+ BundleStatus status = getStatusFromLoc(exception.getBundleLocation());
+ status.setSummary(exception.getMessage());
+ int row = getRowIndex(status);
+ fireTableRowsUpdated(row, row);
+ }
+ }
+
+ class Column {
+ final Class> clazz;
+ final int index;
+ final String name;
+
+ Column(String name, Class> clazz) {
+ this.name = name;
+ this.index = columns.size();
+ columns.add(this);
+ this.clazz = clazz;
+ }
+
+ boolean editable(BundleStatus status) {
+ return false;
+ }
+
+ Object getValue(BundleStatus status) {
+ return null;
+ }
+
+ void setValue(BundleStatus status, Object aValue) {
+ throw new RuntimeException(name + " is not editable!");
+ }
+
+ }
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundle.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundle.java
index 496bb71717..7cfee06ccd 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundle.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundle.java
@@ -29,15 +29,16 @@ import generic.jar.ResourceFile;
*/
public abstract class GhidraBundle {
- protected final ResourceFile path;
+ protected final ResourceFile file;
+
protected final BundleHost bundleHost;
protected boolean enabled;
protected boolean systemBundle;
- GhidraBundle(BundleHost bundleHost, ResourceFile bundlePath, boolean enabled,
+ GhidraBundle(BundleHost bundleHost, ResourceFile bundleFile, boolean enabled,
boolean systemBundle) {
this.bundleHost = bundleHost;
- this.path = bundlePath;
+ this.file = bundleFile;
this.enabled = enabled;
this.systemBundle = systemBundle;
}
@@ -59,29 +60,36 @@ public abstract class GhidraBundle {
public abstract boolean build(PrintWriter writer) throws Exception;
/**
- * same as {@link #build(PrintWriter)} with writer = {@link System.err}.
+ * same as {@link #build(PrintWriter)} with writer = {@link System#err}.
+ *
+ * @return true if build happened, false if already built
+ * @throws Exception if the build cannot complete
*/
- @SuppressWarnings("javadoc")
public boolean build() throws Exception {
return build(new PrintWriter(System.err));
}
/**
* Return the location identifier of the bundle that this GhidraBundle represents.
- * The location identifier is passed to {@link org.osgi.framework.BundleContext#installBundle} when this
- * bundle is installed.
+ *
+ * The location identifier is used by the framework, e.g. it is passed to
+ * {@link org.osgi.framework.BundleContext#installBundle} when the bundle is
+ * first installed.
+ *
+ *
Although the bundle location is a URI, outside of interactions with the framework,
+ * the bundle location should remain opaque.
*
* @return location identifier of this bundle
*/
- public abstract String getBundleLocation();
+ public abstract String getLocationIdentifier();
abstract List getAllRequirements();
/**
- * @return this bundle's path
+ * @return the file where this bundle is loaded from
*/
- public ResourceFile getPath() {
- return path;
+ public ResourceFile getFile() {
+ return file;
}
/**
@@ -94,7 +102,7 @@ public abstract class GhidraBundle {
/**
* set the enablement flag for this bundle.
*
- * If a bundle is enabled its contents will be scanned, e.g. for scripts.
+ * If a bundle is enabled its contents will be scanned, e.g. for scripts.
*
* @param enabled new state
*/
@@ -112,81 +120,74 @@ public abstract class GhidraBundle {
}
/**
- * A GhidraBundle can be
- *
- * - a Bndtools .bnd script
- * - an OSGi bundle .jar file
- * - a directory of Java source
- *
- *
- */
- enum Type {
- BndScript, Jar, SourceDir, INVALID
- }
-
- /**
- * a string error with a time stamp
- */
- public static class BuildFailure {
- long when = -1;
- StringBuilder message = new StringBuilder();
- }
-
- /**
- * Get the type of a GhidraBundle from its path.
+ * Get the type of a GhidraBundle from its file.
*
- * @param path a resource path
+ * @param file a bundle file
* @return the type
*/
- static GhidraBundle.Type getType(ResourceFile path) {
- if (path.isDirectory()) {
- return GhidraBundle.Type.SourceDir;
+ static GhidraBundle.Type getType(ResourceFile file) {
+ if (file.isDirectory()) {
+ return GhidraBundle.Type.SOURCE_DIR;
}
- String n = path.getName().toLowerCase();
- if (n.endsWith(".bnd")) {
- return GhidraBundle.Type.BndScript;
+ String fileName = file.getName().toLowerCase();
+ if (fileName.endsWith(".bnd")) {
+ return GhidraBundle.Type.BND_SCRIPT;
}
- if (n.endsWith(".jar")) {
- return GhidraBundle.Type.Jar;
+ if (fileName.endsWith(".jar")) {
+ return GhidraBundle.Type.JAR;
}
return GhidraBundle.Type.INVALID;
}
/**
- * Get the type of a GhidraBundle from its path.
+ * Get the type of a GhidraBundle from its file.
*
- * @param path a file system path
+ * @param file a bundle file
* @return the type
*/
- public static GhidraBundle.Type getType(File path) {
- if (path.isDirectory()) {
- return GhidraBundle.Type.SourceDir;
+ public static GhidraBundle.Type getType(File file) {
+ if (file.isDirectory()) {
+ return GhidraBundle.Type.SOURCE_DIR;
}
- String n = path.getName().toLowerCase();
- if (n.endsWith(".bnd")) {
- return GhidraBundle.Type.BndScript;
+ String fileName = file.getName().toLowerCase();
+ if (fileName.endsWith(".bnd")) {
+ return GhidraBundle.Type.BND_SCRIPT;
}
- if (n.endsWith(".jar")) {
- return GhidraBundle.Type.Jar;
+ if (fileName.endsWith(".jar")) {
+ return GhidraBundle.Type.JAR;
}
return GhidraBundle.Type.INVALID;
}
/**
- * Get the OSGi bundle respresented by this GhidraBundle or null
+ * Get the OSGi bundle represented by this GhidraBundle or null if it isn't in
+ * the "installed" state.
*
* @return a Bundle or null
*/
public Bundle getOSGiBundle() {
- return bundleHost.getOSGiBundle(getBundleLocation());
+ return bundleHost.getOSGiBundle(getLocationIdentifier());
}
/**
* @return true if this bundle is active
*/
public boolean isActive() {
- Bundle b = getOSGiBundle();
- return (b != null) && b.getState() == Bundle.ACTIVE;
+ Bundle bundle = getOSGiBundle();
+ return (bundle != null) && bundle.getState() == Bundle.ACTIVE;
+ }
+
+ /**
+ * A GhidraBundle can be
+ *
+ * - a Bndtools .bnd script
+ * - an OSGi bundle .jar file
+ * - a directory of Java source
+ *
+ *
+ */
+ enum Type {
+ BND_SCRIPT, JAR, SOURCE_DIR, INVALID
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundleException.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundleException.java
index 141547252d..f36521f83f 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundleException.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraBundleException.java
@@ -20,12 +20,15 @@ import java.util.stream.Collectors;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
+/**
+ * {@link GhidraBundleException}s store the context associated with exceptions thrown during bundle operations.
+ */
public class GhidraBundleException extends OSGiException {
private final Bundle bundle;
private final String bundleLocation;
/**
- * {@link GhidraBundleException}s store the context associated with exceptions thrown during bundle operations.
+ * Construct a new exception originating with {@code bundle}.
*
* @param bundle the bundle (if available)
* @param msg a contextual message
@@ -38,7 +41,7 @@ public class GhidraBundleException extends OSGiException {
}
/**
- * {@link GhidraBundleException}s store the context associated with exceptions thrown during bundle operations.
+ * Construct a new exception originating with the bundle having location identifier {@code bundleLocation}.
*
* @param bundleLocation the bundle location identifier (since no bundle is available)
* @param msg a contextual message
@@ -71,73 +74,56 @@ public class GhidraBundleException extends OSGiException {
return "";
}
if (e instanceof BundleException) {
- BundleException be = (BundleException) e;
- switch (be.getType()) {
+ BundleException bundleException = (BundleException) e;
+ switch (bundleException.getType()) {
default:
return "No exception type";
case BundleException.UNSPECIFIED:
return "UNSPECIFIED";
- /**
- * The operation was unsupported. This type can be used anywhere a
- * BundleException can be thrown.
- */
+ // The operation was unsupported. This type can be used anywhere a BundleException can be thrown.
case BundleException.UNSUPPORTED_OPERATION:
return "UNSUPPORTED_OPERATION";
- /**
- * The operation was invalid.
- */
+ // The operation was invalid.
case BundleException.INVALID_OPERATION:
return "INVALID_OPERATION";
- /**
- * The bundle manifest was in error.
- */
+ // The bundle manifest was in error.
case BundleException.MANIFEST_ERROR:
return "MANIFEST_ERROR";
- /**
- * The bundle was not resolved.
- */
+ // The bundle was not resolved.
case BundleException.RESOLVE_ERROR: {
- String message = be.getMessage();
+ String message = bundleException.getMessage();
if (message.startsWith("Unable to acquire global lock")) {
return message;
}
// parse the package constraints from filters in the BundleRequirement string
- String packages = OSGiUtils.extractPackageNamesFromFailedResolution(be.getMessage())
- .stream()
- .distinct()
- .collect(Collectors.joining("\n"));
+ String packages =
+ OSGiUtils.extractPackageNamesFromFailedResolution(bundleException.getMessage())
+ .stream()
+ .distinct()
+ .collect(Collectors.joining("\n"));
return "RESOLVE_ERROR with reference to packages:\n" + packages;
}
- /**
- * The bundle activator was in error.
- */
+ // The bundle activator was in error.
case BundleException.ACTIVATOR_ERROR:
return "ACTIVATOR_ERROR";
- /**
- * The operation failed due to insufficient permissions.
- */
+ // The operation failed due to insufficient permissions.
case BundleException.SECURITY_ERROR:
return "SECURITY_ERROR";
- /**
- * The operation failed to complete the requested lifecycle state change.
- */
+ // The operation failed to complete the requested lifecycle state change.
case BundleException.STATECHANGE_ERROR:
return "STATECHANGE_ERROR";
- /**
- * The bundle could not be resolved due to an error with the
- * Bundle-NativeCode header.
- */
+ // The bundle could not be resolved due to an error with the Bundle-NativeCode header.
case BundleException.NATIVECODE_ERROR:
return "NATIVECODE_ERROR";
- /**
+ /*
* The install or update operation failed because another already installed
* bundle has the same symbolic name and version. This exception type will
* only occur if the framework is configured to only allow a single bundle
@@ -148,25 +134,16 @@ public class GhidraBundleException extends OSGiException {
case BundleException.DUPLICATE_BUNDLE_ERROR:
return "DUPLICATE_BUNDLE_ERROR";
- /**
- * The start transient operation failed because the start level of the
- * bundle is greater than the current framework start level
- */
+ // The start transient operation failed because the start level of the bundle
+ // is greater than the current framework start level
case BundleException.START_TRANSIENT_ERROR:
return "START_TRANSIENT_ERROR";
- /**
- * The framework received an error while reading the input stream for a
- * bundle.
- */
+ // The framework received an error while reading the input stream for a bundle.
case BundleException.READ_ERROR:
return "READ_ERROR";
- /**
- * A framework hook rejected the operation.
- *
- * @since 1.6
- */
+ // A framework hook rejected the operation.
case BundleException.REJECTED_BY_HOOK:
return "REJECTED_BY_HOOK";
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraJarBundle.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraJarBundle.java
index eec75b2251..a1b9be400d 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraJarBundle.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraJarBundle.java
@@ -36,14 +36,14 @@ public class GhidraJarBundle extends GhidraBundle {
* {@link GhidraJarBundle} wraps an ordinary OSGi bundle .jar.
*
* @param bundleHost the {@link BundleHost} instance this bundle will belong to
- * @param path the jar file's path
+ * @param file the jar file
* @param enabled true to start enabled
* @param systemBundle true if this is a Ghidra system bundle
*/
- public GhidraJarBundle(BundleHost bundleHost, ResourceFile path, boolean enabled,
+ public GhidraJarBundle(BundleHost bundleHost, ResourceFile file, boolean enabled,
boolean systemBundle) {
- super(bundleHost, path, enabled, systemBundle);
- this.bundleLocation = "file://" + path.getAbsolutePath().toString();
+ super(bundleHost, file, enabled, systemBundle);
+ this.bundleLocation = "file://" + file.getAbsolutePath().toString();
}
@Override
@@ -57,19 +57,17 @@ public class GhidraJarBundle extends GhidraBundle {
}
@Override
- public String getBundleLocation() {
+ public String getLocationIdentifier() {
return bundleLocation;
}
@Override
public List getAllRequirements() {
- Jar jar;
- try {
- jar = new Jar(path.getFile(true));
- Manifest m = jar.getManifest();
- String imps = m.getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
- if (imps != null) {
- return OSGiUtils.parseImports(imps);
+ try (Jar jar = new Jar(file.getFile(true))) {
+ Manifest manifest = jar.getManifest();
+ String importPackageString = manifest.getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
+ if (importPackageString != null) {
+ return OSGiUtils.parseImportPackage(importPackageString);
}
return Collections.emptyList();
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraPlaceholderBundle.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraPlaceholderBundle.java
index 28b027c0e7..0f9d6aad02 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraPlaceholderBundle.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraPlaceholderBundle.java
@@ -28,9 +28,9 @@ import generic.jar.ResourceFile;
*/
public class GhidraPlaceholderBundle extends GhidraBundle {
- GhidraPlaceholderBundle(BundleHost bundleHost, ResourceFile bundlePath, boolean isEnabled,
+ GhidraPlaceholderBundle(BundleHost bundleHost, ResourceFile bundleFile, boolean isEnabled,
boolean isSystemBundle) {
- super(bundleHost, bundlePath, isEnabled, isSystemBundle);
+ super(bundleHost, bundleFile, isEnabled, isSystemBundle);
}
@Override
@@ -44,8 +44,8 @@ public class GhidraPlaceholderBundle extends GhidraBundle {
}
@Override
- public String getBundleLocation() {
- return "invalid://" + getPath();
+ public String getLocationIdentifier() {
+ return "invalid://" + getFile();
}
@Override
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java
index d3277b0ff0..f9b3afd25b 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java
@@ -48,11 +48,14 @@ import ghidra.util.Msg;
/**
* {@link GhidraSourceBundle} represents a Java source directory that is compiled on build to an OSGi bundle.
*
- * A manifest and BundleActivator are generated if not already present.
+ * A manifest and {@link BundleActivator} are generated if not already present.
+ */
+/**
+ *
*/
public class GhidraSourceBundle extends GhidraBundle {
private static final String GENERATED_ACTIVATOR_CLASSNAME = "GeneratedActivator";
- private static final Predicate isClassFile =
+ private static final Predicate IS_CLASS_FILE =
Pattern.compile("(\\$.*)?\\.class", Pattern.CASE_INSENSITIVE).asMatchPredicate();
protected interface DiscrepencyCallback {
@@ -61,27 +64,27 @@ public class GhidraSourceBundle extends GhidraBundle {
* corresponding class file(s), {@code classFiles}
*
* @param sourceFile the source file or null to indicate the class files have no corresponding source
- * @param classFiles corresponding class files(s)
+ * @param classFiles corresponding class file(s)
* @throws Throwable an exception
*/
void found(ResourceFile sourceFile, Collection classFiles) throws Throwable;
}
private final String symbolicName;
- private final Path binDir;
+ private final Path binaryDir;
private final String bundleLoc;
private final List newSources = new ArrayList<>();
- private final List oldBin = new ArrayList<>();
+ private final List oldBinaries = new ArrayList<>();
private JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//// information indexed by source file
- private final HashMap buildErrors = new HashMap<>();
- private final HashMap> sourceFileToRequirements =
+ private final Map buildErrors = new HashMap<>();
+ private final Map> sourceFileToRequirements =
new HashMap<>();
- private final HashMap> requirementToSourceFileMap = new HashMap<>();
+ private final Map> requirementToSourceFileMap = new HashMap<>();
private long lastCompileAttempt;
@@ -97,10 +100,18 @@ public class GhidraSourceBundle extends GhidraBundle {
boolean systemBundle) {
super(bundleHost, sourceDirectory, enabled, systemBundle);
- this.symbolicName = GhidraSourceBundle.sourceDirHash(path);
- this.binDir = GhidraSourceBundle.getCompiledBundlesDir().resolve(symbolicName);
+ this.symbolicName = GhidraSourceBundle.sourceDirHash(getSourceDirectory());
+ this.binaryDir = GhidraSourceBundle.getCompiledBundlesDir().resolve(symbolicName);
- this.bundleLoc = "reference:file://" + binDir.toAbsolutePath().normalize().toString();
+ this.bundleLoc = "reference:file://" + binaryDir.toAbsolutePath().normalize().toString();
+ }
+
+ /**
+ * (alias of {@link #getFile} for readability)
+ * @return the source directory this bundle represents
+ */
+ protected final ResourceFile getSourceDirectory() {
+ return file;
}
/**
@@ -119,8 +130,8 @@ public class GhidraSourceBundle extends GhidraBundle {
* When a source bundle doesn't have a manifest, Ghidra computes the bundle's
* symbolic name as a hash of the source directory path.
*
- * The hash is is also used as the final path component of the compile destination:
- * {@code $USERHOME/.ghidra/.ghidra_/osgi/compiled-bundles/ }
+ * This hash is also used as the final path component of the compile destination:
+ *
{@code $USERHOME/.ghidra/.ghidra_/osgi/compiled-bundles/ }
*
* @param sourceDir the source directory
* @return a string hash of the source directory path
@@ -144,17 +155,19 @@ public class GhidraSourceBundle extends GhidraBundle {
}
/**
- * Returen the class name corresponding to a script in this source bundle.
+ * Return the class name corresponding to a script in this source bundle.
*
* @param sourceFile a source file from this bundle
* @return the class name
*/
public String classNameForScript(ResourceFile sourceFile) {
- String p;
try {
- p = sourceFile.getCanonicalPath();
- p = p.substring(1 + path.getCanonicalPath().length(), p.length() - 5);// relative path less ".java"
- return p.replace(File.separatorChar, '.');
+ String path = sourceFile.getCanonicalPath();
+
+ // get the relative path and chop ".java" from the end
+ path = path.substring(1 + getSourceDirectory().getCanonicalPath().length(),
+ path.length() - 5);
+ return path.replace(File.separatorChar, '.');
}
catch (IOException e) {
e.printStackTrace();
@@ -171,63 +184,63 @@ public class GhidraSourceBundle extends GhidraBundle {
* @param sourceFile the file w/ errors
* @param err an error string
*/
- void buildError(ResourceFile sourceFile, String err) {
- GhidraBundle.BuildFailure f =
- buildErrors.computeIfAbsent(sourceFile, x -> new GhidraBundle.BuildFailure());
- f.when = sourceFile.lastModified();
- f.message.append(err);
+ protected void buildError(ResourceFile sourceFile, String err) {
+ BuildError error = buildErrors.computeIfAbsent(sourceFile, BuildError::new);
+ error.append(err);
}
/**
* get any errors associated with building the given source file.
*
* @param sourceFile the source file
- * @return a {@link GhidraBundle.BuildFailure} object
+ * @return a {@link BuildError} object
*/
- public GhidraBundle.BuildFailure getErrors(ResourceFile sourceFile) {
+ public BuildError getErrors(ResourceFile sourceFile) {
return buildErrors.get(sourceFile);
}
private String getPreviousBuildErrors() {
return buildErrors.values()
.stream()
- .map(e -> e.message.toString())
+ .map(BuildError::getMessage)
.collect(Collectors.joining());
}
- private String parseImports(ResourceFile javaSource) {
+ private String parseImportPackageMetadata(ResourceFile javaSource) {
return GhidraScriptUtil.newScriptInfo(javaSource).getImportPackage();
}
/**
- * update buildReqs based on \@importpackages tag in java files in the default(unnamed) package
+ * update buildReqs based on \@importpackage tag in java files in the default(unnamed) package
*
- * @throws GhidraBundleException on failure to parse the \@importpackages tag
+ * @throws GhidraBundleException on failure to parse the \@importpackage tag
*/
private void updateRequirementsFromMetadata() throws GhidraBundleException {
sourceFileToRequirements.clear();
requirementToSourceFileMap.clear();
- for (ResourceFile file : path.listFiles()) {
- if (file.getName().endsWith(".java")) {
- // without GhidraScriptComponentProvider.updateAvailableScriptFilesForDirectory, or GhidraScriptComponentProvider.newScript
- // this might be the earliest need for ScriptInfo, so allow construction.
+ for (ResourceFile rootSourceFile : getSourceDirectory().listFiles()) {
+ if (rootSourceFile.getName().endsWith(".java")) {
+ // without GhidraScriptComponentProvider.updateAvailableScriptFilesForDirectory,
+ // or GhidraScriptComponentProvider.newScript this might be the earliest need for
+ // ScriptInfo, so allow construction.
// NB: ScriptInfo will update field values if lastModified has changed since last time they were computed
- String imps = parseImports(file);
- if (imps != null && !imps.isEmpty()) {
+ String importPackage = parseImportPackageMetadata(rootSourceFile);
+ if (importPackage != null && !importPackage.isEmpty()) {
List requirements;
try {
- requirements = OSGiUtils.parseImports(imps);
+ requirements = OSGiUtils.parseImportPackage(importPackage);
}
catch (BundleException e) {
- throw new GhidraBundleException(getBundleLocation(), "parsing manifest", e);
+ throw new GhidraBundleException(getLocationIdentifier(), "parsing manifest",
+ e);
}
- sourceFileToRequirements.put(file, requirements);
+ sourceFileToRequirements.put(rootSourceFile, requirements);
for (BundleRequirement requirement : requirements) {
requirementToSourceFileMap
.computeIfAbsent(requirement.toString(), x -> new ArrayList<>())
- .add(file);
+ .add(rootSourceFile);
}
}
}
@@ -254,12 +267,12 @@ public class GhidraSourceBundle extends GhidraBundle {
}
Map reqs = getComputedReqs();
// insert requirements from a source manifest
- ResourceFile manifest = getSourceManifestPath();
- if (manifest.exists()) {
+ ResourceFile manifestFile = getSourceManifestFile();
+ if (manifestFile.exists()) {
try {
- Manifest m = new Manifest(manifest.getInputStream());
- String imports = m.getMainAttributes().getValue("Import-Package");
- for (BundleRequirement r : OSGiUtils.parseImports(imports)) {
+ Manifest manifest = new Manifest(manifestFile.getInputStream());
+ String importPackage = manifest.getMainAttributes().getValue("Import-Package");
+ for (BundleRequirement r : OSGiUtils.parseImportPackage(importPackage)) {
reqs.putIfAbsent(r.toString(), r);
}
}
@@ -273,54 +286,68 @@ public class GhidraSourceBundle extends GhidraBundle {
/**
* look for new sources, metadata, manifest file.
*
- * if files had errors last time, haven't changed, and no new requirements are available, remove them.
+ * if files had errors last time, haven't changed, and no new requirements are available, remove them.
*
* @param writer for reporting status to user
* @throws IOException while accessing manifest file
* @throws OSGiException while parsing imports
*/
- void updateNewSourceOldBinFromFilesystem(PrintWriter writer) throws IOException, OSGiException {
+ void updateFromFilesystem(PrintWriter writer) throws IOException, OSGiException {
// look for new source files
newSources.clear();
- oldBin.clear();
+ oldBinaries.clear();
- visitDiscrepencies((sf, bfs) -> {
- if (sf != null) {
- newSources.add(sf);
+ visitDiscrepancies((sourceFile, classFiles) -> {
+ // sourceFile is either newer than its corresponding class files,
+ // or there are no corresponding class files (meaning it's new),
+ // or sourceFile=null and classFiles had no corresponding source
+ if (sourceFile != null) {
+ // these will be (re)compiled
+ newSources.add(sourceFile);
}
- if (bfs != null) {
- oldBin.addAll(bfs);
+ if (classFiles != null) {
+ // these will be deleted
+ oldBinaries.addAll(classFiles);
}
});
- // don't rebuild source files that failed last time and haven't changed
- Iterator it = newSources.iterator();
- while (it.hasNext()) {
- ResourceFile sf = it.next();
- GhidraBundle.BuildFailure f = buildErrors.get(sf);
- if (f != null) {
- if (f.when == sf.lastModified()) {
- it.remove();
- continue;
- }
- // it's either new or worth trying again
- buildErrors.remove(sf);
+ // we don't want to rebuild source files that had errors last time and haven't changed,
+ // so remove them from newSources. Also remove old error messages.
+ Iterator newSourceIterator = newSources.iterator();
+ while (newSourceIterator.hasNext()) {
+ ResourceFile newSourceFile = newSourceIterator.next();
+ if (stillHasErrors(newSourceFile)) {
+ newSourceIterator.remove();
+ }
+ else {
+ // any errors are old, so remove them
+ buildErrors.remove(newSourceFile);
}
}
}
+ private boolean stillHasErrors(ResourceFile newSourceFile) {
+ BuildError error = buildErrors.get(newSourceFile);
+ if (error != null) {
+ if (error.getLastModified() == newSourceFile.lastModified()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void deleteOldBinaries() throws IOException {
// dedupe and omit files that don't exist
- oldBin.sort(null);
- Iterable paths = () -> oldBin.stream().distinct().filter(Files::exists).iterator();
+ oldBinaries.sort(null);
+ Iterable paths =
+ () -> oldBinaries.stream().distinct().filter(Files::exists).iterator();
- for (Path bf : paths) {
- Files.delete(bf);
+ for (Path path : paths) {
+ Files.delete(path);
}
- // oldBin.clear();
}
- int getFailingSourcesCount() {
+ int getBuildErrorCount() {
return buildErrors.size();
}
@@ -345,20 +372,24 @@ public class GhidraSourceBundle extends GhidraBundle {
}
@Override
- public String getBundleLocation() {
+ public String getLocationIdentifier() {
return bundleLoc;
}
- ResourceFile getSourceManifestPath() {
- return new ResourceFile(path, "META-INF" + File.separator + "MANIFEST.MF");
+ ResourceFile getSourceManifestFile() {
+ return new ResourceFile(getSourceDirectory(), "META-INF" + File.separator + "MANIFEST.MF");
}
- boolean hasNewManifest() throws IOException {
- ResourceFile smf = getSourceManifestPath();
- Path dmf = binDir.resolve("META-INF").resolve("MANIFEST.MF");
+ private Path getBinaryManifestPath() {
+ return binaryDir.resolve("META-INF").resolve("MANIFEST.MF");
+ }
- return smf.exists() && (Files.notExists(dmf) ||
- smf.lastModified() > Files.getLastModifiedTime(dmf).toMillis());
+ boolean hasNewManifest() {
+ ResourceFile sourceManfiest = getSourceManifestFile();
+ Path binaryManifest = getBinaryManifestPath();
+
+ return sourceManfiest.exists() && (Files.notExists(binaryManifest) ||
+ sourceManfiest.lastModified() > binaryManifest.toFile().lastModified());
}
protected static boolean wipeContents(Path path) throws IOException {
@@ -374,7 +405,50 @@ public class GhidraSourceBundle extends GhidraBundle {
}
private boolean wipeBinDir() throws IOException {
- return wipeContents(binDir);
+ return wipeContents(binaryDir);
+ }
+
+ /**
+ * if source with a previous build error now resolves, add it to newSources.
+ *
+ * The reason for the previous build error isn't necessarily a missing requirement,
+ * but this shouldn't be too expensive.
+ */
+ private void buildIfPreviosErrorNowResolves() {
+ for (ResourceFile sourceFile : buildErrors.keySet()) {
+ List requirements = sourceFileToRequirements.get(sourceFile);
+ if (requirements != null && !requirements.isEmpty() &&
+ bundleHost.canResolveAll(requirements)) {
+ if (!newSources.contains(sourceFile)) {
+ newSources.add(sourceFile);
+ }
+ for (ResourceFile oldbin : correspondingBinaries(sourceFile)) {
+ oldbin.delete();
+ }
+ }
+ }
+ }
+
+ /**
+ * if a file that previously built without errors is now missing some requirements,
+ * rebuild it to capture errors (if any).
+ */
+ void buildIfRequirementsChanged() {
+ // if previous successes no longer resolve, (cleanup) and try again
+ for (Entry> e : sourceFileToRequirements.entrySet()) {
+ ResourceFile sourceFile = e.getKey();
+ List requirements = e.getValue();
+ if (requirements != null && !requirements.isEmpty() &&
+ !buildErrors.containsKey(sourceFile) && !bundleHost.canResolveAll(requirements)) {
+ if (!newSources.contains(sourceFile)) {
+ newSources.add(sourceFile);
+ }
+
+ Arrays.stream(correspondingBinaries(sourceFile))
+ .map(rf -> rf.getFile(false).toPath())
+ .forEach(oldBinaries::add);
+ }
+ }
}
@Override
@@ -391,44 +465,20 @@ public class GhidraSourceBundle extends GhidraBundle {
wipeBinDir();
}
- updateNewSourceOldBinFromFilesystem(writer);
+ updateFromFilesystem(writer);
updateRequirementsFromMetadata();
- // if previous failures now resolve, try again
- for (ResourceFile sf : buildErrors.keySet()) {
- List reqs = sourceFileToRequirements.get(sf);
- if (reqs != null && !reqs.isEmpty() && bundleHost.canResolveAll(reqs)) {
- if (!newSources.contains(sf)) {
- newSources.add(sf);
- }
- for (ResourceFile oldbin : correspondingBinaries(sf)) {
- oldbin.delete();
- }
- }
- }
- // if previous successes no longer resolve, (cleanup) and try again
- for (Entry> e : sourceFileToRequirements.entrySet()) {
- ResourceFile sf = e.getKey();
- List reqs = e.getValue();
- if (reqs != null && !reqs.isEmpty() && !buildErrors.containsKey(sf) &&
- !bundleHost.canResolveAll(reqs)) {
- if (!newSources.contains(sf)) {
- newSources.add(sf);
- }
+ buildIfPreviosErrorNowResolves();
+ buildIfRequirementsChanged();
- Arrays.stream(correspondingBinaries(sf))
- .map(rf -> rf.getFile(false).toPath())
- .forEach(oldBin::add);
- }
- }
-
- int failuresLastTime = getFailingSourcesCount();
+ int buildErrorsLastTime = getBuildErrorCount();
int newSourceCount = getNewSourcesCount();
if (newSourceCount == 0) {
- if (failuresLastTime > 0) {
+ if (buildErrorsLastTime > 0) {
writer.printf("%s hasn't changed, with %d file%s failing in previous build(s):\n",
- path.toString(), failuresLastTime, failuresLastTime > 1 ? "s" : "");
+ getSourceDirectory().toString(), buildErrorsLastTime,
+ buildErrorsLastTime > 1 ? "s" : "");
writer.printf("%s\n", getPreviousBuildErrors());
}
}
@@ -437,15 +487,15 @@ public class GhidraSourceBundle extends GhidraBundle {
}
// finally, we should be able to handle empty directories
- if (!binDir.toFile().exists()) {
+ if (!binaryDir.toFile().exists()) {
needsCompile = true;
}
if (needsCompile) {
// if there is a bundle at our locations, uninstall it
- Bundle b = bundleHost.getOSGiBundle(getBundleLocation());
- if (b != null) {
- bundleHost.deactivateSynchronously(b);
+ Bundle osgiBundle = bundleHost.getOSGiBundle(getLocationIdentifier());
+ if (osgiBundle != null) {
+ bundleHost.deactivateSynchronously(osgiBundle);
}
// once we've committed to recompile and regenerate generated classes, delete the old stuff
@@ -467,9 +517,9 @@ public class GhidraSourceBundle extends GhidraBundle {
}
try {
- Bundle b = getOSGiBundle();
- if (b != null) {
- bundleHost.deactivateSynchronously(b);
+ Bundle bundle = getOSGiBundle();
+ if (bundle != null) {
+ bundleHost.deactivateSynchronously(bundle);
}
return anythingChanged | wipeBinDir();
}
@@ -482,29 +532,32 @@ public class GhidraSourceBundle extends GhidraBundle {
private ResourceFile[] correspondingBinaries(ResourceFile source) {
String parentPath = source.getParentFile().getAbsolutePath();
- String relpath = parentPath.substring(path.getAbsolutePath().length());
- if (relpath.startsWith(File.separator)) {
- relpath = relpath.substring(1);
+ String relPath = parentPath.substring(getSourceDirectory().getAbsolutePath().length());
+ if (relPath.startsWith(File.separator)) {
+ relPath = relPath.substring(1);
}
- String n0 = source.getName();
- final String n = n0.substring(0, n0.length() - 5);// trim .java
- ResourceFile bp = new ResourceFile(binDir.resolve(relpath).toFile());
- if (!bp.exists() || !bp.isDirectory()) {
+ String className0 = source.getName();
+ String className = className0.substring(0, className0.length() - 5);// drop ".java"
+ ResourceFile binarySubdir = new ResourceFile(binaryDir.resolve(relPath).toFile());
+ if (!binarySubdir.exists() || !binarySubdir.isDirectory()) {
return new ResourceFile[] {};
}
- return bp.listFiles(f -> {
- String nn = f.getName();
- return nn.startsWith(n) && isClassFile.test(nn.substring(n.length()));
+ return binarySubdir.listFiles(f -> {
+ String fileName = f.getName();
+ return fileName.startsWith(className) &&
+ IS_CLASS_FILE.test(fileName.substring(className.length()));
});
}
/**
+ * visit discrepancies between java source and corresponding class files.
+ *
*
* walk resources to find:
* - source files that are newer than their corresponding binary
* reports (source file, list of corresponding binaries)
* - source files with no corresponding binary
- * reports (source file, )
+ * reports (source file, empty list)
* - binary files with no corresponding source
* reports (null, list of binary files)
*
@@ -520,208 +573,138 @@ public class GhidraSourceBundle extends GhidraBundle {
*
* @param cb callback
*/
- protected void visitDiscrepencies(DiscrepencyCallback cb) {
+ protected void visitDiscrepancies(DiscrepencyCallback cb) {
try {
Deque stack = new ArrayDeque<>();
- stack.add(path);
+ // start in the source directory root
+ stack.add(getSourceDirectory());
+
while (!stack.isEmpty()) {
- ResourceFile sd = stack.pop();
- String relpath = sd.getAbsolutePath().substring(path.getAbsolutePath().length());
- if (relpath.startsWith(File.separator)) {
- relpath = relpath.substring(1);
+ ResourceFile sourceSubdir = stack.pop();
+ String relPath = sourceSubdir.getAbsolutePath()
+ .substring(getSourceDirectory().getAbsolutePath().length());
+ if (relPath.startsWith(File.separator)) {
+ relPath = relPath.substring(1);
}
- Path bd = binDir.resolve(relpath);
+ Path binarySubdir = binaryDir.resolve(relPath);
- // index the class files in the corresponding directory by basename
- Map> binfiles = Files.exists(bd) ? Files.list(bd)
- .filter(x -> Files.isRegularFile(x) &&
- x.getFileName().toString().endsWith(".class"))
- .collect(groupingBy(x -> {
- String s = x.getFileName().toString();
- int money = s.indexOf('$');
- if (money >= 0) {
- return s.substring(0, money);
- }
- return s.substring(0, s.length() - 6);
- })) : Collections.emptyMap();
+ ClassMapper mapper = new ClassMapper(binarySubdir);
- for (ResourceFile sf : sd.listFiles()) {
- if (sf.isDirectory()) {
- stack.push(sf);
+ // for each source file, lookup class files by class name
+ for (ResourceFile sourceFile : sourceSubdir.listFiles()) {
+ if (sourceFile.isDirectory()) {
+ stack.push(sourceFile);
}
else {
- String n = sf.getName();
- if (n.endsWith(".java")) {
- long sourceLastModified = sf.lastModified();
- List bfs = binfiles.remove(n.substring(0, n.length() - 5));
- long binaryLastModified = (bfs == null || bfs.isEmpty()) ? -1
- : bfs.stream()
- .mapToLong(bf -> bf.toFile().lastModified())
- .min()
- .getAsLong();
- // if source is newer than the oldest binary, report
- if (sourceLastModified > binaryLastModified) {
- cb.found(sf, bfs);
- }
+ List classFiles = mapper.findAndRemove(sourceFile);
+ if (classFiles != null) {
+ cb.found(sourceFile, classFiles);
}
}
}
// any remaining .class files are missing .java files
- if (!binfiles.isEmpty()) {
- cb.found(null,
- binfiles.values()
- .stream()
- .flatMap(l -> l.stream())
- .collect(Collectors.toList()));
+ if (mapper.hasExtraClassFiles()) {
+ cb.found(null, mapper.extraClassFiles());
}
}
}
- catch (Throwable t) {
- t.printStackTrace();
+ catch (Throwable e) {
+ e.printStackTrace();
}
}
- private static class Summary {
- static String SEP = ", ";
- final StringWriter sw = new StringWriter();
- final PrintWriter pw = new PrintWriter(sw, true);
-
- void printf(String format, Object... args) {
- if (sw.getBuffer().length() > 0) {
- pw.write(SEP);
- }
- pw.printf(format, args);
- }
-
- void print(String arg) {
- pw.print(arg);
- }
-
- String getValue() {
- pw.flush();
- return sw.getBuffer().toString();
- }
-
- }
-
- /**
- * compile a source directory to an exploded bundle
- *
- * @param writer for updating the user during compilation
- * @throws IOException for source/manifest file reading/generation and binary deletion/creation
- * @throws OSGiException if generation of bundle metadata fails
+ /*
+ * when calling the java compiler programmatically, we map import requests to files with
+ * a custom {@link JavaFileManager}. We wrap the system JavaFileManager with one that
+ * handles ResourceFiles then wrap that with Phidia, which handles imports based on
+ * bundle requirements.
*/
- private String compileToExplodedBundle(PrintWriter writer) throws IOException, OSGiException {
- compileAttempted();
-
- Files.createDirectories(binDir);
-
- Summary summary = new Summary();
-
- List options = new ArrayList<>();
- options.add("-g");
- options.add("-d");
- options.add(binDir.toString());
- options.add("-sourcepath");
- options.add(path.toString());
- options.add("-classpath");
- options.add(System.getProperty("java.class.path") + File.pathSeparator + binDir.toString());
- options.add("-proc:none");
-
- final ResourceFileJavaFileManager resourceFileJavaManager =
- new ResourceFileJavaFileManager(Collections.singletonList(path), buildErrors.keySet());
+ private BundleJavaManager createBundleJavaManager(PrintWriter writer, Summary summary,
+ List options) throws IOException {
+ final ResourceFileJavaFileManager resourceFileJavaManager = new ResourceFileJavaFileManager(
+ Collections.singletonList(getSourceDirectory()), buildErrors.keySet());
BundleJavaManager bundleJavaManager =
new BundleJavaManager(bundleHost.getHostFramework(), resourceFileJavaManager, options);
+
// The phidias BundleJavaManager is for compiling from within a bundle -- it makes the
// bundle dependencies available to the compiler classpath. Here, we are compiling in an as-yet
- // non-existing bundle, so we forge the wiring based on @importpackages metadata.
+ // non-existing bundle, so we forge the wiring based on @importpackage metadata.
- // XXX skip this if there's a source manifest, emit warnings about @importpackages
+ // XXX skip this if there's a source manifest, emit warnings about @importpackage
// get wires for currently active bundles to satisfy all requirements
- List reqs = getAllRequirements();
- List bundleWirings = bundleHost.resolve(reqs);
+ List requirements = getAllRequirements();
+ List bundleWirings = bundleHost.resolve(requirements);
- if (!reqs.isEmpty()) {
- writer.printf("%d import requirement%s remain%s unresolved:\n", reqs.size(),
- reqs.size() > 1 ? "s" : "", reqs.size() > 1 ? "" : "s");
- for (BundleRequirement req : reqs) {
- writer.printf(" %s\n", req.toString());
+ if (!requirements.isEmpty()) {
+ writer.printf("%d import requirement%s remain%s unresolved:\n", requirements.size(),
+ requirements.size() > 1 ? "s" : "", requirements.size() > 1 ? "" : "s");
+ for (BundleRequirement requirement : requirements) {
+ writer.printf(" %s\n", requirement.toString());
}
- summary.printf("%d missing @import%s:%s", reqs.size(), reqs.size() > 1 ? "s" : "",
- reqs.stream()
- .flatMap(r -> OSGiUtils.extractPackageNamesFromFailedResolution(r.toString()).stream())
+ summary.printf("%d missing @importpackage%s:%s", requirements.size(),
+ requirements.size() > 1 ? "s" : "",
+ requirements.stream()
+ .flatMap(r -> OSGiUtils.extractPackageNamesFromFailedResolution(r.toString())
+ .stream())
.distinct()
.collect(Collectors.joining(",")));
}
// send the capabilities to phidias
bundleWirings.forEach(bundleJavaManager::addBundleWiring);
+ return bundleJavaManager;
+ }
- final List sourceFiles = newSources.stream()
- .map(sf -> new ResourceFileJavaFileObject(sf.getParentFile(), sf, Kind.SOURCE))
- .collect(Collectors.toList());
+ /*
+ * Try building sourcefiles.. on success return true.
+ *
+ * If build fails, collect errors, remove files that caused
+ * errors from source files, and return false.
+ */
+ private boolean tryBuild(PrintWriter writer, BundleJavaManager bundleJavaManager,
+ List sourceFiles, List options) throws IOException {
+ DiagnosticCollector diagnostics = new DiagnosticCollector();
+ JavaCompiler.CompilationTask task =
+ compiler.getTask(writer, bundleJavaManager, diagnostics, options, null, sourceFiles);
- Path dmf = binDir.resolve("META-INF").resolve("MANIFEST.MF");
- if (Files.exists(dmf)) {
- Files.delete(dmf);
+ Boolean successfulCompilation = task.call();
+ if (successfulCompilation) {
+ return true;
}
-
- // try to compile, if we fail, avoid offenders and try again
- while (!sourceFiles.isEmpty()) {
- DiagnosticCollector diagnostics =
- new DiagnosticCollector();
- JavaCompiler.CompilationTask task = compiler.getTask(writer, bundleJavaManager,
- diagnostics, options, null, sourceFiles);
- // task.setProcessors // for annotation processing / code generation
-
- Boolean successfulCompilation = task.call();
- if (successfulCompilation) {
- break;
+ Set filesWithErrors = new HashSet<>();
+ for (Diagnostic extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
+ String error = diagnostic.toString() + "\n";
+ writer.write(error);
+ ResourceFileJavaFileObject sourceFileObject =
+ (ResourceFileJavaFileObject) diagnostic.getSource();
+ ResourceFile sourceFile = sourceFileObject.getFile();
+ buildError(sourceFile, error); // remember all errors for this file
+ filesWithErrors.add(sourceFileObject);
+ }
+ for (ResourceFileJavaFileObject sourceFileObject : filesWithErrors) {
+ if (sourceFiles.remove(sourceFileObject)) {
+ writer.printf("skipping %s\n", sourceFileObject.getFile().toString());
}
- Set hadErrors = new HashSet<>();
- for (Diagnostic extends JavaFileObject> d : diagnostics.getDiagnostics()) {
- String err = d.toString() + "\n";
- writer.write(err);
- ResourceFileJavaFileObject sf = (ResourceFileJavaFileObject) d.getSource();
- ResourceFile rf = sf.getFile();
- buildError(rf, err); // remember all errors for this file
- hadErrors.add(sf);
- }
- for (ResourceFileJavaFileObject sf : hadErrors) {
- if (sourceFiles.remove(sf)) {
- writer.printf("skipping %s\n", sf.getFile().toString());
- }
- else {
- throw new IOException(
- "compilation error loop condition for " + sf.getFile().toString());
- }
+ else {
+ // we can't tolerate infinite loops here, so bail
+ throw new IOException("compilation error loop condition for " +
+ sourceFileObject.getFile().toString());
}
+ }
+ return false;
+ }
- }
- // mark the successful compilations
- for (ResourceFileJavaFileObject sf : sourceFiles) {
- ResourceFile rf = sf.getFile();
- buildSuccess(rf);
- }
- // buildErrors is now up to date, set status
- if (getFailingSourcesCount() > 0) {
- summary.printf("%d failing source files", getFailingSourcesCount());
- }
-
- ResourceFile smf = getSourceManifestPath();
- if (smf.exists()) {
- Files.createDirectories(dmf.getParent());
- Files.copy(smf.getInputStream(), dmf, StandardCopyOption.REPLACE_EXISTING);
- return summary.getValue();
- }
-
+ /* analyze the current binary dir and generate a manifest and an activator if one isn't found */
+ private String generateManifest(PrintWriter writer, Summary summary, Path binaryManifest)
+ throws OSGiException, IOException {
// no manifest, so create one with bndtools
Analyzer analyzer = new Analyzer();
- analyzer.setJar(new Jar(binDir.toFile())); // give bnd the contents
- analyzer.setProperty("Bundle-SymbolicName", GhidraSourceBundle.sourceDirHash(path));
+ analyzer.setJar(new Jar(binaryDir.toFile())); // give bnd the contents
+ analyzer.setProperty("Bundle-SymbolicName",
+ GhidraSourceBundle.sourceDirHash(getSourceDirectory()));
analyzer.setProperty("Bundle-Version", "1.0");
- // XXX we must constrain analyzed imports according to constraints declared in @importpackages tags
+ // XXX we must constrain analyzed imports according to constraints declared in @importpackage tags
analyzer.setProperty("Import-Package", "*");
analyzer.setProperty("Export-Package", "!*.private.*,!*.internal.*,*");
// analyzer.setBundleActivator(s);
@@ -735,7 +718,6 @@ public class GhidraSourceBundle extends GhidraBundle {
summary.print("bad manifest");
throw new OSGiException("failed to calculate manifest by analyzing code", e);
}
- Attributes ma = manifest.getMainAttributes();
String activatorClassName = null;
try {
@@ -751,28 +733,30 @@ public class GhidraSourceBundle extends GhidraBundle {
summary.print("failed bnd analysis");
throw new OSGiException("failed to query classes while searching for activator", e);
}
+
+ Attributes manifestAttributes = manifest.getMainAttributes();
if (activatorClassName == null) {
activatorClassName = GENERATED_ACTIVATOR_CLASSNAME;
- if (!buildDefaultActivator(binDir, activatorClassName, writer)) {
+ if (!buildDefaultActivator(binaryDir, activatorClassName, writer)) {
summary.print("failed to build generated activator");
return summary.getValue();
}
// since we add the activator after bndtools built the imports, we should add its imports too
- String imps = ma.getValue(Constants.IMPORT_PACKAGE);
+ String imps = manifestAttributes.getValue(Constants.IMPORT_PACKAGE);
if (imps == null) {
- ma.putValue(Constants.IMPORT_PACKAGE,
+ manifestAttributes.putValue(Constants.IMPORT_PACKAGE,
GhidraBundleActivator.class.getPackageName());
}
else {
- ma.putValue(Constants.IMPORT_PACKAGE,
+ manifestAttributes.putValue(Constants.IMPORT_PACKAGE,
imps + "," + GhidraBundleActivator.class.getPackageName());
}
}
- ma.putValue(Constants.BUNDLE_ACTIVATOR, activatorClassName);
+ manifestAttributes.putValue(Constants.BUNDLE_ACTIVATOR, activatorClassName);
// write the manifest
- Files.createDirectories(dmf.getParent());
- try (OutputStream out = Files.newOutputStream(dmf)) {
+ Files.createDirectories(binaryManifest.getParent());
+ try (OutputStream out = Files.newOutputStream(binaryManifest)) {
manifest.write(out);
}
}
@@ -840,4 +824,172 @@ public class GhidraSourceBundle extends GhidraBundle {
return true;
}
+ /**
+ * compile a source directory to an exploded bundle
+ *
+ * @param writer for updating the user during compilation
+ * @throws IOException for source/manifest file reading/generation and binary deletion/creation
+ * @throws OSGiException if generation of bundle metadata fails
+ */
+ private String compileToExplodedBundle(PrintWriter writer) throws IOException, OSGiException {
+ compileAttempted();
+
+ Files.createDirectories(binaryDir);
+
+ Summary summary = new Summary();
+
+ List options = new ArrayList<>();
+ options.add("-g");
+ options.add("-d");
+ options.add(binaryDir.toString());
+ options.add("-sourcepath");
+ options.add(getSourceDirectory().toString());
+ options.add("-classpath");
+ options
+ .add(System.getProperty("java.class.path") + File.pathSeparator + binaryDir.toString());
+ options.add("-proc:none");
+
+ BundleJavaManager bundleJavaManager = createBundleJavaManager(writer, summary, options);
+
+ final List sourceFiles = newSources.stream()
+ .map(sf -> new ResourceFileJavaFileObject(sf.getParentFile(), sf, Kind.SOURCE))
+ .collect(Collectors.toList());
+
+ Path binaryManifest = getBinaryManifestPath();
+ if (Files.exists(binaryManifest)) {
+ Files.delete(binaryManifest);
+ }
+
+ // try to compile, if we fail, avoid offenders and try again
+ while (!sourceFiles.isEmpty()) {
+ if (tryBuild(writer, bundleJavaManager, sourceFiles, options)) {
+ break;
+ }
+ }
+
+ // mark the successful compilations
+ for (ResourceFileJavaFileObject sourceFile : sourceFiles) {
+ buildSuccess(sourceFile.getFile());
+ }
+ // buildErrors is now up to date, set status
+ if (getBuildErrorCount() > 0) {
+ int count = getBuildErrorCount();
+ summary.printf("%d source file%s with errors", count, count > 1 ? "s" : "");
+ }
+
+ ResourceFile sourceManifest = getSourceManifestFile();
+ if (sourceManifest.exists()) {
+ Files.createDirectories(binaryManifest.getParent());
+ Files.copy(sourceManifest.getInputStream(), binaryManifest,
+ StandardCopyOption.REPLACE_EXISTING);
+ return summary.getValue();
+ }
+
+ return generateManifest(writer, summary, binaryManifest);
+ }
+
+ protected static class Compilation {
+ PrintWriter writer;
+ Summary summary;
+ List options;
+ BundleJavaManager bundleJavaManager;
+
+ List sourceFiles;
+
+ }
+
+ private static class Summary {
+ static String SEPERATOR = ", ";
+ final StringWriter stringWriter = new StringWriter();
+ final PrintWriter printWriter = new PrintWriter(stringWriter, true);
+
+ void printf(String format, Object... args) {
+ if (stringWriter.getBuffer().length() > 0) {
+ printWriter.write(SEPERATOR);
+ }
+ printWriter.printf(format, args);
+ }
+
+ void print(String arg) {
+ printWriter.print(arg);
+ }
+
+ String getValue() {
+ printWriter.flush();
+ return stringWriter.getBuffer().toString();
+ }
+ }
+
+ /**
+ * Index *.class files in a directory by class name, e.g.
+ *
+ *
+ * "A" -> [directory/A.class]
+ * "B" -> [directory/B.class, directory/B$inner.class]
+ *
+ *
+ * A list of classes are then processed one at a time with {@link ClassMapper#findAndRemove}.
+ *
+ *
After processing, "extras" are handled with {@link ClassMapper#extraClassFiles}.
+ */
+ private static class ClassMapper {
+ final Map> classToClassFilesMap;
+
+ /**
+ * Map classes in {@code directory} with {@link ClassMapper}.
+ *
+ * @param directory the directory
+ * @throws IOException if there's a problem listing files
+ */
+ ClassMapper(Path directory) throws IOException {
+ classToClassFilesMap = Files.exists(directory) ? Files.list(directory)
+ .filter(
+ f -> Files.isRegularFile(f) && f.getFileName().toString().endsWith(".class"))
+ .collect(groupingBy(f -> {
+ String fileName = f.getFileName().toString();
+ // if f is the class file of an inner class, use the class name
+ int money = fileName.indexOf('$');
+ if (money >= 0) {
+ return fileName.substring(0, money);
+ }
+ // drop ".class"
+ return fileName.substring(0, fileName.length() - 6);
+ })) : Collections.emptyMap();
+ }
+
+ List findAndRemove(ResourceFile sourceFile) {
+ String className = sourceFile.getName();
+ if (className.endsWith(".java")) {
+ className = className.substring(0, className.length() - 5);
+ long lastModifiedSource = sourceFile.lastModified();
+ List classFiles = classToClassFilesMap.remove(className);
+ if (classFiles == null) {
+ classFiles = Collections.emptyList();
+ }
+ long lastModifiedClassFile = classFiles.isEmpty() ? -1
+ : classFiles.stream()
+ .mapToLong(p -> p.toFile().lastModified())
+ .min()
+ .getAsLong();
+ // if source is newer than the oldest binary, report
+ if (lastModifiedSource > lastModifiedClassFile) {
+ return classFiles;
+ }
+ }
+ return null;
+ }
+
+ public boolean hasExtraClassFiles() {
+ return !classToClassFilesMap.isEmpty();
+ }
+
+ public Collection extraClassFiles() {
+ return classToClassFilesMap.values()
+ .stream()
+ .flatMap(l -> l.stream())
+ .collect(Collectors.toList());
+ }
+
+ }
+
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/LessFreneticGTable.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/LessFreneticGTable.java
index f0e66aa25a..8f32417f24 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/LessFreneticGTable.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/LessFreneticGTable.java
@@ -22,8 +22,11 @@ import javax.swing.table.TableModel;
import docking.widgets.table.*;
/**
- * RowObjectSelectionManager attempts to repair selections in a filtered table
- * before and after filter events. The additional selection events, however, cause focus changes we don't want.
+ * {@link RowObjectSelectionManager} attempts to repair selections in a filtered table
+ * before and after filter events. The additional selection events, however, cause focus changes we don't want.
+ *
+ * if the behavior is a bug in RowObjectSelectionManager, and it's fixed, this
+ * class can go away and it's use in {@link BundleStatusComponentProvider} replaced with GTable.
*/
class LessFreneticGTable extends GTable {
boolean chilled = false;
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiException.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiException.java
index cba3ae1d74..756e23d848 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiException.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiException.java
@@ -17,9 +17,12 @@ package ghidra.app.plugin.core.osgi;
import ghidra.util.exception.UsrException;
+/**
+ * Wrapper for exceptions originating with an OSGi operation.
+ */
public class OSGiException extends UsrException {
/**
- * Wrapper for exceptions originating with an OSGi operation.
+ * Create an exception with given {@code message} and {@code cause}.
*
* @param message a contextual message
* @param cause the original exception
@@ -29,7 +32,7 @@ public class OSGiException extends UsrException {
}
/**
- * Wrapper for exceptions originating with an OSGi operation.
+ * Create an exception with given {@code message}.
*
* @param message a contextual message
*/
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiUtils.java
index 08e09d3e2e..f22ed26246 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiUtils.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/OSGiUtils.java
@@ -77,17 +77,17 @@ public class OSGiUtils {
/**
* parse Import-Package string from a bundle manifest
*
- * @param imports Import-Package value
+ * @param importPackageString Import-Package value
* @return deduced requirements or null if there was an error
* @throws BundleException on parse failure
*/
- static List parseImports(String imports) throws BundleException {
+ static List parseImportPackage(String importPackageString)
+ throws BundleException {
// parse it with Felix's ManifestParser to a list of BundleRequirement objects
Map headerMap = new HashMap<>();
- headerMap.put(Constants.IMPORT_PACKAGE, imports);
- ManifestParser mp;
- mp = new ManifestParser(null, null, null, headerMap);
- return mp.getRequirements();
+ headerMap.put(Constants.IMPORT_PACKAGE, importPackageString);
+ ManifestParser manifestParser = new ManifestParser(null, null, null, headerMap);
+ return manifestParser.getRequirements();
}
// from https://dzone.com/articles/locate-jar-classpath-given
@@ -102,10 +102,10 @@ public class OSGiUtils {
location = loader.getResource(classLocation);
}
if (location != null) {
- Pattern p = Pattern.compile("^.*:(.*)!.*$");
- Matcher m = p.matcher(location.toString());
- if (m.find()) {
- return m.group(1);
+ Pattern pattern = Pattern.compile("^.*:(.*)!.*$");
+ Matcher matcher = pattern.matcher(location.toString());
+ if (matcher.find()) {
+ return matcher.group(1);
}
return null; // not loaded from jar?
}
@@ -132,13 +132,14 @@ public class OSGiUtils {
.map(Path::normalize);
}
- static void collectPackagesFromDirectory(Path dirPath, Set s) {
+ static void collectPackagesFromDirectory(Path dirPath, Set packages) {
try {
- Files.walk(dirPath).filter(p -> p.toString().endsWith(".class")).forEach(p -> {
- String n = dirPath.relativize(p).toString();
- int lastSlash = n.lastIndexOf(File.separatorChar);
- s.add(lastSlash > 0 ? n.substring(0, lastSlash).replace(File.separatorChar, '.')
- : "");
+ Files.walk(dirPath).filter(p -> p.toString().endsWith(".class")).forEach(path -> {
+ String relativePath = dirPath.relativize(path).toString();
+ int lastSlash = relativePath.lastIndexOf(File.separatorChar);
+ packages
+ .add(lastSlash > 0 ? relativePath.substring(0, lastSlash).replace(File.separatorChar, '.')
+ : "");
});
}
@@ -147,13 +148,13 @@ public class OSGiUtils {
}
}
- static void collectPackagesFromJar(Path jarPath, Set s) {
+ static void collectPackagesFromJar(Path jarPath, Set packages) {
try {
try (JarFile j = new JarFile(jarPath.toFile())) {
- j.stream().filter(je -> je.getName().endsWith(".class")).forEach(je -> {
- String n = je.getName();
- int lastSlash = n.lastIndexOf('/');
- s.add(lastSlash > 0 ? n.substring(0, lastSlash).replace('/', '.') : "");
+ j.stream().filter(entry -> entry.getName().endsWith(".class")).forEach(jarEntry -> {
+ String entryName = jarEntry.getName();
+ int lastSlash = entryName.lastIndexOf('/');
+ packages.add(lastSlash > 0 ? entryName.substring(0, lastSlash).replace('/', '.') : "");
});
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptActionManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptActionManager.java
index bb4cb6e8fb..fd7ca5b497 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptActionManager.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptActionManager.java
@@ -21,6 +21,7 @@ import java.io.*;
import java.net.*;
import java.nio.file.Files;
import java.util.*;
+import java.util.function.Predicate;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -30,6 +31,7 @@ import javax.swing.KeyStroke;
import docking.ActionContext;
import docking.DockingUtils;
import docking.action.*;
+import docking.action.builder.ActionBuilder;
import docking.actions.KeyBindingUtils;
import docking.tool.ToolConstants;
import docking.widgets.table.GTable;
@@ -61,7 +63,6 @@ class GhidraScriptActionManager {
private DockingAction globalRunLastAction;
private DockingAction renameAction;
private DockingAction keyBindingAction;
- private DockingAction helpAction;
private Map actionMap = new HashMap<>();
GhidraScriptActionManager(GhidraScriptComponentProvider provider, GhidraScriptMgrPlugin plugin,
@@ -78,7 +79,7 @@ class GhidraScriptActionManager {
}
void restoreUserDefinedKeybindings(SaveState saveState) {
- Collection dirs = provider.getBundleHost().getBundlePaths();
+ Collection dirs = provider.getBundleHost().getBundleFiles();
String[] names = saveState.getNames();
for (String name : names) {
@@ -172,54 +173,32 @@ class GhidraScriptActionManager {
globalRunLastAction.firePropertyChanged(DockingActionIf.DESCRIPTION_PROPERTY, "", newDesc);
}
- private DockingAction createScriptAction(String name, String menuEntry,
- String actionDescription, Icon icon, String toolBarGroup, Runnable runnable) {
- DockingAction action = new DockingAction(name, plugin.getName()) {
- @Override
- public void actionPerformed(ActionContext context) {
- runnable.run();
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- Object contextObject = context.getContextObject();
- return contextObject instanceof ResourceFile;
- }
- };
- action.setPopupMenuData(new MenuData(new String[] { menuEntry }, icon));
- action.setToolBarData(new ToolBarData(icon, toolBarGroup));
-
- action.setDescription(actionDescription);
- action.setEnabled(false);
-
- plugin.getTool().addLocalAction(provider, action);
-
- return action;
+ private DockingAction createScriptAction(String name, String menuEntry, String description,
+ Icon icon, String toolBarGroup, Runnable runnable) {
+ return new ActionBuilder(name, plugin.getName()).popupMenuPath(menuEntry)
+ .popupMenuIcon(icon)
+ .toolBarIcon(icon)
+ .toolBarGroup(toolBarGroup)
+ .description(description)
+ .enabled(false)
+ .enabledWhen(context -> context.getContextObject() instanceof ResourceFile)
+ .onAction(context -> runnable.run())
+ .buildAndInstallLocal(provider);
}
- private DockingAction createScriptTableAction(String name, String actionDescription, Icon icon,
+ private DockingAction createScriptTableAction(String name, String description, Icon icon,
Runnable runnable) {
- DockingAction action = new DockingAction(name, plugin.getName()) {
- @Override
- public void actionPerformed(ActionContext context) {
- runnable.run();
- }
-
- @Override
- public boolean isAddToPopup(ActionContext context) {
+ return new ActionBuilder(name, plugin.getName()).popupMenuPath(name)
+ .popupMenuIcon(icon)
+ .toolBarIcon(icon)
+ .toolBarGroup(null)
+ .description(description)
+ .enabledWhen(context -> {
Object contextObject = context.getContextObject();
return (contextObject instanceof GTable) || (contextObject instanceof ResourceFile);
- }
- };
- action.setPopupMenuData(new MenuData(new String[] { name }, icon));
- action.setToolBarData(new ToolBarData(icon, null));
-
- action.setDescription(actionDescription);
- action.setEnabled(true);
-
- plugin.getTool().addLocalAction(provider, action);
-
- return action;
+ })
+ .onAction(context -> runnable.run())
+ .buildAndInstallLocal(provider);
}
private void createActions() {
@@ -253,60 +232,57 @@ class GhidraScriptActionManager {
newAction = createScriptTableAction("New", "Create New Script",
ResourceManager.loadImage("images/script_add.png"), provider::newScript);
- createScriptTableAction("Refresh", "Refresh Script List",
- Icons.REFRESH_ICON, provider::refresh);
+ createScriptTableAction("Refresh", "Refresh Script List", Icons.REFRESH_ICON,
+ provider::refresh);
showBundleStatusAction = createScriptTableAction("Script Directories",
"Manage Script Directories", ResourceManager.loadImage("images/text_list_bullets.png"),
provider::showBundleStatusComponent);
- helpAction = new DockingAction("Ghidra API Help", plugin.getName()) {
- @Override
- public void actionPerformed(ActionContext context) {
- showGhidraScriptJavadoc();
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- Object contextObject = context.getContextObject();
- return (contextObject instanceof GTable) || (contextObject instanceof ResourceFile);
- }
-
- @Override
- public boolean isAddToPopup(ActionContext context) {
- Object contextObject = context.getContextObject();
- return (contextObject instanceof GTable) || (contextObject instanceof ResourceFile);
- }
+ Icon icon = ResourceManager.loadImage("images/red-cross.png");
+ Predicate test = context -> {
+ Object contextObject = context.getContextObject();
+ return (contextObject instanceof GTable) || (contextObject instanceof ResourceFile);
};
- helpAction.setPopupMenuData(new MenuData(new String[] { "Ghidra API Help" },
- ResourceManager.loadImage("images/red-cross.png"), null));
- helpAction.setToolBarData(
- new ToolBarData(ResourceManager.loadImage("images/red-cross.png"), null));
+ new ActionBuilder("Ghidra API Help", plugin.getName()).popupMenuPath("Ghidra API Help")
+ .popupMenuIcon(icon)
+ .popupWhen(test)
+ .toolBarIcon(icon)
+ .toolBarGroup(null)
+ .description("Help")
+ .helpLocation(new HelpLocation(plugin.getName(), "Help"))
+ .enabledWhen(test)
+ .onAction(context -> showGhidraScriptJavadoc())
+ .buildAndInstallLocal(provider);
- helpAction.setDescription("Help");
- helpAction.setEnabled(true);
- helpAction.setHelpLocation(new HelpLocation(plugin.getName(), "Help"));
- plugin.getTool().addLocalAction(provider, helpAction);
-
- DockingAction globalHelpAction = new DockingAction("Ghidra API Help", plugin.getName()) {
+ // XXX In order to override a method of the new DockingAction and use the builder, we
+ // need to override the build method of the ActionBuilder. When the ActionBuilder is
+ // updated, this code can be cleaned up.
+ new ActionBuilder("Ghidra API Help", plugin.getName()) {
@Override
- public void actionPerformed(ActionContext context) {
- showGhidraScriptJavadoc();
- }
+ public DockingAction build() {
+ validate();
+ DockingAction action = new DockingAction(name, owner, keyBindingType) {
+ @Override
+ public void actionPerformed(ActionContext context) {
+ actionCallback.accept(context);
+ }
- @Override
- public boolean shouldAddToWindow(boolean isMainWindow, Set> contextTypes) {
- return true;
+ @Override
+ public boolean shouldAddToWindow(boolean isMainWindow,
+ Set> contextTypes) {
+ return true;
+ }
+ };
+ decorateAction(action);
+ return action;
}
- };
- globalHelpAction.setEnabled(true);
- globalHelpAction.setHelpLocation(new HelpLocation("Misc", "Welcome_to_Ghidra_Help"));
- globalHelpAction.setMenuBarData(
- new MenuData(new String[] { ToolConstants.MENU_HELP, "Ghidra API Help" }, null,
- ToolConstants.HELP_CONTENTS_MENU_GROUP));
- plugin.getTool().addAction(globalHelpAction);
-
+ }.menuGroup(ToolConstants.HELP_CONTENTS_MENU_GROUP)
+ .menuPath(ToolConstants.MENU_HELP, "Ghidra API Help")
+ .helpLocation(new HelpLocation("Misc", "Welcome_to_Ghidra_Help"))
+ .onAction(context -> showGhidraScriptJavadoc())
+ .buildAndInstall(plugin.getTool());
}
private void showGhidraScriptJavadoc() {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java
index 60765808a7..c163edab6a 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptComponentProvider.java
@@ -458,7 +458,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
.stream()
.filter(GhidraSourceBundle.class::isInstance)
.filter(GhidraBundle::isEnabled)
- .map(GhidraBundle::getPath)
+ .map(GhidraBundle::getFile)
.collect(Collectors.toList());
}
@@ -471,7 +471,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
.filter(GhidraSourceBundle.class::isInstance)
.filter(Predicate.not(GhidraBundle::isSystemBundle))
.filter(GhidraBundle::isEnabled)
- .map(GhidraBundle::getPath)
+ .map(GhidraBundle::getFile)
.collect(Collectors.toList());
}
@@ -530,7 +530,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
}
void enableScriptDirectory(ResourceFile scriptDir) {
- bundleHost.enablePath(scriptDir);
+ bundleHost.enable(scriptDir);
Msg.showInfo(this, getComponent(), "Script Path Added/Enabled",
"The directory has been automatically enabled for use:\n" +
scriptDir.getAbsolutePath());
@@ -586,7 +586,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
}
void runScript(String scriptName, TaskListener listener) {
- for (ResourceFile dir : bundleHost.getBundlePaths()) {
+ for (ResourceFile dir : bundleHost.getBundleFiles()) {
if (dir.isDirectory()) {
ResourceFile scriptSource = new ResourceFile(dir, scriptName);
if (scriptSource.exists()) {
@@ -711,55 +711,6 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
bundleStatusComponentProvider.setVisible(true);
}
- class RefreshingBundleHostListener implements BundleHostListener {
-
- @Override
- public void bundleBuilt(GhidraBundle bundle, String summary) {
- if (bundle instanceof GhidraSourceBundle) {
- GhidraSourceBundle sourceBundle = (GhidraSourceBundle) bundle;
- for (ResourceFile sourceFile : sourceBundle.getNewSources()) {
- if (infoManager.containsMetadata(sourceFile)) {
- ScriptInfo scriptInfo = infoManager.getExistingScriptInfo(sourceFile);
- GhidraBundle.BuildFailure e = sourceBundle.getErrors(sourceFile);
- scriptInfo.setCompileErrors(e != null);
- }
- }
- tableModel.fireTableDataChanged();
- }
- }
-
- @Override
- public void bundleEnablementChange(GhidraBundle bundle, boolean newEnablment) {
- if (bundle instanceof GhidraSourceBundle) {
- refresh();
- }
- }
-
- @Override
- public void bundleAdded(GhidraBundle bundle) {
- plugin.getTool().setConfigChanged(true);
- refresh();
- }
-
- @Override
- public void bundlesAdded(Collection bundles) {
- plugin.getTool().setConfigChanged(true);
- refresh();
- }
-
- @Override
- public void bundleRemoved(GhidraBundle bundle) {
- plugin.getTool().setConfigChanged(true);
- refresh();
- }
-
- @Override
- public void bundlesRemoved(Collection bundles) {
- plugin.getTool().setConfigChanged(true);
- refresh();
- }
- }
-
void refresh() {
hasBeenRefreshed = true;
@@ -778,8 +729,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
private void updateAvailableScriptFilesForAllPaths() {
List scriptsToRemove = tableModel.getScripts();
List scriptAccumulator = new ArrayList<>();
- for (ResourceFile bundlePath : getScriptDirectories()) {
- updateAvailableScriptFilesForDirectory(scriptsToRemove, scriptAccumulator, bundlePath);
+ for (ResourceFile bundleFile : getScriptDirectories()) {
+ updateAvailableScriptFilesForDirectory(scriptsToRemove, scriptAccumulator, bundleFile);
}
// note: do this after the loop to prevent a flurry of table model update events
@@ -1135,21 +1086,6 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
return taskListener;
}
- private class ScriptTaskListener implements TaskListener {
- @Override
- public void taskCancelled(Task task) {
- taskCompleted(task);
- }
-
- @Override
- public void taskCompleted(Task task) {
- Rectangle visibleRect = scriptTable.getVisibleRect();
- scriptTable.repaint(visibleRect);
- }
- }
-
- /********************************************************************/
-
@Override
public void componentShown() {
if (!hasBeenRefreshed) {
@@ -1199,6 +1135,69 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
}
}
+ /** passed to runScript, repaints scriptTable when a script completes */
+ private class ScriptTaskListener implements TaskListener {
+ @Override
+ public void taskCancelled(Task task) {
+ taskCompleted(task);
+ }
+
+ @Override
+ public void taskCompleted(Task task) {
+ Rectangle visibleRect = scriptTable.getVisibleRect();
+ scriptTable.repaint(visibleRect);
+ }
+ }
+
+ class RefreshingBundleHostListener implements BundleHostListener {
+
+ @Override
+ public void bundleBuilt(GhidraBundle bundle, String summary) {
+ if (bundle instanceof GhidraSourceBundle) {
+ GhidraSourceBundle sourceBundle = (GhidraSourceBundle) bundle;
+ for (ResourceFile sourceFile : sourceBundle.getNewSources()) {
+ if (infoManager.containsMetadata(sourceFile)) {
+ ScriptInfo scriptInfo = infoManager.getExistingScriptInfo(sourceFile);
+ BuildError e = sourceBundle.getErrors(sourceFile);
+ scriptInfo.setCompileErrors(e != null);
+ }
+ }
+ tableModel.fireTableDataChanged();
+ }
+ }
+
+ @Override
+ public void bundleEnablementChange(GhidraBundle bundle, boolean newEnablment) {
+ if (bundle instanceof GhidraSourceBundle) {
+ refresh();
+ }
+ }
+
+ @Override
+ public void bundleAdded(GhidraBundle bundle) {
+ plugin.getTool().setConfigChanged(true);
+ refresh();
+ }
+
+ @Override
+ public void bundlesAdded(Collection bundles) {
+ plugin.getTool().setConfigChanged(true);
+ refresh();
+ }
+
+ @Override
+ public void bundleRemoved(GhidraBundle bundle) {
+ plugin.getTool().setConfigChanged(true);
+ refresh();
+ }
+
+ @Override
+ public void bundlesRemoved(Collection bundles) {
+ plugin.getTool().setConfigChanged(true);
+ refresh();
+ }
+ }
+
/** Table filter that uses the state of the tree to further filter */
private class ScriptTableSecondaryFilter implements TableFilter {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java
index f5334e3bc6..6c39be52a5 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptEditorComponentProvider.java
@@ -133,8 +133,8 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider {
}
}
- private boolean isReadOnly(ResourceFile scriptSourceFile1) {
- return GhidraScriptUtil.isSystemScriptPath(scriptSourceFile1);
+ private static boolean isReadOnly(ResourceFile scriptSourceFile) {
+ return GhidraScriptUtil.isSystemScript(scriptSourceFile);
}
// private boolean isSystemScript() {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin.java
index cdf84677ca..0b73e94ac6 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin.java
@@ -48,7 +48,8 @@ import ghidra.util.task.TaskListener;
)
//@formatter:on
public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScriptService {
- private static int loaded = 0;
+ /** number of GhidraScriptMgrPlugin references to the BundleHost owned by {@link GhidraScriptUtil} */
+ private static int referenceCount = 0;
private final GhidraScriptComponentProvider provider;
@@ -61,14 +62,16 @@ public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScript
*/
public GhidraScriptMgrPlugin(PluginTool tool) {
super(tool, true, true, true);
- if (loaded == 0) {
+ // Each tool starts a new script manager plugin, but we only ever want one bundle host.
+ // We store the one BundleHost in GhidraScriptUtil and keep a count of references to it.
+ if (referenceCount == 0) {
bundleHost = new BundleHost();
GhidraScriptUtil.initialize(bundleHost, null);
}
else {
bundleHost = GhidraScriptUtil.getBundleHost();
}
- loaded += 1;
+ referenceCount += 1;
provider = new GhidraScriptComponentProvider(this, bundleHost);
}
@@ -77,8 +80,8 @@ public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScript
protected void dispose() {
super.dispose();
provider.dispose();
- loaded -= 1;
- if (loaded == 0) {
+ referenceCount -= 1;
+ if (referenceCount == 0) {
GhidraScriptUtil.dispose();
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScript.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScript.java
index a32f2731a5..66eec871b7 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScript.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScript.java
@@ -98,9 +98,9 @@ import ghidra.util.task.TaskMonitor;
*
* Ghidra Script State
*
- *
- * All scripts, when run, will be handed the current state in the form of class
- * instance variable. These variables are:
+ *
+ *
All scripts, when run, will be handed the current state in the form of class instance
+ * variable. These variables are:
*
* currentProgram: the active program
* currentAddress: the address of the current cursor location in the tool
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptInfoManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptInfoManager.java
index fe6ef5c804..e3d0974cbf 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptInfoManager.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptInfoManager.java
@@ -79,7 +79,7 @@ public class GhidraScriptInfoManager {
* Returns the script info object for the specified script file,
* construct a new one if necessary.
*
- * Only call this method if you expect to be creating ScriptInfo objects.
+ * Only call this method if you expect to be creating ScriptInfo objects.
* Prefer getExistingScriptInfo instead.
*
* @param scriptFile the script file
@@ -122,10 +122,9 @@ public class GhidraScriptInfoManager {
public ScriptInfo getExistingScriptInfo(ResourceFile script) {
ScriptInfo info = scriptFileToInfoMap.get(script);
if (info == null) {
- String s = (script.exists() ? "" : "non") + "existing script" + script.toString() +
+ String error = (script.exists() ? "" : "non") + "existing script" + script.toString() +
" is missing expected ScriptInfo";
- System.err.println(s);
- Msg.showError(GhidraScriptInfoManager.class, null, "ScriptInfo lookup", s);
+ Msg.showError(GhidraScriptInfoManager.class, null, "ScriptInfo lookup", error);
}
return info;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProperties.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProperties.java
index eef2cb8191..7d15b22e72 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProperties.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptProperties.java
@@ -26,7 +26,7 @@ import ghidra.util.Msg;
* Handles processing for .properties files associated with a GhidraScript (.properties file and
* script should share the same basename).
*
- * This should only be called/used by the GhidraScript class.
+ *
This should only be called/used by the GhidraScript class.
*/
public class GhidraScriptProperties {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java
index 6c1e24768d..e4ad037e58 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScriptUtil.java
@@ -24,6 +24,7 @@ import java.util.stream.Collectors;
import generic.jar.ResourceFile;
import ghidra.app.plugin.core.osgi.BundleHost;
import ghidra.app.plugin.core.osgi.OSGiException;
+import ghidra.app.plugin.core.script.GhidraScriptMgrPlugin;
import ghidra.framework.Application;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
@@ -37,6 +38,9 @@ public class GhidraScriptUtil {
*/
public static String USER_SCRIPTS_DIR = buildUserScriptsDirectory();
+ /**
+ * this instance is Ghidra's singleton, a reference is held here and in {@link GhidraScriptMgrPlugin}
+ */
private static BundleHost bundleHost;
private static final String SCRIPTS_SUBDIR_NAME = "ghidra_scripts";
@@ -87,7 +91,7 @@ public class GhidraScriptUtil {
}
bundleHost.add(getUserScriptDirectory(), true, false);
- bundleHost.add(getSystemScriptPaths(), true, true);
+ bundleHost.add(getSystemScriptDirectories(), true, true);
}
/**
@@ -106,13 +110,19 @@ public class GhidraScriptUtil {
* @return a list of the current script directories
*/
public static List getScriptSourceDirectories() {
- return bundleHost.getBundlePaths()
+ return bundleHost.getBundleFiles()
.stream()
.filter(ResourceFile::isDirectory)
.collect(Collectors.toList());
}
- public static ResourceFile getSourceDirectoryContaining(ResourceFile sourceFile) {
+ /**
+ * Search the currently managed source directories for the given script file.
+ *
+ * @param sourceFile the source file
+ * @return the source directory if found, or null if not
+ */
+ public static ResourceFile findSourceDirectoryContaining(ResourceFile sourceFile) {
String sourcePath = sourceFile.getAbsolutePath();
for (ResourceFile sourceDir : getScriptSourceDirectories()) {
if (sourcePath.startsWith(sourceDir.getAbsolutePath() + File.separatorChar)) {
@@ -122,6 +132,12 @@ public class GhidraScriptUtil {
return null;
}
+ /**
+ * Search the currently managed scripts for one with the given name.
+ *
+ * @param scriptName the name
+ * @return the first file found or null if none are found
+ */
public static ResourceFile findScriptByName(String scriptName) {
return findScriptFileInPaths(getScriptSourceDirectories(), scriptName);
}
@@ -148,22 +164,22 @@ public class GhidraScriptUtil {
* Returns a list of the default script directories.
* @return a list of the default script directories
*/
- public static List getSystemScriptPaths() {
- List pathsList = new ArrayList<>();
+ public static List getSystemScriptDirectories() {
+ List dirList = new ArrayList<>();
- addScriptPaths(pathsList, SCRIPTS_SUBDIR_NAME);
- addScriptPaths(pathsList, DEV_SCRIPTS_SUBDIR_NAME);
+ addScriptDirectories(dirList, SCRIPTS_SUBDIR_NAME);
+ addScriptDirectories(dirList, DEV_SCRIPTS_SUBDIR_NAME);
- Collections.sort(pathsList);
- return pathsList;
+ Collections.sort(dirList);
+ return dirList;
}
public static ResourceFile getUserScriptDirectory() {
return new ResourceFile(USER_SCRIPTS_DIR);
}
- private static void addScriptPaths(List pathsList, String directoryName) {
- pathsList.addAll(Application.findModuleSubDirectories(directoryName));
+ private static void addScriptDirectories(List dirList, String directoryName) {
+ dirList.addAll(Application.findModuleSubDirectories(directoryName));
}
/**
@@ -171,7 +187,7 @@ public class GhidraScriptUtil {
* @param file script file or directory
* @return true if file contained within Ghidra installation area
*/
- public static boolean isSystemScriptPath(ResourceFile file) {
+ public static boolean isSystemScript(ResourceFile file) {
return isSystemFile(file);
}
@@ -333,8 +349,8 @@ public class GhidraScriptUtil {
/**
* Fixup name issues, such as package parts in the name and inner class names.
- *
- * This method can handle names with or without '.java' at the end; names with
+ *
+ *
This method can handle names with or without '.java' at the end; names with
* '$' (inner classes) and names with '.' characters for package separators
*
* @param name the name of the script
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java
index 815b9ad87e..e1eefcb3d6 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java
@@ -40,7 +40,7 @@ public class JavaScriptProvider extends GhidraScriptProvider {
* @return the bundle
*/
public GhidraSourceBundle getBundleForSource(ResourceFile sourceFile) {
- ResourceFile sourceDir = GhidraScriptUtil.getSourceDirectoryContaining(sourceFile);
+ ResourceFile sourceDir = GhidraScriptUtil.findSourceDirectoryContaining(sourceFile);
if (sourceDir == null) {
return null;
}
@@ -111,15 +111,15 @@ public class JavaScriptProvider extends GhidraScriptProvider {
* @throws Exception if build, activation, or class loading fail
*/
public Class> loadClass(ResourceFile sourceFile, PrintWriter writer) throws Exception {
- GhidraSourceBundle gb = getBundleForSource(sourceFile);
- gb.build(writer);
+ GhidraSourceBundle bundle = getBundleForSource(sourceFile);
+ bundle.build(writer);
- Bundle b = bundleHost.install(gb);
+ Bundle osgiBundle = bundleHost.install(bundle);
- bundleHost.activateSynchronously(b);
+ bundleHost.activateSynchronously(osgiBundle);
- String classname = gb.classNameForScript(sourceFile);
- Class> clazz = b.loadClass(classname); // throws ClassNotFoundException
+ String classname = bundle.classNameForScript(sourceFile);
+ Class> clazz = osgiBundle.loadClass(classname); // throws ClassNotFoundException
return clazz;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/ResourceFileJavaFileManager.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/ResourceFileJavaFileManager.java
index 3c4f05d67f..d4b151e323 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/ResourceFileJavaFileManager.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/ResourceFileJavaFileManager.java
@@ -26,9 +26,9 @@ import generic.jar.ResourceFile;
import ghidra.util.exception.AssertException;
/**
- * A {@link JavaFileManager} that works with Ghidra's {@link ResourceFile}'s.
- *
- * This class is used to dynamically compile Ghidra scripts.
+ * A {@link JavaFileManager} that works with Ghidra's {@link ResourceFile}s.
+ *
+ *
This class is used to dynamically compile Ghidra scripts.
*/
public class ResourceFileJavaFileManager implements JavaFileManager {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/ResourceFileJavaFileObject.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/ResourceFileJavaFileObject.java
index 8531ea7896..2d381432e7 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/ResourceFileJavaFileObject.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/ResourceFileJavaFileObject.java
@@ -27,8 +27,8 @@ import generic.jar.ResourceFile;
/**
* A {@link JavaFileObject} that works with Ghidra's {@link ResourceFileJavaFileManager}.
- *
- * This class is used to dynamically compile Ghidra scripts.
+ *
+ *
This class is used to dynamically compile Ghidra scripts.
*/
public class ResourceFileJavaFileObject implements JavaFileObject {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/StringTransformer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/StringTransformer.java
index 5ebbe7594f..a0054354be 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/StringTransformer.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/StringTransformer.java
@@ -16,5 +16,5 @@
package ghidra.app.script;
public interface StringTransformer {
- T apply(String s);
+ public T apply(String s);
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java b/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java
index 5f4b846bb4..5d5a699cc0 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/test/TestEnv.java
@@ -550,33 +550,33 @@ public class TestEnv {
});
}
- public ScriptTaskListener runScript(File script) throws PluginException {
- GhidraScriptMgrPlugin sm = getPlugin(GhidraScriptMgrPlugin.class);
- if (sm == null) {
+ public ScriptTaskListener runScript(File scriptFile) throws PluginException {
+ GhidraScriptMgrPlugin scriptManagerPlugin = getPlugin(GhidraScriptMgrPlugin.class);
+ if (scriptManagerPlugin == null) {
lazyTool().addPlugin(GhidraScriptMgrPlugin.class.getName());
- sm = getPlugin(GhidraScriptMgrPlugin.class);
+ scriptManagerPlugin = getPlugin(GhidraScriptMgrPlugin.class);
}
JavaScriptProvider scriptProvider = new JavaScriptProvider();
PrintWriter writer = new PrintWriter(System.out);
- ResourceFile resourceFile = new ResourceFile(script);
- GhidraScript scr=null;
+ ResourceFile resourceFile = new ResourceFile(scriptFile);
+ GhidraScript script=null;
try {
- scr=scriptProvider.getScriptInstance(resourceFile, writer);
+ script=scriptProvider.getScriptInstance(resourceFile, writer);
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
- if (scr==null) {
+ if (script==null) {
writer.flush();
- throw new RuntimeException("Failed to compile script " + script.getAbsolutePath());
+ throw new RuntimeException("Failed to compile script " + scriptFile.getAbsolutePath());
}
- String scriptName = script.getName();
+ String scriptName = scriptFile.getName();
ScriptTaskListener listener = new ScriptTaskListener(scriptName);
- sm.runScript(scriptName, listener);
+ scriptManagerPlugin.runScript(scriptName, listener);
return listener;
}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java
index 7927919d60..e717fe449d 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/AbstractGhidraScriptMgrPluginTest.java
@@ -261,20 +261,20 @@ public abstract class AbstractGhidraScriptMgrPluginTest
protected ResourceFile finishNewScriptDialog(String newScriptName) {
- SaveDialog sd = waitForDialogComponent(SaveDialog.class);
+ SaveDialog saveDialog = waitForDialogComponent(SaveDialog.class);
if (newScriptName != null) {
- setNewScriptName(sd, newScriptName);
+ setNewScriptName(saveDialog, newScriptName);
}
- pressButtonByText(sd, "OK");
+ pressButtonByText(saveDialog, "OK");
waitForSwing();
- ResourceFile newFile = (ResourceFile) invokeInstanceMethod("getFile", sd);
+ ResourceFile newFile = (ResourceFile) invokeInstanceMethod("getFile", saveDialog);
assertNotNull(newFile);
- JTextField textField = (JTextField) getInstanceField("nameField", sd);
- assertTrue("New script dialog did not close. Message: " + sd.getStatusText() +
- " - text: " + textField.getText(), !sd.isShowing());
+ JTextField textField = (JTextField) getInstanceField("nameField", saveDialog);
+ assertTrue("New script dialog did not close. Message: " + saveDialog.getStatusText() +
+ " - text: " + textField.getText(), !saveDialog.isShowing());
return newFile;
}
@@ -334,8 +334,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
GDynamicColumnTableModel, ?> model =
(GDynamicColumnTableModel, ?>) RowObjectTableModel.unwrap(tableModel);
- int n = model.getColumnCount();
- for (int i = 0; i < n; i++) {
+ int columnCount = model.getColumnCount();
+ for (int i = 0; i < columnCount; i++) {
String name = model.getColumnName(i);
if (columnName.equals(name)) {
return i;
@@ -405,8 +405,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
chooseJavaProvider();
- SaveDialog sd = waitForDialogComponent(SaveDialog.class);
- pressButtonByText(sd, "OK");
+ SaveDialog saveDialog = waitForDialogComponent(SaveDialog.class);
+ pressButtonByText(saveDialog, "OK");
waitForSwing();
// initialize our editor variable to the newly opened editor
@@ -415,7 +415,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
waitForSwing();
- return sd.getFile();
+ return saveDialog.getFile();
}
protected void assertCannotCreateNewScriptByName(final String name) throws Exception {
@@ -847,8 +847,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
protected void assertEditorContentsSame(ResourceFile file, String expectedText) {
- Map map = provider.getEditorMap();
- GhidraScriptEditorComponentProvider fileEditor = map.get(file);
+ Map editorMap = provider.getEditorMap();
+ GhidraScriptEditorComponentProvider fileEditor = editorMap.get(file);
final JTextArea textArea =
(JTextArea) findComponentByName(fileEditor.getComponent(), GhidraScriptEditorComponentProvider.EDITOR_COMPONENT_NAME);
assertNotNull(textArea);
@@ -985,7 +985,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
// destroy any NewScriptxxx files...and Temp ones too
List paths = provider.getBundleHost()
- .getBundlePaths()
+ .getBundleFiles()
.stream()
.filter(ResourceFile::isDirectory)
.collect(Collectors.toList());
@@ -1139,7 +1139,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
private boolean isReadOnly(ResourceFile script) {
assertNotNull(script);
- return GhidraScriptUtil.isSystemScriptPath(script);
+ return GhidraScriptUtil.isSystemScript(script);
}
protected void assertSaveButtonDisabled() {
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/BundleHostTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/BundleHostTest.java
index 4e7e47112b..5eadf09404 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/BundleHostTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/BundleHostTest.java
@@ -32,7 +32,15 @@ import ghidra.app.plugin.core.osgi.*;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
- static protected void wipe(Path path) throws IOException {
+ BundleHost bundleHost;
+ CapturingBundleHostListener capturingBundleHostListener;
+
+ Set tempDirs = new HashSet<>();
+ LinkedList bundleStack = new LinkedList<>();
+ GhidraBundle currentBundle;
+
+
+ protected static void wipe(Path path) throws IOException {
if (Files.exists(path)) {
for (Path p : (Iterable) Files.walk(path).sorted(
Comparator.reverseOrder())::iterator) {
@@ -41,23 +49,17 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
}
}
- BundleHost bundleHost;
- CapturingBundleHostListener bhl;
-
- Set tmpdirs = new HashSet<>();
- LinkedList gbstack = new LinkedList<>();
- GhidraBundle current_gb;
protected GhidraBundle pushNewBundle() throws IOException {
- String dir = String.format("sourcebundle%03d", tmpdirs.size());
+ String dir = String.format("sourcebundle%03d", tempDirs.size());
Path tmpDir = new File(getTestDirectoryPath(), dir).toPath();
Files.createDirectories(tmpDir);
- tmpdirs.add(tmpDir);
+ tempDirs.add(tmpDir);
- ResourceFile rp = new ResourceFile(tmpDir.toFile());
- current_gb = bundleHost.add(rp, true, false);
- gbstack.push(current_gb);
- return current_gb;
+ ResourceFile sourceDirectory = new ResourceFile(tmpDir.toFile());
+ currentBundle = bundleHost.add(sourceDirectory, true, false);
+ bundleStack.push(currentBundle);
+ return currentBundle;
}
static class CapturingBundleHostListener implements BundleHostListener {
@@ -75,8 +77,8 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
bundleHost = new BundleHost();
bundleHost.startFramework();
- bhl = new CapturingBundleHostListener();
- bundleHost.addListener(bhl);
+ capturingBundleHostListener = new CapturingBundleHostListener();
+ bundleHost.addListener(capturingBundleHostListener);
pushNewBundle();
}
@@ -84,29 +86,29 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
@After
public void tearDown() throws IOException {
bundleHost.dispose();
- bhl = null;
+ capturingBundleHostListener = null;
bundleHost = null;
- for (Path tmpdir : tmpdirs) {
+ for (Path tmpdir : tempDirs) {
wipe(tmpdir);
}
}
protected void buildWithExpectations(String expectedCompilerOutput, String expectedSummary)
throws Exception {
- StringWriter sw = new StringWriter();
+ StringWriter stringWriter = new StringWriter();
- current_gb.build(new PrintWriter(sw));
- sw.flush();
+ currentBundle.build(new PrintWriter(stringWriter));
+ stringWriter.flush();
assertEquals("unexpected output during build", expectedCompilerOutput,
- sw.getBuffer().toString());
+ stringWriter.getBuffer().toString());
- assertEquals("wrong summary", expectedSummary, bhl.lastBuildSummary);
+ assertEquals("wrong summary", expectedSummary, capturingBundleHostListener.lastBuildSummary);
}
protected void activate() throws Exception {
- Bundle bundle = bundleHost.install(current_gb);
+ Bundle bundle = bundleHost.install(currentBundle);
assertNotNull("failed to install bundle", bundle);
bundle.start();
}
@@ -117,7 +119,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
}
protected Class> loadClass(String classname) throws ClassNotFoundException {
- Class> clazz = current_gb.getOSGiBundle().loadClass(classname);
+ Class> clazz = currentBundle.getOSGiBundle().loadClass(classname);
assertNotNull("failed to load class", clazz);
return clazz;
}
@@ -133,19 +135,19 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
protected void addClass(String meta, String imports, String fullclassname, String body)
throws IOException {
String simplename;
- Path tmpsource = current_gb.getPath().getFile(false).toPath();
+ Path tmpsource = currentBundle.getFile().getFile(false).toPath();
if (fullclassname.contains(".")) {
String packagename;
- Pattern classpat = Pattern.compile("^(.*)\\.([^.]*)$");
- Matcher m = classpat.matcher(fullclassname);
- if (!m.matches()) {
+ Pattern pattern = Pattern.compile("^(.*)\\.([^.]*)$");
+ Matcher matcher = pattern.matcher(fullclassname);
+ if (!matcher.matches()) {
throw new IllegalArgumentException(
"fullclassname must be of the form \"xxxx.xxxx.Xxxx\"");
}
- packagename = m.group(1);
- simplename = m.group(2);
+ packagename = matcher.group(1);
+ simplename = matcher.group(2);
for (String n : packagename.split("\\.")) {
tmpsource = tmpsource.resolve(n);
@@ -169,9 +171,9 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
protected Object getInstance(String classname) throws Exception {
Class> clazz = loadClass(classname);
- Object o = clazz.getDeclaredConstructor().newInstance();
- assertNotNull("failed to create instance", o);
- return o;
+ Object object = clazz.getDeclaredConstructor().newInstance();
+ assertNotNull("failed to create instance", object);
+ return object;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@@ -221,9 +223,9 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
"BClass.java:7: error: ';' expected\n" +
" failing java goes here\n" +
" ^\n" +
- String.format("skipping %s/apackage/BClass.java\n", current_gb.getPath().toString())
+ String.format("skipping %s/apackage/BClass.java\n", currentBundle.getFile().toString())
,
- "1 failing source files"
+ "1 source file with errors"
);
// @formatter:on
@@ -290,9 +292,9 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
" ^\n" +
" symbol: variable Library\n" +
" location: class apackage.AClass\n" +
- String.format("skipping %s/apackage/AClass.java\n", current_gb.getPath().toString())
+ String.format("skipping %s/apackage/AClass.java\n", currentBundle.getFile().toString())
,
- "1 failing source files"
+ "1 source file with errors"
);
// @formatter:on
}
@@ -313,7 +315,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
pushNewBundle();
- // @importpackages tag is only parsed from classes in default package
+ // @importpackage tag is only parsed from classes in default package
addClass(
"//@importpackage lib\n"
,
@@ -353,10 +355,10 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
"}\n"
);
- Path p = current_gb.getPath().getFile(false).toPath();
- p=p.resolve("META-INF");
- Files.createDirectories(p);
- Path manifest=p.resolve("MANIFEST.MF");
+ Path path = currentBundle.getFile().getFile(false).toPath();
+ path=path.resolve("META-INF");
+ Files.createDirectories(path);
+ Path manifest=path.resolve("MANIFEST.MF");
Files.writeString(manifest,
"Manifest-Version: 1.0\n" +
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin2Test.java
index 25513c6b7c..2941477638 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin2Test.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin2Test.java
@@ -216,7 +216,7 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes
tempScriptDir.mkdir();
ResourceFile scriptDir = new ResourceFile(tempScriptDir);
- provider.getBundleHost().enablePath(scriptDir);
+ provider.getBundleHost().enable(scriptDir);
try {
// create a script file in that directory
@@ -257,8 +257,8 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes
chooseJavaProvider();
- SaveDialog sd = AbstractDockingTest.waitForDialogComponent(SaveDialog.class);
- pressButtonByText(sd, "OK");
+ SaveDialog saveDialog = AbstractDockingTest.waitForDialogComponent(SaveDialog.class);
+ pressButtonByText(saveDialog, "OK");
refreshProvider();
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin3Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin3Test.java
index 5142c63347..8e824418fa 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin3Test.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/script/GhidraScriptMgrPlugin3Test.java
@@ -148,11 +148,11 @@ public class GhidraScriptMgrPlugin3Test extends AbstractGhidraScriptMgrPluginTes
public void testRefreshFindsNewScript() throws Exception {
int rowCount = getRowCount();
- JavaScriptProvider jsp = new JavaScriptProvider();
+ JavaScriptProvider javaScriptProvider = new JavaScriptProvider();
- ResourceFile newScript = GhidraScriptUtil.createNewScript(jsp,
+ ResourceFile newScript = GhidraScriptUtil.createNewScript(javaScriptProvider,
new ResourceFile(GhidraScriptUtil.USER_SCRIPTS_DIR), provider.getScriptDirectories());
- jsp.createNewScript(newScript, null);
+ javaScriptProvider.createNewScript(newScript, null);
refreshScriptManager();
@@ -283,16 +283,16 @@ public class GhidraScriptMgrPlugin3Test extends AbstractGhidraScriptMgrPluginTes
final ResourceFile dir = new ResourceFile(getTestDirectoryPath() + "/test_scripts");
dir.getFile(false).mkdirs();
- provider.getBundleHost().enablePath(dir);
+ provider.getBundleHost().enable(dir);
waitForSwing();
pressNewButton();
chooseJavaProvider();
- SaveDialog sd = waitForDialogComponent(SaveDialog.class);
+ SaveDialog saveDialog = waitForDialogComponent(SaveDialog.class);
- final ListPanel listPanel = (ListPanel) findComponentByName(sd.getComponent(), "PATH_LIST");
+ final ListPanel listPanel = (ListPanel) findComponentByName(saveDialog.getComponent(), "PATH_LIST");
assertNotNull(listPanel);
assertTrue(listPanel.isVisible());
assertEquals(2, listPanel.getListModel().getSize());
@@ -315,11 +315,11 @@ public class GhidraScriptMgrPlugin3Test extends AbstractGhidraScriptMgrPluginTes
}, false);
waitForSwing();
- pressButtonByText(sd, "OK");
- assertTrue(!sd.isShowing());
+ pressButtonByText(saveDialog, "OK");
+ assertTrue(!saveDialog.isShowing());
waitForTasks();
- ResourceFile newScript = sd.getFile();
+ ResourceFile newScript = saveDialog.getFile();
assertTrue(newScript.exists());
assertNotNull(newScript);
diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/ShowConstantUse.java b/Ghidra/Features/Decompiler/ghidra_scripts/ShowConstantUse.java
index 0192e7d58e..24e7cea701 100644
--- a/Ghidra/Features/Decompiler/ghidra_scripts/ShowConstantUse.java
+++ b/Ghidra/Features/Decompiler/ghidra_scripts/ShowConstantUse.java
@@ -137,16 +137,16 @@ public class ShowConstantUse extends GhidraScript {
tableDialog.setMessage("Finished!");
}
- private Function getReferencedFunction(Address faddr) {
- Function f = currentProgram.getFunctionManager().getFunctionAt(faddr);
+ private Function getReferencedFunction(Address functionAddress) {
+ Function f = currentProgram.getFunctionManager().getFunctionAt(functionAddress);
// couldn't find the function, see if there is an external ref there.
if (f == null) {
Reference[] referencesFrom =
- currentProgram.getReferenceManager().getReferencesFrom(faddr);
- for (Reference element : referencesFrom) {
- if (element.isExternalReference()) {
- faddr = element.getToAddress();
- f = currentProgram.getFunctionManager().getFunctionAt(faddr);
+ currentProgram.getReferenceManager().getReferencesFrom(functionAddress);
+ for (Reference reference : referencesFrom) {
+ if (reference.isExternalReference()) {
+ functionAddress = reference.getToAddress();
+ f = currentProgram.getFunctionManager().getFunctionAt(functionAddress);
if (f != null) {
break;
}
diff --git a/Ghidra/Framework/Generic/src/main/java/generic/util/Path.java b/Ghidra/Framework/Generic/src/main/java/generic/util/Path.java
index bdb5298e86..8e64d2976b 100644
--- a/Ghidra/Framework/Generic/src/main/java/generic/util/Path.java
+++ b/Ghidra/Framework/Generic/src/main/java/generic/util/Path.java
@@ -27,7 +27,7 @@ import ghidra.framework.OperatingSystem;
public class Path implements Comparable {
public static final String GHIDRA_HOME = "$GHIDRA_HOME";
public static final String USER_HOME = "$USER_HOME";
- private ResourceFile path;
+ private ResourceFile file;
private boolean isEnabled;
private boolean isEditable;
private boolean isReadOnly;
@@ -39,10 +39,10 @@ public class Path implements Comparable {
* - isEditable = true
* - isReadOnly = false
*
- * @param path absolute directory path
+ * @param file absolute directory path
*/
- public Path(File path) {
- this(new ResourceFile(path), true, true, false);
+ public Path(File file) {
+ this(new ResourceFile(file), true, true, false);
}
/**
@@ -52,21 +52,21 @@ public class Path implements Comparable {
* isEditable = true
* isReadOnly = false
*
- * @param path absolute directory path
+ * @param file absolute directory path
*/
- public Path(ResourceFile path) {
- this(path, true, true, false);
+ public Path(ResourceFile file) {
+ this(file, true, true, false);
}
/**
* Identifies an absolute directory path with the specified attributes.
- * @param path absolute directory path
+ * @param file absolute directory path
* @param isEnabled directory path will be searched if true
* @param isEditable if true files contained within directory are considered editable
* @param isReadOnly if true files contained within directory are considered read-only
*/
- public Path(ResourceFile path, boolean isEnabled, boolean isEditable, boolean isReadOnly) {
- this.path = path;
+ public Path(ResourceFile file, boolean isEnabled, boolean isEditable, boolean isReadOnly) {
+ this.file = file;
this.isEnabled = isEnabled;
this.isEditable = isEditable;
this.isReadOnly = isReadOnly;
@@ -106,7 +106,7 @@ public class Path implements Comparable {
* @param isReadOnly if true files contained within directory are considered read-only
*/
public Path(String path, boolean isEnabled, boolean isEditable, boolean isReadOnly) {
- this.path = fromPathString(path);
+ this.file = fromPathString(path);
this.isEnabled = isEnabled;
this.isEditable = isEditable;
@@ -132,12 +132,12 @@ public class Path implements Comparable {
return false;
}
Path that = (Path) obj;
- return this.path.equals(that.path);
+ return this.file.equals(that.file);
}
@Override
public int hashCode() {
- return path.hashCode();
+ return file.hashCode();
}
/**
@@ -171,7 +171,7 @@ public class Path implements Comparable {
}
public ResourceFile getPath() {
- return path;
+ return file;
}
/**
@@ -182,36 +182,36 @@ public class Path implements Comparable {
* @return the path as a ResourceFile.
*/
public static ResourceFile fromPathString(String path) {
- ResourceFile rf = null;
+ ResourceFile resourceFile = null;
if (path.startsWith(GHIDRA_HOME)) {
- rf = resolveGhidraHome(path);
+ resourceFile = resolveGhidraHome(path);
}
else if (path.startsWith(USER_HOME)) {
String userHome = System.getProperty("user.home");
int length = USER_HOME.length();
String relativePath = path.substring(length);
- rf = new ResourceFile(new File(userHome + relativePath));
+ resourceFile = new ResourceFile(new File(userHome + relativePath));
}
else {
- rf = new ResourceFile(path);
+ resourceFile = new ResourceFile(path);
}
if (OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS) {
- rf = rf.getCanonicalFile();
+ resourceFile = resourceFile.getCanonicalFile();
}
- return rf;
+ return resourceFile;
}
/**
* Returns the path as a string with path element placeholders, such as
* {@link #GHIDRA_HOME}.
- * @param path the path
*
+ * @param file the file to translate
* @return the path as a string .
*/
- static public String toPathString(ResourceFile path) {
+ static public String toPathString(ResourceFile file) {
String userHome = System.getProperty("user.home");
- String absolutePath = path.getAbsolutePath();
+ String absolutePath = file.getAbsolutePath();
for (ResourceFile appRoot : Application.getApplicationRootDirectories()) {
String ghidraHome = appRoot.getAbsolutePath();
if (absolutePath.startsWith(ghidraHome)) {
@@ -239,7 +239,7 @@ public class Path implements Comparable {
* @return the path as a string .
*/
public String getPathAsString() {
- return toPathString(path);
+ return toPathString(file);
}
/**
@@ -253,24 +253,24 @@ public class Path implements Comparable {
public void setPath(String path) {
if (isEditable) {
- this.path = new ResourceFile(path);
+ this.file = new ResourceFile(path);
}
else {
throw new IllegalStateException("Path is not editable - " + path);
}
}
- public void setPath(ResourceFile path) {
+ public void setPath(ResourceFile file) {
if (isEditable) {
- this.path = path;
+ this.file = file;
}
else {
- throw new IllegalStateException("Path is not editable - " + path);
+ throw new IllegalStateException("Path is not editable - " + file);
}
}
public boolean exists() {
- return path.exists();
+ return file.exists();
}
@Override
diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java
index 15495f121f..7f4bcbc9ff 100644
--- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java
+++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/GhidraScriptMgrPluginScreenShots.java
@@ -111,15 +111,15 @@ public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator
@Test
public void testScript_Dirs() throws Exception {
- List paths = new ArrayList<>();
- paths.add(Path.fromPathString("$USER_HOME/ghidra_scripts"));
- paths.add(Path.fromPathString("$GHIDRA_HOME/Features/Base/ghidra_scripts"));
- paths.add(Path.fromPathString("/User/defined/invalid/directory"));
+ List bundleFiles = new ArrayList<>();
+ bundleFiles.add(Path.fromPathString("$USER_HOME/ghidra_scripts"));
+ bundleFiles.add(Path.fromPathString("$GHIDRA_HOME/Features/Base/ghidra_scripts"));
+ bundleFiles.add(Path.fromPathString("/User/defined/invalid/directory"));
BundleStatusComponentProvider bundleStatusComponentProvider =
showProvider(BundleStatusComponentProvider.class);
- bundleStatusComponentProvider.setPathsForTesting(paths);
+ bundleStatusComponentProvider.setBundleFilesForTesting(bundleFiles);
waitForComponentProvider(BundleStatusComponentProvider.class);
captureComponent(bundleStatusComponentProvider.getComponent());