mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-26 18:45:50 +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 {
|
||||
|
||||
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) {
|
||||
bundleAdded(gbundle);
|
||||
/**
|
||||
* Invoked when a number of bundles is added at once. A listener should implement this method
|
||||
* 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) {
|
||||
bundleRemoved(gbundle);
|
||||
/**
|
||||
* Invoked when a number of bundles is removed at once. A listener should implement this method
|
||||
* 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.
|
||||
*
|
||||
* 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> {
|
||||
final Path path;
|
||||
final GhidraBundle.Type type;
|
||||
final String bundleLoc;
|
||||
final String bundleLocation;
|
||||
|
||||
boolean active = 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;
|
||||
|
||||
public GhidraBundle.Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
BundleStatus(ResourceFile path, boolean enabled, boolean readonly, String bundleLoc) {
|
||||
this.path = new Path(path, enabled, false, readonly);
|
||||
type = GhidraBundle.getType(getPath());
|
||||
this.bundleLoc = 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();
|
||||
this.bundleLocation = bundleLoc;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,12 +42,103 @@ public class BundleStatus implements Comparable<BundleStatus> {
|
||||
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() {
|
||||
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 {
|
||||
|
||||
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) {
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
+124
-113
@@ -49,7 +49,10 @@ import resources.ResourceManager;
|
||||
* component for managing OSGi bundle status
|
||||
*/
|
||||
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 LessFreneticGTable bundleStatusTable;
|
||||
@@ -60,9 +63,14 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
private GhidraFileFilter filter;
|
||||
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) {
|
||||
super(tool, "BundleManager", owner);
|
||||
setHelpLocation(new HelpLocation("BundleManager", "BundleManager"));
|
||||
@@ -136,12 +144,24 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
// to allow custom cell renderers
|
||||
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;
|
||||
|
||||
int skinnyWidth = 60;
|
||||
//
|
||||
column = bundleStatusTable.getColumnModel().getColumn(
|
||||
bundleStatusTableModel.enabledColumn.index);
|
||||
column = bundleStatusTable.getColumnModel()
|
||||
.getColumn(bundleStatusTableModel.enabledColumn.index);
|
||||
column.setPreferredWidth(skinnyWidth);
|
||||
column.setMinWidth(skinnyWidth);
|
||||
column.setMaxWidth(skinnyWidth);
|
||||
@@ -171,9 +191,7 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
setText("");
|
||||
}
|
||||
return x;
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
//
|
||||
@@ -197,82 +215,63 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
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);
|
||||
scrollPane.getViewport().setBackground(bundleStatusTable.getBackground());
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return bundleStatusTable.getSelectedRows().length > 0;
|
||||
}
|
||||
};
|
||||
action.setPopupMenuData(new MenuData(new String[] { description }, icon, BUNDLE_GROUP));
|
||||
action.setToolBarData(new ToolBarData(icon, BUNDLE_GROUP));
|
||||
action.setDescription(description);
|
||||
action.setEnabled(false);
|
||||
getTool().addLocalAction(this, action);
|
||||
|
||||
}
|
||||
|
||||
private void addBundleListAction(String actionName, String name, String description, Icon icon,
|
||||
Runnable runnable) {
|
||||
DockingAction action = new DockingAction(actionName, this.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
action.setPopupMenuData(new MenuData(new String[] { name }, icon, BUNDLE_LIST_GROUP));
|
||||
action.setToolBarData(new ToolBarData(icon, BUNDLE_LIST_GROUP));
|
||||
action.setDescription(description);
|
||||
action.setEnabled(true);
|
||||
getTool().addLocalAction(this, action);
|
||||
|
||||
panel.add(filterPanel, BorderLayout.SOUTH);
|
||||
panel.add(scrollPane, BorderLayout.CENTER);
|
||||
panel.setPreferredSize(new Dimension(800, 400));
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
DockingAction action;
|
||||
|
||||
//
|
||||
action = new DockingAction("ActivateBundles", this.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
doActivateBundles();
|
||||
}
|
||||
addBundlesAction("ActivateBundles", "Activate bundle(s)",
|
||||
ResourceManager.loadImage("images/media-playback-start.png"), this::doActivateBundles);
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
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("DeactivateBundles", "Deactivate bundle(s)",
|
||||
ResourceManager.loadImage("images/media-playback-stop.png"), this::doDeactivateBundles);
|
||||
|
||||
addBundlesAction("CleanBundles", "Clean bundle(s)",
|
||||
ResourceManager.loadImage("images/erase16.png"), this::doClean);
|
||||
|
||||
//
|
||||
action = new DockingAction("DeactivateBundles", this.getName()) {
|
||||
@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()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
@@ -285,16 +284,16 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
};
|
||||
action.setPopupMenuData(new MenuData(new String[] { "Add bundle(s)" },
|
||||
ResourceManager.loadImage("images/Plus.png"), BUNDLE_LIST_GROUP));
|
||||
action.setToolBarData(
|
||||
new ToolBarData(ResourceManager.loadImage("images/Plus.png"), BUNDLE_LIST_GROUP));
|
||||
|
||||
Icon icon = ResourceManager.loadImage("images/Plus.png");
|
||||
action.setPopupMenuData(
|
||||
new MenuData(new String[] { "Add bundle(s)" }, icon, BUNDLE_LIST_GROUP));
|
||||
action.setToolBarData(new ToolBarData(icon, BUNDLE_LIST_GROUP));
|
||||
action.setDescription("Display file chooser to add bundles to list");
|
||||
action.setEnabled(true);
|
||||
getTool().addLocalAction(this, action);
|
||||
|
||||
//
|
||||
//
|
||||
icon = ResourceManager.loadImage("images/edit-delete.png");
|
||||
action = new DockingAction("RemoveBundles", this.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
@@ -307,11 +306,9 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
};
|
||||
action.setPopupMenuData(new MenuData(new String[] { "Remove bundle(s)" },
|
||||
ResourceManager.loadImage("images/edit-delete.png"), BUNDLE_LIST_GROUP));
|
||||
action.setToolBarData(new ToolBarData(ResourceManager.loadImage("images/edit-delete.png"),
|
||||
BUNDLE_LIST_GROUP));
|
||||
|
||||
action.setPopupMenuData(
|
||||
new MenuData(new String[] { "Remove bundle(s)" }, icon, BUNDLE_LIST_GROUP));
|
||||
action.setToolBarData(new ToolBarData(icon, BUNDLE_LIST_GROUP));
|
||||
action.setDescription("Remove selected bundle(s) from the list");
|
||||
action.setEnabled(true);
|
||||
getTool().addLocalAction(this, action);
|
||||
@@ -341,7 +338,7 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
}
|
||||
if (anythingCleaned) {
|
||||
getModel().fireTableDataChanged();
|
||||
bundleStatusTableModel.fireTableDataChanged();
|
||||
AnimationUtils.shakeComponent(getComponent());
|
||||
}
|
||||
}
|
||||
@@ -354,9 +351,10 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
doDeactivateBundles();
|
||||
|
||||
Map<Boolean, List<GhidraBundle>> bundles =
|
||||
bundleStatusTableModel.getRowObjects(selectedModelRows).stream().map(
|
||||
bs -> bundleHost.getExistingGhidraBundle(bs.getPath())).collect(
|
||||
Collectors.partitioningBy(gb -> gb.isSystemBundle()));
|
||||
bundleStatusTableModel.getRowObjects(selectedModelRows)
|
||||
.stream()
|
||||
.map(bs -> bundleHost.getExistingGhidraBundle(bs.getPath()))
|
||||
.collect(Collectors.partitioningBy(gb -> gb.isSystemBundle()));
|
||||
List<GhidraBundle> systemBundles = bundles.get(true);
|
||||
if (!systemBundles.isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -388,19 +386,19 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(File f, GhidraFileChooserModel l_model) {
|
||||
return filter.accept(f, l_model);
|
||||
public boolean accept(File f, GhidraFileChooserModel model) {
|
||||
return filter.accept(f, model);
|
||||
}
|
||||
});
|
||||
}
|
||||
String lastSelected = Preferences.getProperty(preferenceForLastSelectedBundle);
|
||||
String lastSelected = Preferences.getProperty(PREFENCE_LAST_SELECTED_BUNDLE);
|
||||
if (lastSelected != null) {
|
||||
File f = new File(lastSelected);
|
||||
fileChooser.setSelectedFile(f);
|
||||
}
|
||||
}
|
||||
else {
|
||||
String lastSelected = Preferences.getProperty(preferenceForLastSelectedBundle);
|
||||
String lastSelected = Preferences.getProperty(PREFENCE_LAST_SELECTED_BUNDLE);
|
||||
if (lastSelected != null) {
|
||||
File f = new File(lastSelected);
|
||||
fileChooser.setSelectedFile(f);
|
||||
@@ -410,10 +408,9 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
|
||||
List<File> files = fileChooser.getSelectedFiles();
|
||||
if (!files.isEmpty()) {
|
||||
Preferences.setProperty(preferenceForLastSelectedBundle,
|
||||
files.get(0).getAbsolutePath());
|
||||
Preferences.setProperty(PREFENCE_LAST_SELECTED_BUNDLE, files.get(0).getAbsolutePath());
|
||||
|
||||
bundleHost.addGhidraBundles(
|
||||
bundleHost.add(
|
||||
files.stream().map(ResourceFile::new).collect(Collectors.toUnmodifiableList()),
|
||||
true, false);
|
||||
}
|
||||
@@ -429,8 +426,10 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
bundleStatusTable.chill();
|
||||
|
||||
List<BundleStatus> statuses =
|
||||
bundleStatusTableModel.getRowObjects(selectedModelRows).stream().filter(
|
||||
bs -> !bs.isActive()).collect(Collectors.toUnmodifiableList());
|
||||
bundleStatusTableModel.getRowObjects(selectedModelRows)
|
||||
.stream()
|
||||
.filter(bs -> !bs.isActive())
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
|
||||
List<GhidraBundle> gbs = new ArrayList<>();
|
||||
for (BundleStatus bs : statuses) {
|
||||
@@ -469,16 +468,16 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
new TaskLauncher(new Task("deactivating", true, true, false) {
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
List<GhidraBundle> gbs =
|
||||
bundleStatusTableModel.getRowObjects(selectedModelRows).stream().filter(
|
||||
bs -> bs.isActive()).map(
|
||||
bs -> bundleHost.getExistingGhidraBundle(bs.getPath())).collect(
|
||||
Collectors.toList());
|
||||
List<GhidraBundle> gbs = bundleStatusTableModel.getRowObjects(selectedModelRows)
|
||||
.stream()
|
||||
.filter(bs -> bs.isActive())
|
||||
.map(bs -> bundleHost.getExistingGhidraBundle(bs.getPath()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
monitor.setMaximum(gbs.size());
|
||||
for (GhidraBundle gb : gbs) {
|
||||
try {
|
||||
bundleHost.deactivateSynchronously(gb.getBundleLoc());
|
||||
bundleHost.deactivateSynchronously(gb.getBundleLocation());
|
||||
}
|
||||
catch (GhidraBundleException | InterruptedException e) {
|
||||
e.printStackTrace(console.getStdErr());
|
||||
@@ -501,10 +500,10 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
GhidraBundle gb = bundleHost.getExistingGhidraBundle(status.getPath());
|
||||
if (activate) {
|
||||
gb.build(console.getStdErr());
|
||||
bundleHost.activateSynchronously(gb.getBundleLoc());
|
||||
bundleHost.activateSynchronously(gb.getBundleLocation());
|
||||
}
|
||||
else { // deactivate
|
||||
bundleHost.deactivateSynchronously(gb.getBundleLoc());
|
||||
bundleHost.deactivateSynchronously(gb.getBundleLocation());
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
@@ -518,18 +517,14 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
}, null, 1000);
|
||||
}
|
||||
|
||||
public BundleStatusTableModel getModel() {
|
||||
return bundleStatusTableModel;
|
||||
}
|
||||
|
||||
public void notifyTableRowChanged(BundleStatus status) {
|
||||
private void notifyTableRowChanged(BundleStatus status) {
|
||||
int modelRowIndex = bundleStatusTableModel.getRowIndex(status);
|
||||
int viewRowIndex = filterPanel.getViewRow(modelRowIndex);
|
||||
bundleStatusTable.notifyTableChanged(
|
||||
new TableModelEvent(bundleStatusTableModel, viewRowIndex));
|
||||
bundleStatusTable
|
||||
.notifyTableChanged(new TableModelEvent(bundleStatusTableModel, viewRowIndex));
|
||||
}
|
||||
|
||||
public void notifyTableDataChanged() {
|
||||
private void notifyTableDataChanged() {
|
||||
bundleStatusTable.notifyTableChanged(new TableModelEvent(bundleStatusTableModel));
|
||||
}
|
||||
|
||||
@@ -545,6 +540,9 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* cleanup this component
|
||||
*/
|
||||
public void dispose() {
|
||||
bundleStatusTable.dispose();
|
||||
}
|
||||
@@ -553,4 +551,17 @@ public class BundleStatusComponentProvider extends ComponentProviderAdapter {
|
||||
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) {
|
||||
@Override
|
||||
boolean editable(BundleStatus status) {
|
||||
return status.pathExists(); // XXX maybe only if it's already enabled
|
||||
return status.pathExists() && status.isEnabled();
|
||||
}
|
||||
|
||||
@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;
|
||||
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) {
|
||||
return getStatusFromLoc(gb.getBundleLoc());
|
||||
}
|
||||
|
||||
BundleStatus getStatusFromLoc(String bundleLoc) {
|
||||
BundleStatus status = loc2status.get(bundleLoc);
|
||||
if (status == null) {
|
||||
Msg.showError(BundleStatusTableModel.this, provider.getComponent(),
|
||||
"bundle status error", "bundle has no status!");
|
||||
protected class MyBundleHostListener implements BundleHostListener {
|
||||
@Override
|
||||
public void bundleBuilt(GhidraBundle bundle, String summary) {
|
||||
BundleStatus status = getStatus(bundle);
|
||||
status.setSummary(summary);
|
||||
int row = getRowIndex(status);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleActivationChange(GhidraBundle bundle, boolean newActivation) {
|
||||
BundleStatus status = getStatus(bundle);
|
||||
int row = getRowIndex(status);
|
||||
status.setBusy(false);
|
||||
if (newActivation) {
|
||||
status.setActive(true);
|
||||
}
|
||||
else {
|
||||
status.setActive(false);
|
||||
}
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleAdded(GhidraBundle bundle) {
|
||||
addNewStatus(bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundlesAdded(Collection<GhidraBundle> bundles) {
|
||||
int index = statuses.size();
|
||||
for (GhidraBundle bundle : bundles) {
|
||||
addNewStatusNoFire(bundle);
|
||||
}
|
||||
fireTableRowsInserted(index, bundles.size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleRemoved(GhidraBundle bundle) {
|
||||
BundleStatus status = getStatus(bundle);
|
||||
removeStatus(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundlesRemoved(Collection<GhidraBundle> bundles) {
|
||||
List<BundleStatus> toRemove = bundles.stream()
|
||||
.map(BundleStatusTableModel.this::getStatus)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
removeStatuses(toRemove);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleEnablementChange(GhidraBundle bundle, boolean newEnablement) {
|
||||
BundleStatus status = getStatus(bundle);
|
||||
status.setEnabled(newEnablement);
|
||||
int row = getRowIndex(status);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleException(GhidraBundleException exception) {
|
||||
BundleStatus status = getStatusFromLoc(exception.getBundleLocation());
|
||||
status.setSummary(exception.getMessage());
|
||||
int row = getRowIndex(status);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
BundleStatusTableModel(BundleStatusComponentProvider provider, BundleHost bundleHost) {
|
||||
@@ -142,114 +191,57 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
this.provider = provider;
|
||||
this.bundleHost = bundleHost;
|
||||
statuses = new ArrayList<>();
|
||||
for (GhidraBundle gb : bundleHost.getGhidraBundles()) {
|
||||
addNewStatus(gb);
|
||||
for (GhidraBundle bundle : bundleHost.getGhidraBundles()) {
|
||||
addNewStatus(bundle);
|
||||
}
|
||||
|
||||
bundleHost.addListener(bundleListener = new BundleHostListener() {
|
||||
@Override
|
||||
public void bundleBuilt(GhidraBundle gb, String summary) {
|
||||
BundleStatus status = getStatus(gb);
|
||||
status.setSummary(summary);
|
||||
int row = getRowIndex(status);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
bundleHost.addListener(bundleHostListener = new MyBundleHostListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleActivationChange(GhidraBundle gb, boolean newActivation) {
|
||||
BundleStatus status = getStatus(gb);
|
||||
int row = getRowIndex(status);
|
||||
status.setBusy(false);
|
||||
if (newActivation) {
|
||||
status.setActive(true);
|
||||
}
|
||||
else {
|
||||
status.setActive(false);
|
||||
}
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
Column getColumn(int i) {
|
||||
if (i >= 0 && i < columns.size()) {
|
||||
return columns.get(i);
|
||||
}
|
||||
return badColumn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleAdded(GhidraBundle gb) {
|
||||
addNewStatus(gb);
|
||||
}
|
||||
BundleStatus getStatus(GhidraBundle bundle) {
|
||||
return getStatusFromLoc(bundle.getBundleLocation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundlesAdded(Collection<GhidraBundle> gbundles) {
|
||||
int index = statuses.size();
|
||||
for (GhidraBundle gb : gbundles) {
|
||||
addNewStatusNoFire(gb);
|
||||
}
|
||||
fireTableRowsInserted(index, gbundles.size() - 1);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
});
|
||||
BundleStatus getStatusFromLoc(String bundleLoc) {
|
||||
BundleStatus status = bundleLocToStatusMap.get(bundleLoc);
|
||||
if (status == null) {
|
||||
Msg.showError(BundleStatusTableModel.this, provider.getComponent(),
|
||||
"bundle status error", "bundle has no status!");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
bundleHost.removeListener(bundleListener);
|
||||
bundleHost.removeListener(bundleHostListener);
|
||||
}
|
||||
|
||||
public List<ResourceFile> getEnabledPaths() {
|
||||
List<ResourceFile> list = new ArrayList<>();
|
||||
for (BundleStatus status : statuses) {
|
||||
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());
|
||||
private void addNewStatusNoFire(GhidraBundle bundle) {
|
||||
BundleStatus status = new BundleStatus(bundle.getPath(), bundle.isEnabled(),
|
||||
bundle.isSystemBundle(), bundle.getBundleLocation());
|
||||
if (statuses.contains(status)) {
|
||||
throw new RuntimeException(
|
||||
"Bundle status manager already contains " + gb.getPath().toString());
|
||||
"Bundle status manager already contains " + bundle.getPath().toString());
|
||||
}
|
||||
status.setActive(gb.isActive());
|
||||
loc2status.put(status.getBundleLoc(), status);
|
||||
status.setActive(bundle.isActive());
|
||||
bundleLocToStatusMap.put(status.getBundleLocation(), status);
|
||||
statuses.add(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* add new status and fire a table update
|
||||
*/
|
||||
private void addNewStatus(GhidraBundle gb) {
|
||||
private void addNewStatus(GhidraBundle bundle) {
|
||||
int index = statuses.size();
|
||||
addNewStatusNoFire(gb);
|
||||
addNewStatusNoFire(bundle);
|
||||
fireTableRowsInserted(index, index);
|
||||
}
|
||||
|
||||
@@ -257,7 +249,7 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
if (!status.isReadOnly()) {
|
||||
int i = statuses.indexOf(status);
|
||||
statuses.remove(i);
|
||||
loc2status.remove(status.getBundleLoc());
|
||||
bundleLocToStatusMap.remove(status.getBundleLocation());
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
@@ -271,8 +263,9 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
}
|
||||
|
||||
void remove(int[] modelRows) {
|
||||
List<BundleStatus> toRemove = Arrays.stream(modelRows).mapToObj(statuses::get).collect(
|
||||
Collectors.toUnmodifiableList());
|
||||
List<BundleStatus> toRemove = Arrays.stream(modelRows)
|
||||
.mapToObj(statuses::get)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
removeStatuses(toRemove);
|
||||
}
|
||||
|
||||
@@ -315,7 +308,7 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||
BundleStatus status = statuses.get(rowIndex);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -339,8 +332,19 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
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) {
|
||||
synchronized (bundleStatusListeners) {
|
||||
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) {
|
||||
synchronized (bundleStatusListeners) {
|
||||
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) {
|
||||
List<BundleStatus> rows = new ArrayList<>(modelRowIndices.length);
|
||||
for (int i : modelRowIndices) {
|
||||
@@ -418,24 +433,10 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
* (re)compute cached mapping from bundleloc to bundlepath
|
||||
*/
|
||||
private void computeCache() {
|
||||
loc2status.clear();
|
||||
bundleLocToStatusMap.clear();
|
||||
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;
|
||||
|
||||
/**
|
||||
* Proxy for an OSGi bundle that may require being built.
|
||||
*/
|
||||
public abstract class GhidraBundle {
|
||||
|
||||
protected final ResourceFile path;
|
||||
@@ -31,9 +34,10 @@ public abstract class GhidraBundle {
|
||||
protected boolean enabled;
|
||||
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.path = path;
|
||||
this.path = bundlePath;
|
||||
this.enabled = enabled;
|
||||
this.systemBundle = systemBundle;
|
||||
}
|
||||
@@ -48,45 +52,97 @@ public abstract class GhidraBundle {
|
||||
/**
|
||||
* 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
|
||||
* @throws Exception sorry, wasn't possible
|
||||
* @throws Exception if the build cannot complete
|
||||
*/
|
||||
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 {
|
||||
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() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this bundle is enabled
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
String n = rf.getName().toLowerCase();
|
||||
String n = path.getName().toLowerCase();
|
||||
if (n.endsWith(".bnd")) {
|
||||
return GhidraBundle.Type.BndScript;
|
||||
}
|
||||
@@ -96,11 +152,17 @@ public abstract class GhidraBundle {
|
||||
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;
|
||||
}
|
||||
String n = f.getName().toLowerCase();
|
||||
String n = path.getName().toLowerCase();
|
||||
if (n.endsWith(".bnd")) {
|
||||
return GhidraBundle.Type.BndScript;
|
||||
}
|
||||
@@ -110,21 +172,20 @@ public abstract class GhidraBundle {
|
||||
return GhidraBundle.Type.INVALID;
|
||||
}
|
||||
|
||||
public Bundle getBundle() {
|
||||
return bundleHost.getBundle(getBundleLoc());
|
||||
}
|
||||
|
||||
public void activate() throws Exception {
|
||||
activate(new PrintWriter(System.err));
|
||||
}
|
||||
|
||||
public void activate(PrintWriter writer) throws Exception {
|
||||
build(writer);
|
||||
bundleHost.activateSynchronously(getBundleLoc());
|
||||
/**
|
||||
* Get the OSGi bundle respresented by this GhidraBundle or null
|
||||
*
|
||||
* @return a Bundle or null
|
||||
*/
|
||||
public Bundle getOSGiBundle() {
|
||||
return bundleHost.getOSGiBundle(getBundleLocation());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this bundle is active
|
||||
*/
|
||||
public boolean isActive() {
|
||||
Bundle b = getBundle();
|
||||
Bundle b = getOSGiBundle();
|
||||
return (b != null) && b.getState() == Bundle.ACTIVE;
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -19,18 +19,18 @@ import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
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
|
||||
final public void start(BundleContext bc) throws Exception {
|
||||
start(bc, null);
|
||||
public final void start(BundleContext bundleContext) throws Exception {
|
||||
start(bundleContext, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
final public void stop(BundleContext bc) throws Exception {
|
||||
stop(bc, null);
|
||||
public final void stop(BundleContext bundleContext) throws Exception {
|
||||
stop(bundleContext, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+34
-9
@@ -21,28 +21,52 @@ import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleException;
|
||||
|
||||
public class GhidraBundleException extends OSGiException {
|
||||
private Bundle bundle;
|
||||
private String bundle_loc;
|
||||
private final Bundle bundle;
|
||||
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) {
|
||||
super(msg + ": " + parsedCause(cause), cause);
|
||||
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);
|
||||
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() {
|
||||
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() {
|
||||
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) {
|
||||
return "";
|
||||
}
|
||||
@@ -81,9 +105,10 @@ public class GhidraBundleException extends OSGiException {
|
||||
return message;
|
||||
}
|
||||
// parse the package constraints from filters in the BundleRequirement string
|
||||
String packages =
|
||||
OSGiUtils.extractPackages(be.getMessage()).stream().distinct().collect(
|
||||
Collectors.joining("\n"));
|
||||
String packages = OSGiUtils.extractPackageNamesFromFailedResolution(be.getMessage())
|
||||
.stream()
|
||||
.distinct()
|
||||
.collect(Collectors.joining("\n"));
|
||||
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 generic.jar.ResourceFile;
|
||||
|
||||
/**
|
||||
* Proxy to an ordinary OSGi Jar bundle. {@link GhidraJarBundle#build(PrintWriter)} does nothing.
|
||||
*/
|
||||
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,
|
||||
boolean systemBundle) {
|
||||
super(bundleHost, path, enabled, systemBundle);
|
||||
this.bundleLoc = "file://" + path.getAbsolutePath().toString();
|
||||
this.bundleLocation = "file://" + path.getAbsolutePath().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,19 +57,19 @@ public class GhidraJarBundle extends GhidraBundle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBundleLoc() {
|
||||
return bundleLoc;
|
||||
public String getBundleLocation() {
|
||||
return bundleLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BundleRequirement> getAllReqs() {
|
||||
public List<BundleRequirement> getAllRequirements() {
|
||||
Jar jar;
|
||||
try {
|
||||
jar = new Jar(path.getFile(true));
|
||||
Manifest m = jar.getManifest();
|
||||
String imps = m.getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
|
||||
if (imps != null) {
|
||||
return BundleHost.parseImports(imps);
|
||||
return OSGiUtils.parseImports(imps);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
+8
-5
@@ -23,11 +23,14 @@ import org.osgi.framework.wiring.BundleRequirement;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
|
||||
/**
|
||||
* {@link GhidraPlaceholderBundle} represents invalid bundle paths in the GUI.
|
||||
*/
|
||||
public class GhidraPlaceholderBundle extends GhidraBundle {
|
||||
|
||||
GhidraPlaceholderBundle(BundleHost bundleHost, ResourceFile path, boolean enabled,
|
||||
boolean systemBundle) {
|
||||
super(bundleHost, path, enabled, systemBundle);
|
||||
GhidraPlaceholderBundle(BundleHost bundleHost, ResourceFile bundlePath, boolean isEnabled,
|
||||
boolean isSystemBundle) {
|
||||
super(bundleHost, bundlePath, isEnabled, isSystemBundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -41,12 +44,12 @@ public class GhidraPlaceholderBundle extends GhidraBundle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBundleLoc() {
|
||||
public String getBundleLocation() {
|
||||
return "invalid://" + getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
List<BundleRequirement> getAllReqs() {
|
||||
List<BundleRequirement> getAllRequirements() {
|
||||
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
|
||||
* 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 {
|
||||
boolean chilled = false;
|
||||
@@ -35,27 +35,29 @@ class LessFreneticGTable extends GTable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tableChanged(TableModelEvent e) {
|
||||
public void tableChanged(TableModelEvent event) {
|
||||
if (!chilled) {
|
||||
super.tableChanged(e);
|
||||
super.tableChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LessFreneticGTable(TableModel model) {
|
||||
super(model);
|
||||
}
|
||||
|
||||
/** suppress issuing table change events */
|
||||
public void chill() {
|
||||
chilled = true;
|
||||
}
|
||||
|
||||
/** resume issuing table change events */
|
||||
public void thaw() {
|
||||
chilled = false;
|
||||
notifyTableChanged(new TableModelEvent(getModel()));
|
||||
}
|
||||
|
||||
LessFreneticGTable(TableModel dm) {
|
||||
super(dm);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected <T> SelectionManager createSelectionManager(TableModel model) {
|
||||
|
||||
@@ -14,15 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import ghidra.util.exception.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;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.io.File;
|
||||
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.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 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 m.group(1);
|
||||
}).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() {
|
||||
URI URI = entryFile.toURI();
|
||||
URL URL = null;
|
||||
URI uri = entryFile.toURI();
|
||||
URL url = null;
|
||||
try {
|
||||
URL = URI.toURL();
|
||||
url = uri.toURL();
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
// shouldn't happen
|
||||
@@ -591,7 +591,7 @@ class GhidraScriptActionManager {
|
||||
return;
|
||||
}
|
||||
|
||||
BrowserLoader.display(URL, URL, plugin.getTool());
|
||||
BrowserLoader.display(url, url, plugin.getTool());
|
||||
}
|
||||
|
||||
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 generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.osgi.*;
|
||||
import ghidra.app.plugin.core.osgi.BundleHost.BuildFailure;
|
||||
import ghidra.app.script.*;
|
||||
import ghidra.app.services.ConsoleService;
|
||||
import ghidra.framework.options.SaveState;
|
||||
@@ -66,12 +65,12 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
static final String WINDOW_GROUP = "Script Group";
|
||||
|
||||
private Map<ResourceFile, GhidraScriptEditorComponentProvider> editorMap = new HashMap<>();
|
||||
final private GhidraScriptMgrPlugin plugin;
|
||||
private final GhidraScriptMgrPlugin plugin;
|
||||
private JPanel component;
|
||||
private RootNode scriptRoot;
|
||||
private GTree scriptCategoryTree;
|
||||
private DraggableScriptTable scriptTable;
|
||||
final private GhidraScriptInfoManager infoManager;
|
||||
private final GhidraScriptInfoManager infoManager;
|
||||
private GhidraScriptTableModel tableModel;
|
||||
private BundleStatusComponentProvider bundleStatusComponentProvider;
|
||||
private TaskListener taskListener = new ScriptTaskListener();
|
||||
@@ -99,8 +98,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
};
|
||||
|
||||
final private BundleHost bundleHost;
|
||||
final private RefreshingBundleHostListener refreshingBundleHostListener =
|
||||
private final BundleHost bundleHost;
|
||||
private final RefreshingBundleHostListener refreshingBundleHostListener =
|
||||
new RefreshingBundleHostListener();
|
||||
|
||||
GhidraScriptComponentProvider(GhidraScriptMgrPlugin plugin, BundleHost bundleHost) {
|
||||
@@ -158,8 +157,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
});
|
||||
|
||||
scriptCategoryTree.getSelectionModel().setSelectionMode(
|
||||
TreeSelectionModel.SINGLE_TREE_SELECTION);
|
||||
scriptCategoryTree.getSelectionModel()
|
||||
.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
||||
|
||||
tableModel = new GhidraScriptTableModel(this, infoManager);
|
||||
|
||||
@@ -237,7 +236,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
//==================================================================================================
|
||||
|
||||
public void readConfigState(SaveState saveState) {
|
||||
bundleHost.restoreStateAndActivate(saveState, getTool());
|
||||
bundleHost.restoreManagedBundleState(saveState, getTool());
|
||||
|
||||
actionManager.restoreUserDefinedKeybindings(saveState);
|
||||
actionManager.restoreScriptsThatAreInTool(saveState);
|
||||
@@ -261,7 +260,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
bundleHost.saveState(saveState);
|
||||
bundleHost.saveManagedBundleState(saveState);
|
||||
|
||||
actionManager.saveUserDefinedKeybindings(saveState);
|
||||
actionManager.saveScriptsThatAreInTool(saveState);
|
||||
@@ -408,8 +407,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
action.setKeyBindingData(new KeyBindingData(ks));
|
||||
}
|
||||
|
||||
assert !infoManager.containsMetadata(
|
||||
renameFile) : "renamed script already has metadata";
|
||||
assert !infoManager
|
||||
.containsMetadata(renameFile) : "renamed script already has metadata";
|
||||
infoManager.getScriptInfo(renameFile);
|
||||
|
||||
tableModel.switchScript(script, renameFile);
|
||||
@@ -452,15 +451,20 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
public List<ResourceFile> getScriptDirectories() {
|
||||
return bundleHost.getGhidraBundles().stream().filter(
|
||||
gb -> gb.isEnabled() && gb instanceof GhidraSourceBundle).map(
|
||||
GhidraBundle::getPath).collect(Collectors.toList());
|
||||
return bundleHost.getGhidraBundles()
|
||||
.stream()
|
||||
.filter(bundle -> bundle.isEnabled() && bundle instanceof GhidraSourceBundle)
|
||||
.map(GhidraBundle::getPath)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<ResourceFile> getWritableScriptDirectories() {
|
||||
return bundleHost.getGhidraBundles().stream().filter(
|
||||
GhidraSourceBundle.class::isInstance).filter(gb -> !gb.isSystemBundle()).map(
|
||||
GhidraBundle::getPath).collect(Collectors.toList());
|
||||
return bundleHost.getGhidraBundles()
|
||||
.stream()
|
||||
.filter(GhidraSourceBundle.class::isInstance)
|
||||
.filter(bundle -> !bundle.isSystemBundle())
|
||||
.map(GhidraBundle::getPath)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
boolean isEditorOpen(ResourceFile script) {
|
||||
@@ -698,14 +702,14 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
class RefreshingBundleHostListener implements BundleHostListener {
|
||||
|
||||
@Override
|
||||
public void bundleBuilt(GhidraBundle sb, String summary) {
|
||||
if (sb instanceof GhidraSourceBundle) {
|
||||
GhidraSourceBundle gsb = (GhidraSourceBundle) sb;
|
||||
for (ResourceFile sf : gsb.getNewSources()) {
|
||||
if (infoManager.containsMetadata(sf)) {
|
||||
ScriptInfo info = infoManager.getExistingScriptInfo(sf);
|
||||
BuildFailure e = gsb.getErrors(sf);
|
||||
info.setCompileErrors(e != null);
|
||||
public void bundleBuilt(GhidraBundle bundle, String summary) {
|
||||
if (bundle instanceof GhidraSourceBundle) {
|
||||
GhidraSourceBundle sourceBundle = (GhidraSourceBundle) bundle;
|
||||
for (ResourceFile sourceFile : sourceBundle.getNewSources()) {
|
||||
if (infoManager.containsMetadata(sourceFile)) {
|
||||
ScriptInfo scriptInfo = infoManager.getExistingScriptInfo(sourceFile);
|
||||
GhidraBundle.BuildFailure e = sourceBundle.getErrors(sourceFile);
|
||||
scriptInfo.setCompileErrors(e != null);
|
||||
}
|
||||
}
|
||||
tableModel.fireTableDataChanged();
|
||||
@@ -713,32 +717,32 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleEnablementChange(GhidraBundle gbundle, boolean newEnablment) {
|
||||
if (gbundle instanceof GhidraSourceBundle) {
|
||||
public void bundleEnablementChange(GhidraBundle bundle, boolean newEnablment) {
|
||||
if (bundle instanceof GhidraSourceBundle) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleAdded(GhidraBundle gbundle) {
|
||||
public void bundleAdded(GhidraBundle bundle) {
|
||||
plugin.getTool().setConfigChanged(true);
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundlesAdded(Collection<GhidraBundle> gbundles) {
|
||||
public void bundlesAdded(Collection<GhidraBundle> bundles) {
|
||||
plugin.getTool().setConfigChanged(true);
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleRemoved(GhidraBundle gbundle) {
|
||||
public void bundleRemoved(GhidraBundle bundle) {
|
||||
plugin.getTool().setConfigChanged(true);
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundlesRemoved(Collection<GhidraBundle> gbundles) {
|
||||
public void bundlesRemoved(Collection<GhidraBundle> bundles) {
|
||||
plugin.getTool().setConfigChanged(true);
|
||||
refresh();
|
||||
}
|
||||
@@ -1030,10 +1034,10 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
private JComponent buildDescriptionComponent() {
|
||||
JPanel descriptionPanel = new JPanel(new BorderLayout());
|
||||
descriptionTextPane = new JTextPane();
|
||||
descriptionTextPane.setEditable(false);
|
||||
descriptionTextPane.setEditorKit(new HTMLEditorKit());
|
||||
JPanel descriptionPanel = new JPanel(new BorderLayout());
|
||||
descriptionPanel.add(descriptionTextPane);
|
||||
JScrollPane scrollPane = new JScrollPane(descriptionPanel);
|
||||
|
||||
|
||||
+3
-3
@@ -50,10 +50,10 @@ import ghidra.util.task.TaskListener;
|
||||
//@formatter:on
|
||||
public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScriptService {
|
||||
|
||||
final private GhidraScriptComponentProvider provider;
|
||||
private final GhidraScriptComponentProvider provider;
|
||||
|
||||
static private int loaded = 0;
|
||||
final private BundleHost bundleHost;
|
||||
private static int loaded = 0;
|
||||
private final BundleHost bundleHost;
|
||||
|
||||
public GhidraScriptMgrPlugin(PluginTool tool) {
|
||||
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 ImageIcon ERROR_IMG = Icons.ERROR_ICON;
|
||||
|
||||
final static String SCRIPT_ACTION_COLUMN_NAME = "In Tool";
|
||||
final static String SCRIPT_STATUS_COLUMN_NAME = "Status";
|
||||
static final String SCRIPT_ACTION_COLUMN_NAME = "In Tool";
|
||||
static final String SCRIPT_STATUS_COLUMN_NAME = "Status";
|
||||
|
||||
private GhidraScriptComponentProvider provider;
|
||||
private List<ResourceFile> scriptList = new ArrayList<>();
|
||||
final private GhidraScriptInfoManager infoManager;
|
||||
private final GhidraScriptInfoManager infoManager;
|
||||
|
||||
GhidraScriptTableModel(GhidraScriptComponentProvider provider,
|
||||
GhidraScriptInfoManager infoManager) {
|
||||
|
||||
@@ -74,12 +74,12 @@ public class GhidraScriptUtil {
|
||||
setBundleHost(bundleHost);
|
||||
if (extraSystemPaths != null) {
|
||||
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.addGhidraBundles(GhidraScriptUtil.getSystemScriptPaths(), true, true);
|
||||
bundleHost.add(GhidraScriptUtil.getUserScriptDirectory(), true, false);
|
||||
bundleHost.add(GhidraScriptUtil.getSystemScriptPaths(), true, true);
|
||||
}
|
||||
|
||||
public static void dispose() {
|
||||
|
||||
@@ -51,9 +51,9 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
||||
@Override
|
||||
public boolean deleteScript(ResourceFile sourceFile) {
|
||||
try {
|
||||
Bundle b = getBundleForSource(sourceFile).getBundle();
|
||||
if (b != null) {
|
||||
_bundleHost.deactivateSynchronously(b);
|
||||
Bundle osgiBundle = getBundleForSource(sourceFile).getOSGiBundle();
|
||||
if (osgiBundle != null) {
|
||||
_bundleHost.deactivateSynchronously(osgiBundle);
|
||||
}
|
||||
}
|
||||
catch (GhidraBundleException | InterruptedException e) {
|
||||
@@ -95,7 +95,8 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
||||
public Class<?> loadClass(ResourceFile sourceFile, PrintWriter writer) throws Exception {
|
||||
GhidraSourceBundle gb = getBundleForSource(sourceFile);
|
||||
gb.build(writer);
|
||||
Bundle b = _bundleHost.installFromLoc(gb.getBundleLoc());
|
||||
|
||||
Bundle b = _bundleHost.install(gb);
|
||||
|
||||
_bundleHost.activateSynchronously(b);
|
||||
|
||||
|
||||
+12
-12
@@ -26,6 +26,7 @@ import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.TableModel;
|
||||
@@ -44,11 +45,9 @@ import docking.widgets.table.RowObjectTableModel;
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import generic.jar.ResourceFile;
|
||||
import generic.test.TestUtils;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.console.ConsoleComponentProvider;
|
||||
import ghidra.app.plugin.core.osgi.BundleHost;
|
||||
import ghidra.app.plugin.core.osgi.BundleStatusComponentProvider;
|
||||
import ghidra.app.plugin.core.osgi.GhidraSourceBundle;
|
||||
import ghidra.app.script.*;
|
||||
import ghidra.app.services.ConsoleService;
|
||||
import ghidra.framework.Application;
|
||||
@@ -181,8 +180,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||
|
||||
static protected void wipe(Path path) throws IOException {
|
||||
if (Files.exists(path)) {
|
||||
for (Path p : (Iterable<Path>) Files.walk(path).sorted(
|
||||
Comparator.reverseOrder())::iterator) {
|
||||
for (Path p : (Iterable<Path>) Files.walk(path)
|
||||
.sorted(Comparator.reverseOrder())::iterator) {
|
||||
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 " +
|
||||
"a save operation: " + file);
|
||||
printChars(expectedContents, fileText);
|
||||
Assert.fail(
|
||||
"Contents of file on disk do not match that of the editor after performing " +
|
||||
Assert
|
||||
.fail("Contents of file on disk do not match that of the editor after performing " +
|
||||
"a save operation: " + file);
|
||||
}
|
||||
//
|
||||
@@ -980,15 +979,16 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||
|
||||
protected void cleanupOldTestFiles() throws IOException {
|
||||
// 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();
|
||||
|
||||
// destroy any NewScriptxxx files...and Temp ones too
|
||||
BundleStatusComponentProvider bundleStatusComponentProvider =
|
||||
(BundleStatusComponentProvider) TestUtils.getInstanceField(
|
||||
"bundleStatusComponentProvider", provider);
|
||||
List<ResourceFile> paths = bundleStatusComponentProvider.getModel().getEnabledPaths();
|
||||
List<ResourceFile> paths = provider.getBundleHost()
|
||||
.getBundlePaths()
|
||||
.stream()
|
||||
.filter(ResourceFile::isDirectory)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (ResourceFile path : paths) {
|
||||
File file = path.getFile(false);
|
||||
|
||||
+5
-5
@@ -55,7 +55,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
tmpdirs.add(tmpDir);
|
||||
|
||||
ResourceFile rp = new ResourceFile(tmpDir.toFile());
|
||||
current_gb = bundleHost.addGhidraBundle(rp, true, false);
|
||||
current_gb = bundleHost.add(rp, true, false);
|
||||
gbstack.push(current_gb);
|
||||
return current_gb;
|
||||
}
|
||||
@@ -71,7 +71,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
@Before
|
||||
public void setup() throws OSGiException, IOException {
|
||||
wipe(BundleHost.getCompiledBundlesDir());
|
||||
wipe(GhidraSourceBundle.getCompiledBundlesDir());
|
||||
|
||||
bundleHost = new BundleHost();
|
||||
bundleHost.startFramework();
|
||||
@@ -106,7 +106,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
}
|
||||
|
||||
protected void activate() throws Exception {
|
||||
Bundle bundle = bundleHost.installFromLoc(current_gb.getBundleLoc());
|
||||
Bundle bundle = bundleHost.install(current_gb);
|
||||
assertNotNull("failed to install bundle", bundle);
|
||||
bundle.start();
|
||||
}
|
||||
@@ -117,7 +117,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
}
|
||||
|
||||
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);
|
||||
return clazz;
|
||||
}
|
||||
@@ -313,7 +313,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
|
||||
pushNewBundle();
|
||||
// @imports tag is only parsed from classes in default package
|
||||
// @importpackages tag is only parsed from classes in default package
|
||||
addClass(
|
||||
"//@importpackage lib\n"
|
||||
,
|
||||
|
||||
Reference in New Issue
Block a user