mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-31 01:18:48 +08:00
style fixes for ghidra.app.plugin.core.osgi
This commit is contained in:
File diff suppressed because it is too large
Load Diff
+57
-12
@@ -22,39 +22,84 @@ import java.util.Collection;
|
|||||||
*/
|
*/
|
||||||
public interface BundleHostListener {
|
public interface BundleHostListener {
|
||||||
|
|
||||||
default void bundleBuilt(GhidraBundle gbundle, String summary) {
|
/**
|
||||||
|
* Invoked when a bundle is built.
|
||||||
|
*
|
||||||
|
* @param bundle the bundle
|
||||||
|
* @param summary a summary of the build
|
||||||
|
*/
|
||||||
|
default void bundleBuilt(GhidraBundle bundle, String summary) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
default void bundleEnablementChange(GhidraBundle gbundle, boolean newEnablement) {
|
/**
|
||||||
|
* Invoked when a bundle is enabled or disabled.
|
||||||
|
*
|
||||||
|
* @param bundle the bundle
|
||||||
|
* @param newEnablement true if enabled, false if disabled
|
||||||
|
*/
|
||||||
|
default void bundleEnablementChange(GhidraBundle bundle, boolean newEnablement) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
default void bundleActivationChange(GhidraBundle gbundle, boolean newActivation) {
|
/**
|
||||||
|
* Invoked when a bundle is activated or deactivated.
|
||||||
|
*
|
||||||
|
* @param bundle the bundle
|
||||||
|
* @param newActivation true if activated, false if deactivated
|
||||||
|
*/
|
||||||
|
default void bundleActivationChange(GhidraBundle bundle, boolean newActivation) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
default void bundleAdded(GhidraBundle gbundle) {
|
/**
|
||||||
|
* Invoked when a bundle is added to {@link BundleHost}
|
||||||
|
*
|
||||||
|
* @param bundle the bundle
|
||||||
|
*/
|
||||||
|
default void bundleAdded(GhidraBundle bundle) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
default void bundlesAdded(Collection<GhidraBundle> gbundles) {
|
/**
|
||||||
for (GhidraBundle gbundle : gbundles) {
|
* Invoked when a number of bundles is added at once. A listener should implement this method
|
||||||
bundleAdded(gbundle);
|
* to avoid repeated invocation of {@link #bundleAdded} in quick succession.
|
||||||
|
*
|
||||||
|
* @param bundles the bundles
|
||||||
|
*/
|
||||||
|
default void bundlesAdded(Collection<GhidraBundle> bundles) {
|
||||||
|
for (GhidraBundle bundle : bundles) {
|
||||||
|
bundleAdded(bundle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default void bundleRemoved(GhidraBundle gbundle) {
|
/**
|
||||||
|
* Invoked when a bundle is removed from {@link BundleHost}
|
||||||
|
*
|
||||||
|
* @param bundle the bundle
|
||||||
|
*/
|
||||||
|
default void bundleRemoved(GhidraBundle bundle) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
default void bundlesRemoved(Collection<GhidraBundle> gbundles) {
|
/**
|
||||||
for (GhidraBundle gbundle : gbundles) {
|
* Invoked when a number of bundles is removed at once. A listener should implement this method
|
||||||
bundleRemoved(gbundle);
|
* to avoid repeated invocation of {@link #bundleRemoved} in quick succession.
|
||||||
|
*
|
||||||
|
* @param bundles the bundles
|
||||||
|
*/
|
||||||
|
default void bundlesRemoved(Collection<GhidraBundle> bundles) {
|
||||||
|
for (GhidraBundle bundle : bundles) {
|
||||||
|
bundleRemoved(bundle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default void bundleException(GhidraBundleException gbe) {
|
/**
|
||||||
|
* Invoked when {@link BundleHost} excepts during bundle activation/deactivation.
|
||||||
|
*
|
||||||
|
* @param exception the exception thrown
|
||||||
|
*/
|
||||||
|
default void bundleException(GhidraBundleException exception) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,75 +20,21 @@ 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 OSGi bundles in Ghidra.
|
||||||
*
|
|
||||||
* XXX: this class relies on generic.util.Path solely for the parsing and formatting of USER_HOME and GHIDRA_HOME
|
|
||||||
*/
|
*/
|
||||||
public class BundleStatus implements Comparable<BundleStatus> {
|
public class BundleStatus implements Comparable<BundleStatus> {
|
||||||
final Path path;
|
final Path path;
|
||||||
final GhidraBundle.Type type;
|
final GhidraBundle.Type type;
|
||||||
final String bundleLoc;
|
final String bundleLocation;
|
||||||
|
|
||||||
boolean active = false;
|
boolean active = false;
|
||||||
boolean busy = false;
|
boolean busy = false;
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return path.isEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnabled(boolean isEnabled) {
|
|
||||||
path.setEnabled(isEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isReadOnly() {
|
|
||||||
return path.isReadOnly();
|
|
||||||
}
|
|
||||||
|
|
||||||
String summary;
|
String summary;
|
||||||
|
|
||||||
public GhidraBundle.Type getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
BundleStatus(ResourceFile path, boolean enabled, boolean readonly, String bundleLoc) {
|
BundleStatus(ResourceFile path, boolean enabled, boolean readonly, String bundleLoc) {
|
||||||
this.path = new Path(path, enabled, false, readonly);
|
this.path = new Path(path, enabled, false, readonly);
|
||||||
type = GhidraBundle.getType(getPath());
|
type = GhidraBundle.getType(getPath());
|
||||||
this.bundleLoc = bundleLoc;
|
this.bundleLocation = bundleLoc;
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDirectory() {
|
|
||||||
return getPath().isDirectory();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isActive() {
|
|
||||||
return active;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setActive(boolean b) {
|
|
||||||
active = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBusy(boolean b) {
|
|
||||||
busy = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBusy() {
|
|
||||||
return busy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSummary(String summary) {
|
|
||||||
this.summary = summary;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSummary() {
|
|
||||||
return summary != null ? summary : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResourceFile getPath() {
|
|
||||||
return path.getPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean pathExists() {
|
|
||||||
return path.exists();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -96,12 +42,103 @@ public class BundleStatus implements Comparable<BundleStatus> {
|
|||||||
return path.compareTo(o != null ? o.path : null);
|
return path.compareTo(o != null ? o.path : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the bundle is enabled
|
||||||
|
*/
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return path.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the bundle's status to enabled or disabled.
|
||||||
|
*
|
||||||
|
* @param isEnabled true to set status to enabled
|
||||||
|
*/
|
||||||
|
public void setEnabled(boolean isEnabled) {
|
||||||
|
path.setEnabled(isEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the bundle is read only
|
||||||
|
*/
|
||||||
|
public boolean isReadOnly() {
|
||||||
|
return path.isReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the bundle type
|
||||||
|
*
|
||||||
|
* @see GhidraBundle.Type
|
||||||
|
*/
|
||||||
|
public GhidraBundle.Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the bundle is active
|
||||||
|
*/
|
||||||
|
public boolean isActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the bundle's status to active or inactive.
|
||||||
|
*
|
||||||
|
* @param isActive true for active, false for inactive
|
||||||
|
*/
|
||||||
|
public void setActive(boolean isActive) {
|
||||||
|
active = isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the bundle's build summary.
|
||||||
|
*
|
||||||
|
* @param summary the build summary
|
||||||
|
*/
|
||||||
|
public void setSummary(String summary) {
|
||||||
|
this.summary = summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the bundle's build summary
|
||||||
|
*/
|
||||||
|
public String getSummary() {
|
||||||
|
return summary != null ? summary : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the bundle's path
|
||||||
|
*/
|
||||||
|
public ResourceFile getPath() {
|
||||||
|
return path.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the bundle's path exists
|
||||||
|
*/
|
||||||
|
public boolean pathExists() {
|
||||||
|
return path.exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the bundle's path as a string, using $USER and $GHIDRA_HOME when appropriate
|
||||||
|
*/
|
||||||
public String getPathAsString() {
|
public String getPathAsString() {
|
||||||
return path.getPathAsString();
|
return path.getPathAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getBundleLoc() {
|
/**
|
||||||
return bundleLoc;
|
* @return the bundle's location identifier
|
||||||
|
*/
|
||||||
|
public String getBundleLocation() {
|
||||||
|
return bundleLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setBusy(boolean isBusy) {
|
||||||
|
busy = isBusy;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isBusy() {
|
||||||
|
return busy;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-2
@@ -20,11 +20,23 @@ package ghidra.app.plugin.core.osgi;
|
|||||||
*/
|
*/
|
||||||
public interface BundleStatusChangeRequestListener {
|
public interface BundleStatusChangeRequestListener {
|
||||||
|
|
||||||
default public void bundleEnablementChangeRequest(BundleStatus status, boolean newValue) {
|
/**
|
||||||
|
* Invoked when the user requests that a bundle is enabled/disabled.
|
||||||
|
*
|
||||||
|
* @param status the current status
|
||||||
|
* @param newValue true if enabled, false if disabled
|
||||||
|
*/
|
||||||
|
default void bundleEnablementChangeRequest(BundleStatus status, boolean newValue) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
default public void bundleActivationChangeRequest(BundleStatus status, boolean newValue) {
|
/**
|
||||||
|
* Invoked when the user requests that a bundle is activated/deactivated.
|
||||||
|
*
|
||||||
|
* @param status the current status
|
||||||
|
* @param newValue true if activated, false if deactivated
|
||||||
|
*/
|
||||||
|
default void bundleActivationChangeRequest(BundleStatus status, boolean newValue) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+122
-111
@@ -49,7 +49,10 @@ import resources.ResourceManager;
|
|||||||
* component for managing OSGi bundle status
|
* component for managing OSGi bundle status
|
||||||
*/
|
*/
|
||||||
public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||||
static String preferenceForLastSelectedBundle = "LastGhidraBundle";
|
static final String BUNDLE_GROUP = "0bundle group";
|
||||||
|
static final String BUNDLE_LIST_GROUP = "1bundle list group";
|
||||||
|
|
||||||
|
static final String PREFENCE_LAST_SELECTED_BUNDLE = "LastGhidraBundle";
|
||||||
|
|
||||||
private JPanel panel;
|
private JPanel panel;
|
||||||
private LessFreneticGTable bundleStatusTable;
|
private LessFreneticGTable bundleStatusTable;
|
||||||
@@ -60,9 +63,14 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
private GhidraFileFilter filter;
|
private GhidraFileFilter filter;
|
||||||
private final BundleHost bundleHost;
|
private final BundleHost bundleHost;
|
||||||
|
|
||||||
static final String BUNDLE_GROUP = "0bundle group";
|
/**
|
||||||
static final String BUNDLE_LIST_GROUP = "1bundle list group";
|
* {@link BundleStatusComponentProvider} visualizes bundle status and exposes actions for
|
||||||
|
* adding, removing, enabling, disabling, activating, and deactivating bundles.
|
||||||
|
*
|
||||||
|
* @param tool the tool
|
||||||
|
* @param owner the owner name
|
||||||
|
* @param bundleHost the bundle host
|
||||||
|
*/
|
||||||
public BundleStatusComponentProvider(PluginTool tool, String owner, BundleHost bundleHost) {
|
public BundleStatusComponentProvider(PluginTool tool, String owner, BundleHost bundleHost) {
|
||||||
super(tool, "BundleManager", owner);
|
super(tool, "BundleManager", owner);
|
||||||
setHelpLocation(new HelpLocation("BundleManager", "BundleManager"));
|
setHelpLocation(new HelpLocation("BundleManager", "BundleManager"));
|
||||||
@@ -136,12 +144,24 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
// to allow custom cell renderers
|
// to allow custom cell renderers
|
||||||
bundleStatusTable.setAutoCreateColumnsFromModel(false);
|
bundleStatusTable.setAutoCreateColumnsFromModel(false);
|
||||||
|
|
||||||
|
configureTableColumns();
|
||||||
|
filterPanel = new GTableFilterPanel<>(bundleStatusTable, bundleStatusTableModel);
|
||||||
|
|
||||||
|
JScrollPane scrollPane = new JScrollPane(bundleStatusTable);
|
||||||
|
scrollPane.getViewport().setBackground(bundleStatusTable.getBackground());
|
||||||
|
|
||||||
|
panel.add(filterPanel, BorderLayout.SOUTH);
|
||||||
|
panel.add(scrollPane, BorderLayout.CENTER);
|
||||||
|
panel.setPreferredSize(new Dimension(800, 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureTableColumns() {
|
||||||
TableColumn column;
|
TableColumn column;
|
||||||
|
|
||||||
int skinnyWidth = 60;
|
int skinnyWidth = 60;
|
||||||
//
|
//
|
||||||
column = bundleStatusTable.getColumnModel().getColumn(
|
column = bundleStatusTable.getColumnModel()
|
||||||
bundleStatusTableModel.enabledColumn.index);
|
.getColumn(bundleStatusTableModel.enabledColumn.index);
|
||||||
column.setPreferredWidth(skinnyWidth);
|
column.setPreferredWidth(skinnyWidth);
|
||||||
column.setMinWidth(skinnyWidth);
|
column.setMinWidth(skinnyWidth);
|
||||||
column.setMaxWidth(skinnyWidth);
|
column.setMaxWidth(skinnyWidth);
|
||||||
@@ -171,9 +191,7 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
setText("");
|
setText("");
|
||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -197,80 +215,61 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
filterPanel = new GTableFilterPanel<>(bundleStatusTable, bundleStatusTableModel);
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
JScrollPane scrollPane = new JScrollPane(bundleStatusTable);
|
@Override
|
||||||
scrollPane.getViewport().setBackground(bundleStatusTable.getBackground());
|
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);
|
||||||
|
|
||||||
panel.add(filterPanel, BorderLayout.SOUTH);
|
|
||||||
panel.add(scrollPane, BorderLayout.CENTER);
|
|
||||||
panel.setPreferredSize(new Dimension(800, 400));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createActions() {
|
private void createActions() {
|
||||||
DockingAction action;
|
DockingAction action;
|
||||||
|
|
||||||
//
|
addBundlesAction("ActivateBundles", "Activate bundle(s)",
|
||||||
action = new DockingAction("ActivateBundles", this.getName()) {
|
ResourceManager.loadImage("images/media-playback-start.png"), this::doActivateBundles);
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionContext context) {
|
|
||||||
doActivateBundles();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
addBundlesAction("DeactivateBundles", "Deactivate bundle(s)",
|
||||||
public boolean isEnabledForContext(ActionContext context) {
|
ResourceManager.loadImage("images/media-playback-stop.png"), this::doDeactivateBundles);
|
||||||
return bundleStatusTable.getSelectedRows().length > 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
action.setPopupMenuData(new MenuData(new String[] { "Activate bundle(s)" },
|
|
||||||
ResourceManager.loadImage("images/media-playback-start.png"), BUNDLE_GROUP));
|
|
||||||
action.setToolBarData(new ToolBarData(
|
|
||||||
ResourceManager.loadImage("images/media-playback-start.png"), BUNDLE_GROUP));
|
|
||||||
action.setDescription("Activate bundle(s)");
|
|
||||||
action.setEnabled(false);
|
|
||||||
getTool().addLocalAction(this, action);
|
|
||||||
|
|
||||||
//
|
addBundlesAction("CleanBundles", "Clean bundle(s)",
|
||||||
action = new DockingAction("DeactivateBundles", this.getName()) {
|
ResourceManager.loadImage("images/erase16.png"), this::doClean);
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionContext context) {
|
|
||||||
doDeactivateBundles();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabledForContext(ActionContext context) {
|
|
||||||
return bundleStatusTable.getSelectedRows().length > 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
action.setPopupMenuData(new MenuData(new String[] { "Deactivate bundle(s)" },
|
|
||||||
ResourceManager.loadImage("images/media-playback-stop.png"), BUNDLE_GROUP));
|
|
||||||
action.setToolBarData(new ToolBarData(
|
|
||||||
ResourceManager.loadImage("images/media-playback-stop.png"), BUNDLE_GROUP));
|
|
||||||
action.setDescription("Deactivate bundle(s)");
|
|
||||||
action.setEnabled(false);
|
|
||||||
getTool().addLocalAction(this, action);
|
|
||||||
|
|
||||||
//
|
|
||||||
action = new DockingAction("CleanBundles", this.getName()) {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionContext context) {
|
|
||||||
doClean();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabledForContext(ActionContext context) {
|
|
||||||
return bundleStatusTable.getSelectedRows().length > 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// cache is a lightning bolt
|
|
||||||
action.setPopupMenuData(new MenuData(new String[] { "Clean bundle(s)" },
|
|
||||||
ResourceManager.loadImage("images/erase16.png"), BUNDLE_GROUP));
|
|
||||||
action.setToolBarData(
|
|
||||||
new ToolBarData(ResourceManager.loadImage("images/erase16.png"), BUNDLE_GROUP));
|
|
||||||
action.setDescription("Clean build artifacts for bundle(s)");
|
|
||||||
action.setEnabled(false);
|
|
||||||
getTool().addLocalAction(this, action);
|
|
||||||
|
|
||||||
//
|
//
|
||||||
action = new DockingAction("AddBundles", this.getName()) {
|
action = new DockingAction("AddBundles", this.getName()) {
|
||||||
@@ -285,16 +284,16 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
action.setPopupMenuData(new MenuData(new String[] { "Add bundle(s)" },
|
Icon icon = ResourceManager.loadImage("images/Plus.png");
|
||||||
ResourceManager.loadImage("images/Plus.png"), BUNDLE_LIST_GROUP));
|
action.setPopupMenuData(
|
||||||
action.setToolBarData(
|
new MenuData(new String[] { "Add bundle(s)" }, icon, BUNDLE_LIST_GROUP));
|
||||||
new ToolBarData(ResourceManager.loadImage("images/Plus.png"), BUNDLE_LIST_GROUP));
|
action.setToolBarData(new ToolBarData(icon, BUNDLE_LIST_GROUP));
|
||||||
|
|
||||||
action.setDescription("Display file chooser to add bundles to list");
|
action.setDescription("Display file chooser to add bundles to list");
|
||||||
action.setEnabled(true);
|
action.setEnabled(true);
|
||||||
getTool().addLocalAction(this, action);
|
getTool().addLocalAction(this, action);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
icon = ResourceManager.loadImage("images/edit-delete.png");
|
||||||
action = new DockingAction("RemoveBundles", this.getName()) {
|
action = new DockingAction("RemoveBundles", this.getName()) {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
@@ -307,11 +306,9 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
action.setPopupMenuData(new MenuData(new String[] { "Remove bundle(s)" },
|
action.setPopupMenuData(
|
||||||
ResourceManager.loadImage("images/edit-delete.png"), BUNDLE_LIST_GROUP));
|
new MenuData(new String[] { "Remove bundle(s)" }, icon, BUNDLE_LIST_GROUP));
|
||||||
action.setToolBarData(new ToolBarData(ResourceManager.loadImage("images/edit-delete.png"),
|
action.setToolBarData(new ToolBarData(icon, BUNDLE_LIST_GROUP));
|
||||||
BUNDLE_LIST_GROUP));
|
|
||||||
|
|
||||||
action.setDescription("Remove selected bundle(s) from the list");
|
action.setDescription("Remove selected bundle(s) from the list");
|
||||||
action.setEnabled(true);
|
action.setEnabled(true);
|
||||||
getTool().addLocalAction(this, action);
|
getTool().addLocalAction(this, action);
|
||||||
@@ -341,7 +338,7 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (anythingCleaned) {
|
if (anythingCleaned) {
|
||||||
getModel().fireTableDataChanged();
|
bundleStatusTableModel.fireTableDataChanged();
|
||||||
AnimationUtils.shakeComponent(getComponent());
|
AnimationUtils.shakeComponent(getComponent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -354,9 +351,10 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
doDeactivateBundles();
|
doDeactivateBundles();
|
||||||
|
|
||||||
Map<Boolean, List<GhidraBundle>> bundles =
|
Map<Boolean, List<GhidraBundle>> bundles =
|
||||||
bundleStatusTableModel.getRowObjects(selectedModelRows).stream().map(
|
bundleStatusTableModel.getRowObjects(selectedModelRows)
|
||||||
bs -> bundleHost.getExistingGhidraBundle(bs.getPath())).collect(
|
.stream()
|
||||||
Collectors.partitioningBy(gb -> gb.isSystemBundle()));
|
.map(bs -> bundleHost.getExistingGhidraBundle(bs.getPath()))
|
||||||
|
.collect(Collectors.partitioningBy(gb -> gb.isSystemBundle()));
|
||||||
List<GhidraBundle> systemBundles = bundles.get(true);
|
List<GhidraBundle> systemBundles = bundles.get(true);
|
||||||
if (!systemBundles.isEmpty()) {
|
if (!systemBundles.isEmpty()) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
@@ -388,19 +386,19 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(File f, GhidraFileChooserModel l_model) {
|
public boolean accept(File f, GhidraFileChooserModel model) {
|
||||||
return filter.accept(f, l_model);
|
return filter.accept(f, model);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
String lastSelected = Preferences.getProperty(preferenceForLastSelectedBundle);
|
String lastSelected = Preferences.getProperty(PREFENCE_LAST_SELECTED_BUNDLE);
|
||||||
if (lastSelected != null) {
|
if (lastSelected != null) {
|
||||||
File f = new File(lastSelected);
|
File f = new File(lastSelected);
|
||||||
fileChooser.setSelectedFile(f);
|
fileChooser.setSelectedFile(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String lastSelected = Preferences.getProperty(preferenceForLastSelectedBundle);
|
String lastSelected = Preferences.getProperty(PREFENCE_LAST_SELECTED_BUNDLE);
|
||||||
if (lastSelected != null) {
|
if (lastSelected != null) {
|
||||||
File f = new File(lastSelected);
|
File f = new File(lastSelected);
|
||||||
fileChooser.setSelectedFile(f);
|
fileChooser.setSelectedFile(f);
|
||||||
@@ -410,10 +408,9 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
|
|
||||||
List<File> files = fileChooser.getSelectedFiles();
|
List<File> files = fileChooser.getSelectedFiles();
|
||||||
if (!files.isEmpty()) {
|
if (!files.isEmpty()) {
|
||||||
Preferences.setProperty(preferenceForLastSelectedBundle,
|
Preferences.setProperty(PREFENCE_LAST_SELECTED_BUNDLE, files.get(0).getAbsolutePath());
|
||||||
files.get(0).getAbsolutePath());
|
|
||||||
|
|
||||||
bundleHost.addGhidraBundles(
|
bundleHost.add(
|
||||||
files.stream().map(ResourceFile::new).collect(Collectors.toUnmodifiableList()),
|
files.stream().map(ResourceFile::new).collect(Collectors.toUnmodifiableList()),
|
||||||
true, false);
|
true, false);
|
||||||
}
|
}
|
||||||
@@ -429,8 +426,10 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
bundleStatusTable.chill();
|
bundleStatusTable.chill();
|
||||||
|
|
||||||
List<BundleStatus> statuses =
|
List<BundleStatus> statuses =
|
||||||
bundleStatusTableModel.getRowObjects(selectedModelRows).stream().filter(
|
bundleStatusTableModel.getRowObjects(selectedModelRows)
|
||||||
bs -> !bs.isActive()).collect(Collectors.toUnmodifiableList());
|
.stream()
|
||||||
|
.filter(bs -> !bs.isActive())
|
||||||
|
.collect(Collectors.toUnmodifiableList());
|
||||||
|
|
||||||
List<GhidraBundle> gbs = new ArrayList<>();
|
List<GhidraBundle> gbs = new ArrayList<>();
|
||||||
for (BundleStatus bs : statuses) {
|
for (BundleStatus bs : statuses) {
|
||||||
@@ -469,16 +468,16 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
new TaskLauncher(new Task("deactivating", true, true, false) {
|
new TaskLauncher(new Task("deactivating", true, true, false) {
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) throws CancelledException {
|
public void run(TaskMonitor monitor) throws CancelledException {
|
||||||
List<GhidraBundle> gbs =
|
List<GhidraBundle> gbs = bundleStatusTableModel.getRowObjects(selectedModelRows)
|
||||||
bundleStatusTableModel.getRowObjects(selectedModelRows).stream().filter(
|
.stream()
|
||||||
bs -> bs.isActive()).map(
|
.filter(bs -> bs.isActive())
|
||||||
bs -> bundleHost.getExistingGhidraBundle(bs.getPath())).collect(
|
.map(bs -> bundleHost.getExistingGhidraBundle(bs.getPath()))
|
||||||
Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
monitor.setMaximum(gbs.size());
|
monitor.setMaximum(gbs.size());
|
||||||
for (GhidraBundle gb : gbs) {
|
for (GhidraBundle gb : gbs) {
|
||||||
try {
|
try {
|
||||||
bundleHost.deactivateSynchronously(gb.getBundleLoc());
|
bundleHost.deactivateSynchronously(gb.getBundleLocation());
|
||||||
}
|
}
|
||||||
catch (GhidraBundleException | InterruptedException e) {
|
catch (GhidraBundleException | InterruptedException e) {
|
||||||
e.printStackTrace(console.getStdErr());
|
e.printStackTrace(console.getStdErr());
|
||||||
@@ -501,10 +500,10 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
GhidraBundle gb = bundleHost.getExistingGhidraBundle(status.getPath());
|
GhidraBundle gb = bundleHost.getExistingGhidraBundle(status.getPath());
|
||||||
if (activate) {
|
if (activate) {
|
||||||
gb.build(console.getStdErr());
|
gb.build(console.getStdErr());
|
||||||
bundleHost.activateSynchronously(gb.getBundleLoc());
|
bundleHost.activateSynchronously(gb.getBundleLocation());
|
||||||
}
|
}
|
||||||
else { // deactivate
|
else { // deactivate
|
||||||
bundleHost.deactivateSynchronously(gb.getBundleLoc());
|
bundleHost.deactivateSynchronously(gb.getBundleLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
@@ -518,18 +517,14 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
}, null, 1000);
|
}, null, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BundleStatusTableModel getModel() {
|
private void notifyTableRowChanged(BundleStatus status) {
|
||||||
return bundleStatusTableModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void notifyTableRowChanged(BundleStatus status) {
|
|
||||||
int modelRowIndex = bundleStatusTableModel.getRowIndex(status);
|
int modelRowIndex = bundleStatusTableModel.getRowIndex(status);
|
||||||
int viewRowIndex = filterPanel.getViewRow(modelRowIndex);
|
int viewRowIndex = filterPanel.getViewRow(modelRowIndex);
|
||||||
bundleStatusTable.notifyTableChanged(
|
bundleStatusTable
|
||||||
new TableModelEvent(bundleStatusTableModel, viewRowIndex));
|
.notifyTableChanged(new TableModelEvent(bundleStatusTableModel, viewRowIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyTableDataChanged() {
|
private void notifyTableDataChanged() {
|
||||||
bundleStatusTable.notifyTableChanged(new TableModelEvent(bundleStatusTableModel));
|
bundleStatusTable.notifyTableChanged(new TableModelEvent(bundleStatusTableModel));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,6 +540,9 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cleanup this component
|
||||||
|
*/
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
bundleStatusTable.dispose();
|
bundleStatusTable.dispose();
|
||||||
}
|
}
|
||||||
@@ -553,4 +551,17 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
|||||||
bundleStatusTable.selectRow(filterPanel.getViewRow(modelRowIndex));
|
bundleStatusTable.selectRow(filterPanel.getViewRow(modelRowIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* @param bundlePaths the paths to use
|
||||||
|
*/
|
||||||
|
public void setPathsForTesting(List<ResourceFile> bundlePaths) {
|
||||||
|
bundleStatusTableModel.setModelData(bundlePaths.stream()
|
||||||
|
.map(f -> new BundleStatus(f, true, false, null))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+130
-129
@@ -71,7 +71,7 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
|||||||
Column activeColumn = new Column("Active", Boolean.class) {
|
Column activeColumn = new Column("Active", Boolean.class) {
|
||||||
@Override
|
@Override
|
||||||
boolean editable(BundleStatus status) {
|
boolean editable(BundleStatus status) {
|
||||||
return status.pathExists(); // XXX maybe only if it's already enabled
|
return status.pathExists() && status.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -110,31 +110,80 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column getColumn(int i) {
|
|
||||||
if (i >= 0 && i < columns.size()) {
|
|
||||||
return columns.get(i);
|
|
||||||
}
|
|
||||||
return badColumn;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BundleStatusComponentProvider provider;
|
|
||||||
private List<BundleStatus> statuses;
|
|
||||||
private BundleHost bundleHost;
|
private BundleHost bundleHost;
|
||||||
BundleHostListener bundleListener;
|
private BundleStatusComponentProvider provider;
|
||||||
|
private Map<String, BundleStatus> bundleLocToStatusMap = new HashMap<>();
|
||||||
|
private BundleHostListener bundleHostListener;
|
||||||
|
|
||||||
private Map<String, BundleStatus> loc2status = new HashMap<>();
|
private ArrayList<BundleStatusChangeRequestListener> bundleStatusListeners = new ArrayList<>();
|
||||||
|
private List<BundleStatus> statuses;
|
||||||
|
|
||||||
BundleStatus getStatus(GhidraBundle gb) {
|
protected class MyBundleHostListener implements BundleHostListener {
|
||||||
return getStatusFromLoc(gb.getBundleLoc());
|
@Override
|
||||||
}
|
public void bundleBuilt(GhidraBundle bundle, String summary) {
|
||||||
|
BundleStatus status = getStatus(bundle);
|
||||||
BundleStatus getStatusFromLoc(String bundleLoc) {
|
status.setSummary(summary);
|
||||||
BundleStatus status = loc2status.get(bundleLoc);
|
int row = getRowIndex(status);
|
||||||
if (status == null) {
|
fireTableRowsUpdated(row, row);
|
||||||
Msg.showError(BundleStatusTableModel.this, provider.getComponent(),
|
}
|
||||||
"bundle status error", "bundle has no status!");
|
|
||||||
|
@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);
|
||||||
}
|
}
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BundleStatusTableModel(BundleStatusComponentProvider provider, BundleHost bundleHost) {
|
BundleStatusTableModel(BundleStatusComponentProvider provider, BundleHost bundleHost) {
|
||||||
@@ -142,114 +191,57 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
|||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.bundleHost = bundleHost;
|
this.bundleHost = bundleHost;
|
||||||
statuses = new ArrayList<>();
|
statuses = new ArrayList<>();
|
||||||
for (GhidraBundle gb : bundleHost.getGhidraBundles()) {
|
for (GhidraBundle bundle : bundleHost.getGhidraBundles()) {
|
||||||
addNewStatus(gb);
|
addNewStatus(bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
bundleHost.addListener(bundleListener = new BundleHostListener() {
|
bundleHost.addListener(bundleHostListener = new MyBundleHostListener());
|
||||||
@Override
|
}
|
||||||
public void bundleBuilt(GhidraBundle gb, String summary) {
|
|
||||||
BundleStatus status = getStatus(gb);
|
|
||||||
status.setSummary(summary);
|
|
||||||
int row = getRowIndex(status);
|
|
||||||
fireTableRowsUpdated(row, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
Column getColumn(int i) {
|
||||||
public void bundleActivationChange(GhidraBundle gb, boolean newActivation) {
|
if (i >= 0 && i < columns.size()) {
|
||||||
BundleStatus status = getStatus(gb);
|
return columns.get(i);
|
||||||
int row = getRowIndex(status);
|
}
|
||||||
status.setBusy(false);
|
return badColumn;
|
||||||
if (newActivation) {
|
}
|
||||||
status.setActive(true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
status.setActive(false);
|
|
||||||
}
|
|
||||||
fireTableRowsUpdated(row, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
BundleStatus getStatus(GhidraBundle bundle) {
|
||||||
public void bundleAdded(GhidraBundle gb) {
|
return getStatusFromLoc(bundle.getBundleLocation());
|
||||||
addNewStatus(gb);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
BundleStatus getStatusFromLoc(String bundleLoc) {
|
||||||
public void bundlesAdded(Collection<GhidraBundle> gbundles) {
|
BundleStatus status = bundleLocToStatusMap.get(bundleLoc);
|
||||||
int index = statuses.size();
|
if (status == null) {
|
||||||
for (GhidraBundle gb : gbundles) {
|
Msg.showError(BundleStatusTableModel.this, provider.getComponent(),
|
||||||
addNewStatusNoFire(gb);
|
"bundle status error", "bundle has no status!");
|
||||||
}
|
}
|
||||||
fireTableRowsInserted(index, gbundles.size() - 1);
|
return status;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bundleRemoved(GhidraBundle gbundle) {
|
|
||||||
BundleStatus status = getStatus(gbundle);
|
|
||||||
removeStatus(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bundlesRemoved(Collection<GhidraBundle> gbundles) {
|
|
||||||
List<BundleStatus> toRemove =
|
|
||||||
gbundles.stream().map(BundleStatusTableModel.this::getStatus).collect(
|
|
||||||
Collectors.toUnmodifiableList());
|
|
||||||
removeStatuses(toRemove);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bundleEnablementChange(GhidraBundle gbundle, boolean newEnablement) {
|
|
||||||
BundleStatus status = getStatus(gbundle);
|
|
||||||
status.setEnabled(newEnablement);
|
|
||||||
int row = getRowIndex(status);
|
|
||||||
fireTableRowsUpdated(row, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bundleException(GhidraBundleException gbe) {
|
|
||||||
BundleStatus status = getStatusFromLoc(gbe.getBundleLocation());
|
|
||||||
status.setSummary(gbe.getMessage());
|
|
||||||
int row = getRowIndex(status);
|
|
||||||
fireTableRowsUpdated(row, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
bundleHost.removeListener(bundleListener);
|
bundleHost.removeListener(bundleHostListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ResourceFile> getEnabledPaths() {
|
private void addNewStatusNoFire(GhidraBundle bundle) {
|
||||||
List<ResourceFile> list = new ArrayList<>();
|
BundleStatus status = new BundleStatus(bundle.getPath(), bundle.isEnabled(),
|
||||||
for (BundleStatus status : statuses) {
|
bundle.isSystemBundle(), bundle.getBundleLocation());
|
||||||
if (status.isEnabled()) {
|
|
||||||
list.add(status.getPath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addNewStatusNoFire(GhidraBundle gb) {
|
|
||||||
BundleStatus status =
|
|
||||||
new BundleStatus(gb.getPath(), gb.isEnabled(), gb.isSystemBundle(), gb.getBundleLoc());
|
|
||||||
if (statuses.contains(status)) {
|
if (statuses.contains(status)) {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
"Bundle status manager already contains " + gb.getPath().toString());
|
"Bundle status manager already contains " + bundle.getPath().toString());
|
||||||
}
|
}
|
||||||
status.setActive(gb.isActive());
|
status.setActive(bundle.isActive());
|
||||||
loc2status.put(status.getBundleLoc(), status);
|
bundleLocToStatusMap.put(status.getBundleLocation(), status);
|
||||||
statuses.add(status);
|
statuses.add(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add new status and fire a table update
|
* add new status and fire a table update
|
||||||
*/
|
*/
|
||||||
private void addNewStatus(GhidraBundle gb) {
|
private void addNewStatus(GhidraBundle bundle) {
|
||||||
int index = statuses.size();
|
int index = statuses.size();
|
||||||
addNewStatusNoFire(gb);
|
addNewStatusNoFire(bundle);
|
||||||
fireTableRowsInserted(index, index);
|
fireTableRowsInserted(index, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +249,7 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
|||||||
if (!status.isReadOnly()) {
|
if (!status.isReadOnly()) {
|
||||||
int i = statuses.indexOf(status);
|
int i = statuses.indexOf(status);
|
||||||
statuses.remove(i);
|
statuses.remove(i);
|
||||||
loc2status.remove(status.getBundleLoc());
|
bundleLocToStatusMap.remove(status.getBundleLocation());
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
@@ -271,8 +263,9 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
|||||||
}
|
}
|
||||||
|
|
||||||
void remove(int[] modelRows) {
|
void remove(int[] modelRows) {
|
||||||
List<BundleStatus> toRemove = Arrays.stream(modelRows).mapToObj(statuses::get).collect(
|
List<BundleStatus> toRemove = Arrays.stream(modelRows)
|
||||||
Collectors.toUnmodifiableList());
|
.mapToObj(statuses::get)
|
||||||
|
.collect(Collectors.toUnmodifiableList());
|
||||||
removeStatuses(toRemove);
|
removeStatuses(toRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +308,7 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
|||||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||||
BundleStatus status = statuses.get(rowIndex);
|
BundleStatus status = statuses.get(rowIndex);
|
||||||
getColumn(columnIndex).setValue(status, aValue);
|
getColumn(columnIndex).setValue(status, aValue);
|
||||||
// XXX I don't know why it's unselected, but it's maddening
|
// anything that's clicked on should become selected!
|
||||||
provider.selectModelRow(rowIndex);
|
provider.selectModelRow(rowIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,8 +332,19 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
|||||||
return statuses;
|
return statuses;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<BundleStatusChangeRequestListener> bundleStatusListeners = new ArrayList<>();
|
void setModelData(List<BundleStatus> statuses) {
|
||||||
|
this.statuses = statuses;
|
||||||
|
computeCache();
|
||||||
|
fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a change request listener.
|
||||||
|
*
|
||||||
|
* 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) {
|
public void addListener(BundleStatusChangeRequestListener listener) {
|
||||||
synchronized (bundleStatusListeners) {
|
synchronized (bundleStatusListeners) {
|
||||||
if (!bundleStatusListeners.contains(listener)) {
|
if (!bundleStatusListeners.contains(listener)) {
|
||||||
@@ -349,6 +353,11 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove change request listener.
|
||||||
|
*
|
||||||
|
* @param listener the listener to remove
|
||||||
|
*/
|
||||||
public void removeListener(BundleStatusChangeRequestListener listener) {
|
public void removeListener(BundleStatusChangeRequestListener listener) {
|
||||||
synchronized (bundleStatusListeners) {
|
synchronized (bundleStatusListeners) {
|
||||||
bundleStatusListeners.remove(listener);
|
bundleStatusListeners.remove(listener);
|
||||||
@@ -371,6 +380,12 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the row objects corresponding an array of model row indices.
|
||||||
|
*
|
||||||
|
* @param modelRowIndices row indices
|
||||||
|
* @return status objects
|
||||||
|
*/
|
||||||
public List<BundleStatus> getRowObjects(int[] modelRowIndices) {
|
public List<BundleStatus> getRowObjects(int[] modelRowIndices) {
|
||||||
List<BundleStatus> rows = new ArrayList<>(modelRowIndices.length);
|
List<BundleStatus> rows = new ArrayList<>(modelRowIndices.length);
|
||||||
for (int i : modelRowIndices) {
|
for (int i : modelRowIndices) {
|
||||||
@@ -418,24 +433,10 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
|||||||
* (re)compute cached mapping from bundleloc to bundlepath
|
* (re)compute cached mapping from bundleloc to bundlepath
|
||||||
*/
|
*/
|
||||||
private void computeCache() {
|
private void computeCache() {
|
||||||
loc2status.clear();
|
bundleLocToStatusMap.clear();
|
||||||
for (BundleStatus status : statuses) {
|
for (BundleStatus status : statuses) {
|
||||||
loc2status.put(status.getBundleLoc(), status);
|
bundleLocToStatusMap.put(status.getBundleLocation(), status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*
|
|
||||||
* @param paths the statuses to use
|
|
||||||
*/
|
|
||||||
public void setPathsForTesting(List<ResourceFile> paths) {
|
|
||||||
this.statuses = paths.stream().map(f -> new BundleStatus(f, true, false, null)).collect(
|
|
||||||
Collectors.toList());
|
|
||||||
computeCache();
|
|
||||||
fireTableDataChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ import org.osgi.framework.wiring.BundleRequirement;
|
|||||||
|
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy for an OSGi bundle that may require being built.
|
||||||
|
*/
|
||||||
public abstract class GhidraBundle {
|
public abstract class GhidraBundle {
|
||||||
|
|
||||||
protected final ResourceFile path;
|
protected final ResourceFile path;
|
||||||
@@ -31,9 +34,10 @@ public abstract class GhidraBundle {
|
|||||||
protected boolean enabled;
|
protected boolean enabled;
|
||||||
protected boolean systemBundle;
|
protected boolean systemBundle;
|
||||||
|
|
||||||
GhidraBundle(BundleHost bundleHost, ResourceFile path, boolean enabled, boolean systemBundle) {
|
GhidraBundle(BundleHost bundleHost, ResourceFile bundlePath, boolean enabled,
|
||||||
|
boolean systemBundle) {
|
||||||
this.bundleHost = bundleHost;
|
this.bundleHost = bundleHost;
|
||||||
this.path = path;
|
this.path = bundlePath;
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
this.systemBundle = systemBundle;
|
this.systemBundle = systemBundle;
|
||||||
}
|
}
|
||||||
@@ -48,45 +52,97 @@ public abstract class GhidraBundle {
|
|||||||
/**
|
/**
|
||||||
* build OSGi bundle if possible
|
* build OSGi bundle if possible
|
||||||
*
|
*
|
||||||
* @param writer console for user messages
|
* @param writer console for build messages to user
|
||||||
* @return true if build happened, false if already built
|
* @return true if build happened, false if already built
|
||||||
* @throws Exception sorry, wasn't possible
|
* @throws Exception if the build cannot complete
|
||||||
*/
|
*/
|
||||||
public abstract boolean build(PrintWriter writer) throws Exception;
|
public abstract boolean build(PrintWriter writer) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* same as {@link #build(PrintWriter)} with writer = {@link System.err}.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("javadoc")
|
||||||
public boolean build() throws Exception {
|
public boolean build() throws Exception {
|
||||||
return build(new PrintWriter(System.err));
|
return build(new PrintWriter(System.err));
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract String getBundleLoc();
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @return location identifier of this bundle
|
||||||
|
*/
|
||||||
|
public abstract String getBundleLocation();
|
||||||
|
|
||||||
abstract List<BundleRequirement> getAllReqs();
|
abstract List<BundleRequirement> getAllRequirements();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return this bundle's path
|
||||||
|
*/
|
||||||
public ResourceFile getPath() {
|
public ResourceFile getPath() {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if this bundle is enabled
|
||||||
|
*/
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
/**
|
||||||
|
* set the enablement flag for this bundle.
|
||||||
|
*
|
||||||
|
* If a bundle is enabled its contents will be scanned, e.g. for scripts.
|
||||||
|
*
|
||||||
|
* @param enabled new state
|
||||||
|
*/
|
||||||
|
void setEnabled(boolean enabled) {
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a bundle is a "system bundle" it cannot be removed and its contends cannot be edited.
|
||||||
|
*
|
||||||
|
* @return true if this is a system bundle
|
||||||
|
*/
|
||||||
public boolean isSystemBundle() {
|
public boolean isSystemBundle() {
|
||||||
return systemBundle;
|
return systemBundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
enum Type {
|
||||||
BndScript, Jar, SourceDir, INVALID
|
BndScript, Jar, SourceDir, INVALID
|
||||||
}
|
}
|
||||||
|
|
||||||
static GhidraBundle.Type getType(ResourceFile rf) {
|
/**
|
||||||
if (rf.isDirectory()) {
|
* 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.
|
||||||
|
*
|
||||||
|
* @param path a resource path
|
||||||
|
* @return the type
|
||||||
|
*/
|
||||||
|
static GhidraBundle.Type getType(ResourceFile path) {
|
||||||
|
if (path.isDirectory()) {
|
||||||
return GhidraBundle.Type.SourceDir;
|
return GhidraBundle.Type.SourceDir;
|
||||||
}
|
}
|
||||||
String n = rf.getName().toLowerCase();
|
String n = path.getName().toLowerCase();
|
||||||
if (n.endsWith(".bnd")) {
|
if (n.endsWith(".bnd")) {
|
||||||
return GhidraBundle.Type.BndScript;
|
return GhidraBundle.Type.BndScript;
|
||||||
}
|
}
|
||||||
@@ -96,11 +152,17 @@ public abstract class GhidraBundle {
|
|||||||
return GhidraBundle.Type.INVALID;
|
return GhidraBundle.Type.INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public GhidraBundle.Type getType(File f) {
|
/**
|
||||||
if (f.isDirectory()) {
|
* Get the type of a GhidraBundle from its path.
|
||||||
|
*
|
||||||
|
* @param path a file system path
|
||||||
|
* @return the type
|
||||||
|
*/
|
||||||
|
public static GhidraBundle.Type getType(File path) {
|
||||||
|
if (path.isDirectory()) {
|
||||||
return GhidraBundle.Type.SourceDir;
|
return GhidraBundle.Type.SourceDir;
|
||||||
}
|
}
|
||||||
String n = f.getName().toLowerCase();
|
String n = path.getName().toLowerCase();
|
||||||
if (n.endsWith(".bnd")) {
|
if (n.endsWith(".bnd")) {
|
||||||
return GhidraBundle.Type.BndScript;
|
return GhidraBundle.Type.BndScript;
|
||||||
}
|
}
|
||||||
@@ -110,21 +172,20 @@ public abstract class GhidraBundle {
|
|||||||
return GhidraBundle.Type.INVALID;
|
return GhidraBundle.Type.INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bundle getBundle() {
|
/**
|
||||||
return bundleHost.getBundle(getBundleLoc());
|
* Get the OSGi bundle respresented by this GhidraBundle or null
|
||||||
}
|
*
|
||||||
|
* @return a Bundle or null
|
||||||
public void activate() throws Exception {
|
*/
|
||||||
activate(new PrintWriter(System.err));
|
public Bundle getOSGiBundle() {
|
||||||
}
|
return bundleHost.getOSGiBundle(getBundleLocation());
|
||||||
|
|
||||||
public void activate(PrintWriter writer) throws Exception {
|
|
||||||
build(writer);
|
|
||||||
bundleHost.activateSynchronously(getBundleLoc());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if this bundle is active
|
||||||
|
*/
|
||||||
public boolean isActive() {
|
public boolean isActive() {
|
||||||
Bundle b = getBundle();
|
Bundle b = getOSGiBundle();
|
||||||
return (b != null) && b.getState() == Bundle.ACTIVE;
|
return (b != null) && b.getState() == Bundle.ACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -19,18 +19,18 @@ import org.osgi.framework.BundleActivator;
|
|||||||
import org.osgi.framework.BundleContext;
|
import org.osgi.framework.BundleContext;
|
||||||
|
|
||||||
public abstract class GhidraBundleActivator implements BundleActivator {
|
public abstract class GhidraBundleActivator implements BundleActivator {
|
||||||
protected abstract void start(BundleContext bc, Object api);
|
protected abstract void start(BundleContext bundleContext, Object api);
|
||||||
|
|
||||||
protected abstract void stop(BundleContext bc, Object api);
|
protected abstract void stop(BundleContext bundleContext, Object api);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
final public void start(BundleContext bc) throws Exception {
|
public final void start(BundleContext bundleContext) throws Exception {
|
||||||
start(bc, null);
|
start(bundleContext, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
final public void stop(BundleContext bc) throws Exception {
|
public final void stop(BundleContext bundleContext) throws Exception {
|
||||||
stop(bc, null);
|
stop(bundleContext, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-9
@@ -21,28 +21,52 @@ import org.osgi.framework.Bundle;
|
|||||||
import org.osgi.framework.BundleException;
|
import org.osgi.framework.BundleException;
|
||||||
|
|
||||||
public class GhidraBundleException extends OSGiException {
|
public class GhidraBundleException extends OSGiException {
|
||||||
private Bundle bundle;
|
private final Bundle bundle;
|
||||||
private String bundle_loc;
|
private final String bundleLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link GhidraBundleException}s store the context associated with exceptions thrown during bundle operations.
|
||||||
|
*
|
||||||
|
* @param bundle the bundle (if available)
|
||||||
|
* @param msg a contextual message
|
||||||
|
* @param cause the original exception
|
||||||
|
*/
|
||||||
public GhidraBundleException(Bundle bundle, String msg, BundleException cause) {
|
public GhidraBundleException(Bundle bundle, String msg, BundleException cause) {
|
||||||
super(msg + ": " + parsedCause(cause), cause);
|
super(msg + ": " + parsedCause(cause), cause);
|
||||||
this.bundle = bundle;
|
this.bundle = bundle;
|
||||||
|
this.bundleLocation = bundle.getLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GhidraBundleException(String bundle_loc, String msg, BundleException cause) {
|
/**
|
||||||
|
* {@link GhidraBundleException}s store the context associated with exceptions thrown during bundle operations.
|
||||||
|
*
|
||||||
|
* @param bundleLocation the bundle location identifier (since no bundle is available)
|
||||||
|
* @param msg a contextual message
|
||||||
|
* @param cause the original exception
|
||||||
|
*/
|
||||||
|
public GhidraBundleException(String bundleLocation, String msg, BundleException cause) {
|
||||||
super(msg + ": " + parsedCause(cause), cause);
|
super(msg + ": " + parsedCause(cause), cause);
|
||||||
this.bundle_loc = bundle_loc;
|
this.bundle = null;
|
||||||
|
this.bundleLocation = bundleLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the associated bundle, or null. If null, the bundle location identifier will be non-null
|
||||||
|
*/
|
||||||
public Bundle getBundle() {
|
public Bundle getBundle() {
|
||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When no {@link Bundle} is available, {@link #getBundle()} will return {@code null}.
|
||||||
|
*
|
||||||
|
* @return the bundle location identifier of the offending bundle.
|
||||||
|
*/
|
||||||
public String getBundleLocation() {
|
public String getBundleLocation() {
|
||||||
return bundle_loc != null ? bundle_loc : bundle.getLocation();
|
return bundleLocation != null ? bundleLocation : bundle.getLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
static private String parsedCause(Throwable e) {
|
private static String parsedCause(Throwable e) {
|
||||||
if (e == null) {
|
if (e == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -81,9 +105,10 @@ public class GhidraBundleException extends OSGiException {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
// parse the package constraints from filters in the BundleRequirement string
|
// parse the package constraints from filters in the BundleRequirement string
|
||||||
String packages =
|
String packages = OSGiUtils.extractPackageNamesFromFailedResolution(be.getMessage())
|
||||||
OSGiUtils.extractPackages(be.getMessage()).stream().distinct().collect(
|
.stream()
|
||||||
Collectors.joining("\n"));
|
.distinct()
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
return "RESOLVE_ERROR with reference to packages:\n" + packages;
|
return "RESOLVE_ERROR with reference to packages:\n" + packages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-6
@@ -26,13 +26,24 @@ import aQute.bnd.osgi.Constants;
|
|||||||
import aQute.bnd.osgi.Jar;
|
import aQute.bnd.osgi.Jar;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy to an ordinary OSGi Jar bundle. {@link GhidraJarBundle#build(PrintWriter)} does nothing.
|
||||||
|
*/
|
||||||
public class GhidraJarBundle extends GhidraBundle {
|
public class GhidraJarBundle extends GhidraBundle {
|
||||||
final String bundleLoc;
|
final String bundleLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@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 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 path, boolean enabled,
|
||||||
boolean systemBundle) {
|
boolean systemBundle) {
|
||||||
super(bundleHost, path, enabled, systemBundle);
|
super(bundleHost, path, enabled, systemBundle);
|
||||||
this.bundleLoc = "file://" + path.getAbsolutePath().toString();
|
this.bundleLocation = "file://" + path.getAbsolutePath().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -46,19 +57,19 @@ public class GhidraJarBundle extends GhidraBundle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getBundleLoc() {
|
public String getBundleLocation() {
|
||||||
return bundleLoc;
|
return bundleLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<BundleRequirement> getAllReqs() {
|
public List<BundleRequirement> getAllRequirements() {
|
||||||
Jar jar;
|
Jar jar;
|
||||||
try {
|
try {
|
||||||
jar = new Jar(path.getFile(true));
|
jar = new Jar(path.getFile(true));
|
||||||
Manifest m = jar.getManifest();
|
Manifest m = jar.getManifest();
|
||||||
String imps = m.getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
|
String imps = m.getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
|
||||||
if (imps != null) {
|
if (imps != null) {
|
||||||
return BundleHost.parseImports(imps);
|
return OSGiUtils.parseImports(imps);
|
||||||
}
|
}
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-5
@@ -23,11 +23,14 @@ import org.osgi.framework.wiring.BundleRequirement;
|
|||||||
|
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link GhidraPlaceholderBundle} represents invalid bundle paths in the GUI.
|
||||||
|
*/
|
||||||
public class GhidraPlaceholderBundle extends GhidraBundle {
|
public class GhidraPlaceholderBundle extends GhidraBundle {
|
||||||
|
|
||||||
GhidraPlaceholderBundle(BundleHost bundleHost, ResourceFile path, boolean enabled,
|
GhidraPlaceholderBundle(BundleHost bundleHost, ResourceFile bundlePath, boolean isEnabled,
|
||||||
boolean systemBundle) {
|
boolean isSystemBundle) {
|
||||||
super(bundleHost, path, enabled, systemBundle);
|
super(bundleHost, bundlePath, isEnabled, isSystemBundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -41,12 +44,12 @@ public class GhidraPlaceholderBundle extends GhidraBundle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getBundleLoc() {
|
public String getBundleLocation() {
|
||||||
return "invalid://" + getPath();
|
return "invalid://" + getPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
List<BundleRequirement> getAllReqs() {
|
List<BundleRequirement> getAllRequirements() {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+228
-171
File diff suppressed because it is too large
Load Diff
+9
-7
@@ -23,7 +23,7 @@ import docking.widgets.table.*;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* RowObjectSelectionManager attempts to repair selections in a filtered table
|
* RowObjectSelectionManager attempts to repair selections in a filtered table
|
||||||
* before and after filter events. The additiona selection events, however, cause focus changes we don't want.
|
* before and after filter events. The additional selection events, however, cause focus changes we don't want.
|
||||||
*/
|
*/
|
||||||
class LessFreneticGTable extends GTable {
|
class LessFreneticGTable extends GTable {
|
||||||
boolean chilled = false;
|
boolean chilled = false;
|
||||||
@@ -35,27 +35,29 @@ class LessFreneticGTable extends GTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tableChanged(TableModelEvent e) {
|
public void tableChanged(TableModelEvent event) {
|
||||||
if (!chilled) {
|
if (!chilled) {
|
||||||
super.tableChanged(e);
|
super.tableChanged(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LessFreneticGTable(TableModel model) {
|
||||||
|
super(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** suppress issuing table change events */
|
||||||
public void chill() {
|
public void chill() {
|
||||||
chilled = true;
|
chilled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** resume issuing table change events */
|
||||||
public void thaw() {
|
public void thaw() {
|
||||||
chilled = false;
|
chilled = false;
|
||||||
notifyTableChanged(new TableModelEvent(getModel()));
|
notifyTableChanged(new TableModelEvent(getModel()));
|
||||||
}
|
}
|
||||||
|
|
||||||
LessFreneticGTable(TableModel dm) {
|
|
||||||
super(dm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
protected <T> SelectionManager createSelectionManager(TableModel model) {
|
protected <T> SelectionManager createSelectionManager(TableModel model) {
|
||||||
|
|||||||
@@ -14,15 +14,26 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.osgi;
|
package ghidra.app.plugin.core.osgi;
|
||||||
|
|
||||||
import ghidra.util.exception.UsrException;
|
import ghidra.util.exception.UsrException;
|
||||||
|
|
||||||
|
|
||||||
public class OSGiException extends UsrException {
|
public class OSGiException extends UsrException {
|
||||||
public OSGiException(String msg, Throwable cause) {
|
/**
|
||||||
super(msg, cause);
|
* Wrapper for exceptions originating with an OSGi operation.
|
||||||
|
*
|
||||||
|
* @param message a contextual message
|
||||||
|
* @param cause the original exception
|
||||||
|
*/
|
||||||
|
public OSGiException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OSGiException(String msg) {
|
/**
|
||||||
super(msg);
|
* Wrapper for exceptions originating with an OSGi operation.
|
||||||
|
*
|
||||||
|
* @param message a contextual message
|
||||||
|
*/
|
||||||
|
public OSGiException(String message) {
|
||||||
|
super(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,19 +15,151 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.plugin.core.osgi;
|
package ghidra.app.plugin.core.osgi;
|
||||||
|
|
||||||
import java.util.List;
|
import java.io.File;
|
||||||
import java.util.Scanner;
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.felix.framework.util.manifestparser.ManifestParser;
|
||||||
|
import org.osgi.framework.*;
|
||||||
|
import org.osgi.framework.wiring.BundleRequirement;
|
||||||
|
|
||||||
public class OSGiUtils {
|
public class OSGiUtils {
|
||||||
|
|
||||||
public static List<String> extractPackages(String str) {
|
/**
|
||||||
try (Scanner s = new Scanner(str)) {
|
* The syntax of the error generated when OSGi requirements cannot be resolved is
|
||||||
|
* difficult to parse, so we try to extract package names.
|
||||||
|
*
|
||||||
|
* @param osgiExceptionMessage the exception message
|
||||||
|
* @return a list of package names
|
||||||
|
*/
|
||||||
|
static List<String> extractPackageNamesFromFailedResolution(String osgiExceptionMessage) {
|
||||||
|
try (Scanner s = new Scanner(osgiExceptionMessage)) {
|
||||||
return s.findAll(Pattern.compile("\\(osgi\\.wiring\\.package=([^)]*)\\)")).map(m -> {
|
return s.findAll(Pattern.compile("\\(osgi\\.wiring\\.package=([^)]*)\\)")).map(m -> {
|
||||||
return m.group(1);
|
return m.group(1);
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String getEventTypeString(BundleEvent e) {
|
||||||
|
switch (e.getType()) {
|
||||||
|
case BundleEvent.INSTALLED:
|
||||||
|
return "INSTALLED";
|
||||||
|
case BundleEvent.RESOLVED:
|
||||||
|
return "RESOLVED";
|
||||||
|
case BundleEvent.LAZY_ACTIVATION:
|
||||||
|
return "LAZY_ACTIVATION";
|
||||||
|
case BundleEvent.STARTING:
|
||||||
|
return "STARTING";
|
||||||
|
case BundleEvent.STARTED:
|
||||||
|
return "STARTED";
|
||||||
|
case BundleEvent.STOPPING:
|
||||||
|
return "STOPPING";
|
||||||
|
case BundleEvent.STOPPED:
|
||||||
|
return "STOPPED";
|
||||||
|
case BundleEvent.UPDATED:
|
||||||
|
return "UPDATED";
|
||||||
|
case BundleEvent.UNRESOLVED:
|
||||||
|
return "UNRESOLVED";
|
||||||
|
case BundleEvent.UNINSTALLED:
|
||||||
|
return "UNINSTALLED";
|
||||||
|
default:
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parse Import-Package string from a bundle manifest
|
||||||
|
*
|
||||||
|
* @param imports 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 {
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// from https://dzone.com/articles/locate-jar-classpath-given
|
||||||
|
static String findJarForClass(Class<?> c) {
|
||||||
|
final URL location;
|
||||||
|
final String classLocation = c.getName().replace('.', '/') + ".class";
|
||||||
|
final ClassLoader loader = c.getClassLoader();
|
||||||
|
if (loader == null) {
|
||||||
|
location = ClassLoader.getSystemResource(classLocation);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
location = loader.getResource(classLocation);
|
||||||
|
}
|
||||||
|
if (location != null) {
|
||||||
|
Pattern p = Pattern.compile("^.*:(.*)!.*$");
|
||||||
|
Matcher m = p.matcher(location.toString());
|
||||||
|
if (m.find()) {
|
||||||
|
return m.group(1);
|
||||||
|
}
|
||||||
|
return null; // not loaded from jar?
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void getPackagesFromClasspath(Set<String> s) {
|
||||||
|
getClasspathElements().forEach(p -> {
|
||||||
|
if (Files.isDirectory(p)) {
|
||||||
|
collectPackagesFromDirectory(p, s);
|
||||||
|
}
|
||||||
|
else if (p.toString().endsWith(".jar")) {
|
||||||
|
collectPackagesFromJar(p, s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<Path> getClasspathElements() {
|
||||||
|
String classpathStr = System.getProperty("java.class.path");
|
||||||
|
return Collections.list(new StringTokenizer(classpathStr, File.pathSeparator))
|
||||||
|
.stream()
|
||||||
|
.map(String.class::cast)
|
||||||
|
.map(Paths::get)
|
||||||
|
.map(Path::normalize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void collectPackagesFromDirectory(Path dirPath, Set<String> s) {
|
||||||
|
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, '.')
|
||||||
|
: "");
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void collectPackagesFromJar(Path jarPath, Set<String> s) {
|
||||||
|
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('/', '.') : "");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -578,10 +578,10 @@ class GhidraScriptActionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void launchJavadoc() {
|
private void launchJavadoc() {
|
||||||
URI URI = entryFile.toURI();
|
URI uri = entryFile.toURI();
|
||||||
URL URL = null;
|
URL url = null;
|
||||||
try {
|
try {
|
||||||
URL = URI.toURL();
|
url = uri.toURL();
|
||||||
}
|
}
|
||||||
catch (MalformedURLException e) {
|
catch (MalformedURLException e) {
|
||||||
// shouldn't happen
|
// shouldn't happen
|
||||||
@@ -591,7 +591,7 @@ class GhidraScriptActionManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowserLoader.display(URL, URL, plugin.getTool());
|
BrowserLoader.display(url, url, plugin.getTool());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeZipEntry(File unzipDirectory, ZipEntry entry, InputStream inputStream)
|
private void writeZipEntry(File unzipDirectory, ZipEntry entry, InputStream inputStream)
|
||||||
|
|||||||
+36
-32
@@ -41,7 +41,6 @@ import docking.widgets.tree.GTreeNode;
|
|||||||
import docking.widgets.tree.support.BreadthFirstIterator;
|
import docking.widgets.tree.support.BreadthFirstIterator;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.plugin.core.osgi.*;
|
import ghidra.app.plugin.core.osgi.*;
|
||||||
import ghidra.app.plugin.core.osgi.BundleHost.BuildFailure;
|
|
||||||
import ghidra.app.script.*;
|
import ghidra.app.script.*;
|
||||||
import ghidra.app.services.ConsoleService;
|
import ghidra.app.services.ConsoleService;
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
@@ -66,12 +65,12 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
static final String WINDOW_GROUP = "Script Group";
|
static final String WINDOW_GROUP = "Script Group";
|
||||||
|
|
||||||
private Map<ResourceFile, GhidraScriptEditorComponentProvider> editorMap = new HashMap<>();
|
private Map<ResourceFile, GhidraScriptEditorComponentProvider> editorMap = new HashMap<>();
|
||||||
final private GhidraScriptMgrPlugin plugin;
|
private final GhidraScriptMgrPlugin plugin;
|
||||||
private JPanel component;
|
private JPanel component;
|
||||||
private RootNode scriptRoot;
|
private RootNode scriptRoot;
|
||||||
private GTree scriptCategoryTree;
|
private GTree scriptCategoryTree;
|
||||||
private DraggableScriptTable scriptTable;
|
private DraggableScriptTable scriptTable;
|
||||||
final private GhidraScriptInfoManager infoManager;
|
private final GhidraScriptInfoManager infoManager;
|
||||||
private GhidraScriptTableModel tableModel;
|
private GhidraScriptTableModel tableModel;
|
||||||
private BundleStatusComponentProvider bundleStatusComponentProvider;
|
private BundleStatusComponentProvider bundleStatusComponentProvider;
|
||||||
private TaskListener taskListener = new ScriptTaskListener();
|
private TaskListener taskListener = new ScriptTaskListener();
|
||||||
@@ -99,8 +98,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
final private BundleHost bundleHost;
|
private final BundleHost bundleHost;
|
||||||
final private RefreshingBundleHostListener refreshingBundleHostListener =
|
private final RefreshingBundleHostListener refreshingBundleHostListener =
|
||||||
new RefreshingBundleHostListener();
|
new RefreshingBundleHostListener();
|
||||||
|
|
||||||
GhidraScriptComponentProvider(GhidraScriptMgrPlugin plugin, BundleHost bundleHost) {
|
GhidraScriptComponentProvider(GhidraScriptMgrPlugin plugin, BundleHost bundleHost) {
|
||||||
@@ -158,8 +157,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
scriptCategoryTree.getSelectionModel().setSelectionMode(
|
scriptCategoryTree.getSelectionModel()
|
||||||
TreeSelectionModel.SINGLE_TREE_SELECTION);
|
.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
||||||
|
|
||||||
tableModel = new GhidraScriptTableModel(this, infoManager);
|
tableModel = new GhidraScriptTableModel(this, infoManager);
|
||||||
|
|
||||||
@@ -237,7 +236,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
public void readConfigState(SaveState saveState) {
|
public void readConfigState(SaveState saveState) {
|
||||||
bundleHost.restoreStateAndActivate(saveState, getTool());
|
bundleHost.restoreManagedBundleState(saveState, getTool());
|
||||||
|
|
||||||
actionManager.restoreUserDefinedKeybindings(saveState);
|
actionManager.restoreUserDefinedKeybindings(saveState);
|
||||||
actionManager.restoreScriptsThatAreInTool(saveState);
|
actionManager.restoreScriptsThatAreInTool(saveState);
|
||||||
@@ -261,7 +260,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeConfigState(SaveState saveState) {
|
public void writeConfigState(SaveState saveState) {
|
||||||
bundleHost.saveState(saveState);
|
bundleHost.saveManagedBundleState(saveState);
|
||||||
|
|
||||||
actionManager.saveUserDefinedKeybindings(saveState);
|
actionManager.saveUserDefinedKeybindings(saveState);
|
||||||
actionManager.saveScriptsThatAreInTool(saveState);
|
actionManager.saveScriptsThatAreInTool(saveState);
|
||||||
@@ -408,8 +407,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
action.setKeyBindingData(new KeyBindingData(ks));
|
action.setKeyBindingData(new KeyBindingData(ks));
|
||||||
}
|
}
|
||||||
|
|
||||||
assert !infoManager.containsMetadata(
|
assert !infoManager
|
||||||
renameFile) : "renamed script already has metadata";
|
.containsMetadata(renameFile) : "renamed script already has metadata";
|
||||||
infoManager.getScriptInfo(renameFile);
|
infoManager.getScriptInfo(renameFile);
|
||||||
|
|
||||||
tableModel.switchScript(script, renameFile);
|
tableModel.switchScript(script, renameFile);
|
||||||
@@ -452,15 +451,20 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<ResourceFile> getScriptDirectories() {
|
public List<ResourceFile> getScriptDirectories() {
|
||||||
return bundleHost.getGhidraBundles().stream().filter(
|
return bundleHost.getGhidraBundles()
|
||||||
gb -> gb.isEnabled() && gb instanceof GhidraSourceBundle).map(
|
.stream()
|
||||||
GhidraBundle::getPath).collect(Collectors.toList());
|
.filter(bundle -> bundle.isEnabled() && bundle instanceof GhidraSourceBundle)
|
||||||
|
.map(GhidraBundle::getPath)
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ResourceFile> getWritableScriptDirectories() {
|
public List<ResourceFile> getWritableScriptDirectories() {
|
||||||
return bundleHost.getGhidraBundles().stream().filter(
|
return bundleHost.getGhidraBundles()
|
||||||
GhidraSourceBundle.class::isInstance).filter(gb -> !gb.isSystemBundle()).map(
|
.stream()
|
||||||
GhidraBundle::getPath).collect(Collectors.toList());
|
.filter(GhidraSourceBundle.class::isInstance)
|
||||||
|
.filter(bundle -> !bundle.isSystemBundle())
|
||||||
|
.map(GhidraBundle::getPath)
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isEditorOpen(ResourceFile script) {
|
boolean isEditorOpen(ResourceFile script) {
|
||||||
@@ -698,14 +702,14 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
class RefreshingBundleHostListener implements BundleHostListener {
|
class RefreshingBundleHostListener implements BundleHostListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bundleBuilt(GhidraBundle sb, String summary) {
|
public void bundleBuilt(GhidraBundle bundle, String summary) {
|
||||||
if (sb instanceof GhidraSourceBundle) {
|
if (bundle instanceof GhidraSourceBundle) {
|
||||||
GhidraSourceBundle gsb = (GhidraSourceBundle) sb;
|
GhidraSourceBundle sourceBundle = (GhidraSourceBundle) bundle;
|
||||||
for (ResourceFile sf : gsb.getNewSources()) {
|
for (ResourceFile sourceFile : sourceBundle.getNewSources()) {
|
||||||
if (infoManager.containsMetadata(sf)) {
|
if (infoManager.containsMetadata(sourceFile)) {
|
||||||
ScriptInfo info = infoManager.getExistingScriptInfo(sf);
|
ScriptInfo scriptInfo = infoManager.getExistingScriptInfo(sourceFile);
|
||||||
BuildFailure e = gsb.getErrors(sf);
|
GhidraBundle.BuildFailure e = sourceBundle.getErrors(sourceFile);
|
||||||
info.setCompileErrors(e != null);
|
scriptInfo.setCompileErrors(e != null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tableModel.fireTableDataChanged();
|
tableModel.fireTableDataChanged();
|
||||||
@@ -713,32 +717,32 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bundleEnablementChange(GhidraBundle gbundle, boolean newEnablment) {
|
public void bundleEnablementChange(GhidraBundle bundle, boolean newEnablment) {
|
||||||
if (gbundle instanceof GhidraSourceBundle) {
|
if (bundle instanceof GhidraSourceBundle) {
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bundleAdded(GhidraBundle gbundle) {
|
public void bundleAdded(GhidraBundle bundle) {
|
||||||
plugin.getTool().setConfigChanged(true);
|
plugin.getTool().setConfigChanged(true);
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bundlesAdded(Collection<GhidraBundle> gbundles) {
|
public void bundlesAdded(Collection<GhidraBundle> bundles) {
|
||||||
plugin.getTool().setConfigChanged(true);
|
plugin.getTool().setConfigChanged(true);
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bundleRemoved(GhidraBundle gbundle) {
|
public void bundleRemoved(GhidraBundle bundle) {
|
||||||
plugin.getTool().setConfigChanged(true);
|
plugin.getTool().setConfigChanged(true);
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bundlesRemoved(Collection<GhidraBundle> gbundles) {
|
public void bundlesRemoved(Collection<GhidraBundle> bundles) {
|
||||||
plugin.getTool().setConfigChanged(true);
|
plugin.getTool().setConfigChanged(true);
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
@@ -1030,10 +1034,10 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private JComponent buildDescriptionComponent() {
|
private JComponent buildDescriptionComponent() {
|
||||||
JPanel descriptionPanel = new JPanel(new BorderLayout());
|
|
||||||
descriptionTextPane = new JTextPane();
|
descriptionTextPane = new JTextPane();
|
||||||
descriptionTextPane.setEditable(false);
|
descriptionTextPane.setEditable(false);
|
||||||
descriptionTextPane.setEditorKit(new HTMLEditorKit());
|
descriptionTextPane.setEditorKit(new HTMLEditorKit());
|
||||||
|
JPanel descriptionPanel = new JPanel(new BorderLayout());
|
||||||
descriptionPanel.add(descriptionTextPane);
|
descriptionPanel.add(descriptionTextPane);
|
||||||
JScrollPane scrollPane = new JScrollPane(descriptionPanel);
|
JScrollPane scrollPane = new JScrollPane(descriptionPanel);
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -50,10 +50,10 @@ import ghidra.util.task.TaskListener;
|
|||||||
//@formatter:on
|
//@formatter:on
|
||||||
public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScriptService {
|
public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScriptService {
|
||||||
|
|
||||||
final private GhidraScriptComponentProvider provider;
|
private final GhidraScriptComponentProvider provider;
|
||||||
|
|
||||||
static private int loaded = 0;
|
private static int loaded = 0;
|
||||||
final private BundleHost bundleHost;
|
private final BundleHost bundleHost;
|
||||||
|
|
||||||
public GhidraScriptMgrPlugin(PluginTool tool) {
|
public GhidraScriptMgrPlugin(PluginTool tool) {
|
||||||
super(tool, true, true, true);
|
super(tool, true, true, true);
|
||||||
|
|||||||
+3
-3
@@ -39,12 +39,12 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
|
|||||||
private static final String EMPTY_STRING = "";
|
private static final String EMPTY_STRING = "";
|
||||||
private static final ImageIcon ERROR_IMG = Icons.ERROR_ICON;
|
private static final ImageIcon ERROR_IMG = Icons.ERROR_ICON;
|
||||||
|
|
||||||
final static String SCRIPT_ACTION_COLUMN_NAME = "In Tool";
|
static final String SCRIPT_ACTION_COLUMN_NAME = "In Tool";
|
||||||
final static String SCRIPT_STATUS_COLUMN_NAME = "Status";
|
static final String SCRIPT_STATUS_COLUMN_NAME = "Status";
|
||||||
|
|
||||||
private GhidraScriptComponentProvider provider;
|
private GhidraScriptComponentProvider provider;
|
||||||
private List<ResourceFile> scriptList = new ArrayList<>();
|
private List<ResourceFile> scriptList = new ArrayList<>();
|
||||||
final private GhidraScriptInfoManager infoManager;
|
private final GhidraScriptInfoManager infoManager;
|
||||||
|
|
||||||
GhidraScriptTableModel(GhidraScriptComponentProvider provider,
|
GhidraScriptTableModel(GhidraScriptComponentProvider provider,
|
||||||
GhidraScriptInfoManager infoManager) {
|
GhidraScriptInfoManager infoManager) {
|
||||||
|
|||||||
@@ -74,12 +74,12 @@ public class GhidraScriptUtil {
|
|||||||
setBundleHost(bundleHost);
|
setBundleHost(bundleHost);
|
||||||
if (extraSystemPaths != null) {
|
if (extraSystemPaths != null) {
|
||||||
for (String path : extraSystemPaths) {
|
for (String path : extraSystemPaths) {
|
||||||
bundleHost.addGhidraBundle(new ResourceFile(path), true, true);
|
bundleHost.add(new ResourceFile(path), true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bundleHost.addGhidraBundle(GhidraScriptUtil.getUserScriptDirectory(), true, false);
|
bundleHost.add(GhidraScriptUtil.getUserScriptDirectory(), true, false);
|
||||||
bundleHost.addGhidraBundles(GhidraScriptUtil.getSystemScriptPaths(), true, true);
|
bundleHost.add(GhidraScriptUtil.getSystemScriptPaths(), true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void dispose() {
|
public static void dispose() {
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||||||
@Override
|
@Override
|
||||||
public boolean deleteScript(ResourceFile sourceFile) {
|
public boolean deleteScript(ResourceFile sourceFile) {
|
||||||
try {
|
try {
|
||||||
Bundle b = getBundleForSource(sourceFile).getBundle();
|
Bundle osgiBundle = getBundleForSource(sourceFile).getOSGiBundle();
|
||||||
if (b != null) {
|
if (osgiBundle != null) {
|
||||||
_bundleHost.deactivateSynchronously(b);
|
_bundleHost.deactivateSynchronously(osgiBundle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (GhidraBundleException | InterruptedException e) {
|
catch (GhidraBundleException | InterruptedException e) {
|
||||||
@@ -95,7 +95,8 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
|||||||
public Class<?> loadClass(ResourceFile sourceFile, PrintWriter writer) throws Exception {
|
public Class<?> loadClass(ResourceFile sourceFile, PrintWriter writer) throws Exception {
|
||||||
GhidraSourceBundle gb = getBundleForSource(sourceFile);
|
GhidraSourceBundle gb = getBundleForSource(sourceFile);
|
||||||
gb.build(writer);
|
gb.build(writer);
|
||||||
Bundle b = _bundleHost.installFromLoc(gb.getBundleLoc());
|
|
||||||
|
Bundle b = _bundleHost.install(gb);
|
||||||
|
|
||||||
_bundleHost.activateSynchronously(b);
|
_bundleHost.activateSynchronously(b);
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -26,6 +26,7 @@ import java.util.concurrent.CountDownLatch;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.table.TableModel;
|
import javax.swing.table.TableModel;
|
||||||
@@ -44,11 +45,9 @@ import docking.widgets.table.RowObjectTableModel;
|
|||||||
import docking.widgets.tree.GTree;
|
import docking.widgets.tree.GTree;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import generic.test.TestUtils;
|
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.app.plugin.core.console.ConsoleComponentProvider;
|
import ghidra.app.plugin.core.console.ConsoleComponentProvider;
|
||||||
import ghidra.app.plugin.core.osgi.BundleHost;
|
import ghidra.app.plugin.core.osgi.GhidraSourceBundle;
|
||||||
import ghidra.app.plugin.core.osgi.BundleStatusComponentProvider;
|
|
||||||
import ghidra.app.script.*;
|
import ghidra.app.script.*;
|
||||||
import ghidra.app.services.ConsoleService;
|
import ghidra.app.services.ConsoleService;
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
@@ -181,8 +180,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||||||
|
|
||||||
static protected void wipe(Path path) throws IOException {
|
static protected void wipe(Path path) throws IOException {
|
||||||
if (Files.exists(path)) {
|
if (Files.exists(path)) {
|
||||||
for (Path p : (Iterable<Path>) Files.walk(path).sorted(
|
for (Path p : (Iterable<Path>) Files.walk(path)
|
||||||
Comparator.reverseOrder())::iterator) {
|
.sorted(Comparator.reverseOrder())::iterator) {
|
||||||
Files.deleteIfExists(p);
|
Files.deleteIfExists(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -687,8 +686,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||||||
"Contents of file on disk do not match that of the editor after performing " +
|
"Contents of file on disk do not match that of the editor after performing " +
|
||||||
"a save operation: " + file);
|
"a save operation: " + file);
|
||||||
printChars(expectedContents, fileText);
|
printChars(expectedContents, fileText);
|
||||||
Assert.fail(
|
Assert
|
||||||
"Contents of file on disk do not match that of the editor after performing " +
|
.fail("Contents of file on disk do not match that of the editor after performing " +
|
||||||
"a save operation: " + file);
|
"a save operation: " + file);
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
@@ -980,15 +979,16 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||||||
|
|
||||||
protected void cleanupOldTestFiles() throws IOException {
|
protected void cleanupOldTestFiles() throws IOException {
|
||||||
// remove the compiled bundles directory so that any scripts we use will be recompiled
|
// remove the compiled bundles directory so that any scripts we use will be recompiled
|
||||||
wipe(BundleHost.getCompiledBundlesDir());
|
wipe(GhidraSourceBundle.getCompiledBundlesDir());
|
||||||
|
|
||||||
String myTestName = super.testName.getMethodName();
|
String myTestName = super.testName.getMethodName();
|
||||||
|
|
||||||
// destroy any NewScriptxxx files...and Temp ones too
|
// destroy any NewScriptxxx files...and Temp ones too
|
||||||
BundleStatusComponentProvider bundleStatusComponentProvider =
|
List<ResourceFile> paths = provider.getBundleHost()
|
||||||
(BundleStatusComponentProvider) TestUtils.getInstanceField(
|
.getBundlePaths()
|
||||||
"bundleStatusComponentProvider", provider);
|
.stream()
|
||||||
List<ResourceFile> paths = bundleStatusComponentProvider.getModel().getEnabledPaths();
|
.filter(ResourceFile::isDirectory)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
for (ResourceFile path : paths) {
|
for (ResourceFile path : paths) {
|
||||||
File file = path.getFile(false);
|
File file = path.getFile(false);
|
||||||
|
|||||||
+5
-5
@@ -55,7 +55,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||||||
tmpdirs.add(tmpDir);
|
tmpdirs.add(tmpDir);
|
||||||
|
|
||||||
ResourceFile rp = new ResourceFile(tmpDir.toFile());
|
ResourceFile rp = new ResourceFile(tmpDir.toFile());
|
||||||
current_gb = bundleHost.addGhidraBundle(rp, true, false);
|
current_gb = bundleHost.add(rp, true, false);
|
||||||
gbstack.push(current_gb);
|
gbstack.push(current_gb);
|
||||||
return current_gb;
|
return current_gb;
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws OSGiException, IOException {
|
public void setup() throws OSGiException, IOException {
|
||||||
wipe(BundleHost.getCompiledBundlesDir());
|
wipe(GhidraSourceBundle.getCompiledBundlesDir());
|
||||||
|
|
||||||
bundleHost = new BundleHost();
|
bundleHost = new BundleHost();
|
||||||
bundleHost.startFramework();
|
bundleHost.startFramework();
|
||||||
@@ -106,7 +106,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void activate() throws Exception {
|
protected void activate() throws Exception {
|
||||||
Bundle bundle = bundleHost.installFromLoc(current_gb.getBundleLoc());
|
Bundle bundle = bundleHost.install(current_gb);
|
||||||
assertNotNull("failed to install bundle", bundle);
|
assertNotNull("failed to install bundle", bundle);
|
||||||
bundle.start();
|
bundle.start();
|
||||||
}
|
}
|
||||||
@@ -117,7 +117,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Class<?> loadClass(String classname) throws ClassNotFoundException {
|
protected Class<?> loadClass(String classname) throws ClassNotFoundException {
|
||||||
Class<?> clazz = current_gb.getBundle().loadClass(classname);
|
Class<?> clazz = current_gb.getOSGiBundle().loadClass(classname);
|
||||||
assertNotNull("failed to load class", clazz);
|
assertNotNull("failed to load class", clazz);
|
||||||
return clazz;
|
return clazz;
|
||||||
}
|
}
|
||||||
@@ -313,7 +313,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||||||
|
|
||||||
|
|
||||||
pushNewBundle();
|
pushNewBundle();
|
||||||
// @imports tag is only parsed from classes in default package
|
// @importpackages tag is only parsed from classes in default package
|
||||||
addClass(
|
addClass(
|
||||||
"//@importpackage lib\n"
|
"//@importpackage lib\n"
|
||||||
,
|
,
|
||||||
|
|||||||
Reference in New Issue
Block a user