style fixes for review

This commit is contained in:
Jason P. Leasure
2020-06-16 12:14:53 -04:00
parent 2015f13542
commit 0d520dae16
34 changed files with 1549 additions and 1351 deletions
@@ -45,7 +45,7 @@
<h2><a name="source_bundles"/>Source bundles</h2>
<p>When a directory is added to the Bundle Manager, it is treated as a
<em>source bundle</em> and of its Java contents will be compiled to <br>
<em>source bundle</em> and its Java contents will be compiled to <br>
&nbsp;&nbsp;&nbsp;&nbsp;<code>&lt;user home&gt;/.ghidra/.ghidra-&lt;version&gt;/osgi/compiled-bundles/&lt;hash&gt;</code>, <br>
where <code>&lt;hash&gt;</code> is a hash of the source bundle location.</p>
<h3>exploded bundles</h3>
@@ -0,0 +1,60 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.osgi;
import generic.jar.ResourceFile;
/**
* An error produced during {@link GhidraBundle#build()} with a time stamp
*/
public class BuildError {
// the lastModified time of the source causing this error
private final long lastModified;
private final StringBuilder message = new StringBuilder();
/**
* Construct an object to record error message produced for {@code sourceFile}
* @param sourceFile the file causing this error
*/
public BuildError(ResourceFile sourceFile) {
lastModified = sourceFile.lastModified();
}
/**
* Append {@code str} to the current error message
*
* @param str the string to append
*/
public void append(String str) {
message.append(str);
}
/**
* @return the error message
*/
String getMessage() {
return message.toString();
}
/**
* @return the last modified time of the source for this build error
*/
public long getLastModified() {
return lastModified;
}
}
File diff suppressed because it is too large Load Diff
@@ -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<BundleStatus> {
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<BundleStatus> {
* @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<BundleStatus> {
}
/**
* @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) {
@@ -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<BundleStatus> {
List<Column> 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<BundleStatu
Column activeColumn = new Column("Active", Boolean.class) {
@Override
boolean editable(BundleStatus status) {
return status.pathExists() && status.isEnabled();
return status.fileExists() && status.isEnabled();
}
@Override
@@ -94,7 +76,7 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
Column pathColumn = new Column("Path", ResourceFile.class) {
@Override
Object getValue(BundleStatus status) {
return status.getPath();
return status.getFile();
}
};
Column summaryColumn = new Column("Summary", String.class) {
@@ -115,77 +97,10 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
private Map<String, BundleStatus> bundleLocToStatusMap = new HashMap<>();
private BundleHostListener bundleHostListener;
private ArrayList<BundleStatusChangeRequestListener> bundleStatusListeners = new ArrayList<>();
private List<BundleStatusChangeRequestListener> bundleStatusListeners =
new CopyOnWriteArrayList<>();
private List<BundleStatus> 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<GhidraBundle> 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<GhidraBundle> bundles) {
List<BundleStatus> 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<BundleStatu
addNewStatus(bundle);
}
bundleHost.addListener(bundleHostListener = new MyBundleHostListener());
bundleHostListener = new MyBundleHostListener();
bundleHost.addListener(bundleHostListener);
}
Column getColumn(int i) {
@@ -206,7 +122,7 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
}
BundleStatus getStatus(GhidraBundle bundle) {
return getStatusFromLoc(bundle.getBundleLocation());
return getStatusFromLoc(bundle.getLocationIdentifier());
}
BundleStatus getStatusFromLoc(String bundleLoc) {
@@ -224,15 +140,25 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
bundleHost.removeListener(bundleHostListener);
}
@Override
public void fireTableChanged(TableModelEvent e) {
if (SwingUtilities.isEventDispatchThread()) {
super.fireTableChanged(e);
return;
}
final TableModelEvent e1 = e;
SwingUtilities.invokeLater(() -> 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<BundleStatu
private int removeStatusNoFire(BundleStatus status) {
if (!status.isReadOnly()) {
int i = statuses.indexOf(status);
statuses.remove(i);
bundleLocToStatusMap.remove(status.getBundleLocation());
return i;
int statusIndex = statuses.indexOf(status);
statuses.remove(statusIndex);
bundleLocToStatusMap.remove(status.getLocationIdentifier());
return statusIndex;
}
return -1;
}
@@ -289,7 +215,7 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
}
@Override
public java.lang.Class<?> getColumnClass(int columnIndex) {
public Class<?> getColumnClass(int columnIndex) {
return getColumn(columnIndex).clazz;
}
@@ -341,15 +267,13 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
/**
* Add a change request listener.
*
* When the user requests a change to the status of a bundle, each listener is called.
* <p>When 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<BundleStatu
* @param listener the listener to remove
*/
public void removeListener(BundleStatusChangeRequestListener listener) {
synchronized (bundleStatusListeners) {
bundleStatusListeners.remove(listener);
bundleStatusListeners.remove(listener);
}
void fireBundleEnablementChangeRequested(BundleStatus status, boolean newValue) {
for (BundleStatusChangeRequestListener listener : bundleStatusListeners) {
listener.bundleEnablementChangeRequest(status, newValue);
}
}
void fireBundleEnablementChangeRequested(BundleStatus path, boolean newValue) {
synchronized (bundleStatusListeners) {
for (BundleStatusChangeRequestListener listener : bundleStatusListeners) {
listener.bundleEnablementChangeRequest(path, newValue);
}
}
}
void fireBundleActivationChangeRequested(BundleStatus path, boolean newValue) {
synchronized (bundleStatusListeners) {
for (BundleStatusChangeRequestListener listener : bundleStatusListeners) {
listener.bundleActivationChangeRequest(path, newValue);
}
void fireBundleActivationChangeRequested(BundleStatus status, boolean newValue) {
for (BundleStatusChangeRequestListener listener : bundleStatusListeners) {
listener.bundleActivationChangeRequest(status, newValue);
}
}
@@ -399,6 +317,8 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
*/
@Override
protected void sort(List<BundleStatus> data, TableSortingContext<BundleStatus> 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<BundleStatu
hasEverSorted = true; // signal that we have sorted at least one time
boolean[] change = { false };
Comparator<BundleStatus> proxy = new Comparator<BundleStatus>() {
Comparator<BundleStatus> p = sortingContext.getComparator();
// wrap the assigned comparator to detect if the order changes
boolean[] changed = { false };
Comparator<BundleStatus> wrapper = new Comparator<BundleStatus>() {
Comparator<BundleStatus> 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<BundleStatu
private void computeCache() {
bundleLocToStatusMap.clear();
for (BundleStatus status : statuses) {
bundleLocToStatusMap.put(status.getBundleLocation(), status);
bundleLocToStatusMap.put(status.getLocationIdentifier(), status);
}
}
/**
* when bundles are added or removed, update the table.
* when bundles change enablement or activation, update rows.
*/
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<GhidraBundle> 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<GhidraBundle> bundles) {
List<BundleStatus> 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!");
}
}
}
@@ -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.
*
* <p>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.
*
* <p>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<BundleRequirement> 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.
* <p>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
* <ul>
* <li>a Bndtools .bnd script</li>
* <li>an OSGi bundle .jar file</li>
* <li>a directory of Java source</li>
* </u>
*
*/
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
* <ul>
* <li>a Bndtools .bnd script</li>
* <li>an OSGi bundle .jar file</li>
* <li>a directory of Java source</li>
* </ul>
*
*/
enum Type {
BND_SCRIPT, JAR, SOURCE_DIR, INVALID
}
}
@@ -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";
}
@@ -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<BundleRequirement> 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();
}
@@ -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
File diff suppressed because it is too large Load Diff
@@ -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.
*
* <p>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;
@@ -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
*/
@@ -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<BundleRequirement> parseImports(String imports) throws BundleException {
static List<BundleRequirement> parseImportPackage(String importPackageString)
throws BundleException {
// parse it with Felix's ManifestParser to a list of BundleRequirement objects
Map<String, Object> 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<String> s) {
static void collectPackagesFromDirectory(Path dirPath, Set<String> 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<String> s) {
static void collectPackagesFromJar(Path jarPath, Set<String> 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('/', '.') : "");
});
}
}
@@ -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<ResourceFile, ScriptAction> actionMap = new HashMap<>();
GhidraScriptActionManager(GhidraScriptComponentProvider provider, GhidraScriptMgrPlugin plugin,
@@ -78,7 +79,7 @@ class GhidraScriptActionManager {
}
void restoreUserDefinedKeybindings(SaveState saveState) {
Collection<ResourceFile> dirs = provider.getBundleHost().getBundlePaths();
Collection<ResourceFile> 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<ActionContext> 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<Class<?>> contextTypes) {
return true;
@Override
public boolean shouldAddToWindow(boolean isMainWindow,
Set<Class<?>> 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() {
@@ -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<GhidraBundle> bundles) {
plugin.getTool().setConfigChanged(true);
refresh();
}
@Override
public void bundleRemoved(GhidraBundle bundle) {
plugin.getTool().setConfigChanged(true);
refresh();
}
@Override
public void bundlesRemoved(Collection<GhidraBundle> bundles) {
plugin.getTool().setConfigChanged(true);
refresh();
}
}
void refresh() {
hasBeenRefreshed = true;
@@ -778,8 +729,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
private void updateAvailableScriptFilesForAllPaths() {
List<ResourceFile> scriptsToRemove = tableModel.getScripts();
List<ResourceFile> 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<GhidraBundle> bundles) {
plugin.getTool().setConfigChanged(true);
refresh();
}
@Override
public void bundleRemoved(GhidraBundle bundle) {
plugin.getTool().setConfigChanged(true);
refresh();
}
@Override
public void bundlesRemoved(Collection<GhidraBundle> bundles) {
plugin.getTool().setConfigChanged(true);
refresh();
}
}
/** Table filter that uses the state of the tree to further filter */
private class ScriptTableSecondaryFilter implements TableFilter<ResourceFile> {
@@ -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() {
@@ -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();
}
}
@@ -98,9 +98,9 @@ import ghidra.util.task.TaskMonitor;
* </pre>
* <h3>Ghidra Script State</h3>
* <blockquote>
* <p>
* All scripts, when run, will be handed the current state in the form of class
* instance variable. These variables are:
*
* <p>All scripts, when run, will be handed the current state in the form of class instance
* variable. These variables are:
* <ol>
* <li><code>currentProgram</code>: the active program</li>
* <li><code>currentAddress</code>: the address of the current cursor location in the tool</li>
@@ -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.
* <p>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;
}
@@ -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.
* <p>This should only be called/used by the GhidraScript class.
*/
public class GhidraScriptProperties {
@@ -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<ResourceFile> 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<ResourceFile> getSystemScriptPaths() {
List<ResourceFile> pathsList = new ArrayList<>();
public static List<ResourceFile> getSystemScriptDirectories() {
List<ResourceFile> 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<ResourceFile> pathsList, String directoryName) {
pathsList.addAll(Application.findModuleSubDirectories(directoryName));
private static void addScriptDirectories(List<ResourceFile> 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.
* <p>
* This method can handle names with or without '.java' at the end; names with
*
* <p>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
@@ -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;
}
@@ -26,9 +26,9 @@ import generic.jar.ResourceFile;
import ghidra.util.exception.AssertException;
/**
* A {@link JavaFileManager} that works with Ghidra's {@link ResourceFile}'s.
* <p>
* This class is used to dynamically compile Ghidra scripts.
* A {@link JavaFileManager} that works with Ghidra's {@link ResourceFile}s.
*
* <p>This class is used to dynamically compile Ghidra scripts.
*/
public class ResourceFileJavaFileManager implements JavaFileManager {
@@ -27,8 +27,8 @@ import generic.jar.ResourceFile;
/**
* A {@link JavaFileObject} that works with Ghidra's {@link ResourceFileJavaFileManager}.
* <p>
* This class is used to dynamically compile Ghidra scripts.
*
* <p>This class is used to dynamically compile Ghidra scripts.
*/
public class ResourceFileJavaFileObject implements JavaFileObject {
@@ -16,5 +16,5 @@
package ghidra.app.script;
public interface StringTransformer<T> {
T apply(String s);
public T apply(String s);
}
@@ -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;
}
@@ -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<ResourceFile, GhidraScriptEditorComponentProvider> map = provider.getEditorMap();
GhidraScriptEditorComponentProvider fileEditor = map.get(file);
Map<ResourceFile, GhidraScriptEditorComponentProvider> 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<ResourceFile> 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() {
@@ -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<Path> tempDirs = new HashSet<>();
LinkedList<GhidraBundle> bundleStack = new LinkedList<>();
GhidraBundle currentBundle;
protected static void wipe(Path path) throws IOException {
if (Files.exists(path)) {
for (Path p : (Iterable<Path>) Files.walk(path).sorted(
Comparator.reverseOrder())::iterator) {
@@ -41,23 +49,17 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
}
}
BundleHost bundleHost;
CapturingBundleHostListener bhl;
Set<Path> tmpdirs = new HashSet<>();
LinkedList<GhidraBundle> 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" +
@@ -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();
@@ -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);
@@ -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;
}
@@ -27,7 +27,7 @@ import ghidra.framework.OperatingSystem;
public class Path implements Comparable<Path> {
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<Path> {
* <li>isEditable = true</li>
* <li>isReadOnly = false</li>
* </ul>
* @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<Path> {
* <li>isEditable = true</li>
* <li>isReadOnly = false</li>
* </ul>
* @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<Path> {
* @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<Path> {
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<Path> {
}
public ResourceFile getPath() {
return path;
return file;
}
/**
@@ -182,36 +182,36 @@ public class Path implements Comparable<Path> {
* @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 <b>with path element placeholders</b>, 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<Path> {
* @return the path as a string .
*/
public String getPathAsString() {
return toPathString(path);
return toPathString(file);
}
/**
@@ -253,24 +253,24 @@ public class Path implements Comparable<Path> {
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
@@ -111,15 +111,15 @@ public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator
@Test
public void testScript_Dirs() throws Exception {
List<ResourceFile> 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<ResourceFile> 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());