diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPlugin.java index 979763eb5e..67a99bde38 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/listing/DebuggerListingPlugin.java @@ -352,33 +352,6 @@ public class DebuggerListingPlugin extends AbstractCodeBrowserPlugin - * This is only used by the ProgramManager. I don't need state per program. It would be nice to - * have state per Trace, but this facility is usurped only for the ProgramManager. Here, it gets - * in my way, since it restores previous, now incorrect, state on program switch. It tends to - * override the static sync. - */ - @Override - public Object getTransientState() { - // ProgramManager does all this for programs. I don't need that here. - return new Object[] {}; - } - - /** - * {@inheritDoc} - * - * @see #getTransientState() - */ - @Override - public void restoreTransientState(Object objectState) { - /*try (Suppression supp = cbGoTo.suppress(null)) { - super.restoreTransientState(objectState); - }*/ - } - @Override public void writeDataState(SaveState saveState) { SaveState connectedProviderState = new SaveState(); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesPlugin.java index 356c0d12e3..158127c9df 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesPlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/memory/DebuggerMemoryBytesPlugin.java @@ -166,13 +166,13 @@ public class DebuggerMemoryBytesPlugin } @Override - public Object getTransientState() { + public ByteViewerTransientState getTransientState() { // Not needed, since I'm not coordinated with ProgramManager - return new Object[] {}; + return null; } @Override - public void restoreTransientState(Object objectState) { + public void restoreTransientState(ByteViewerTransientState objectState) { // Not needed, since I'm not coordinated with ProgramManager } diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java index ca7077b1ea..480b7e6c7c 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java @@ -504,8 +504,12 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm !ProgramEmulationUtils.isEmulatedProgram(current.getTrace())) { return false; } + ProgramLocation programLoc = ctx.getLocation(); + if (programLoc == null) { + return false; + } TraceLocation traceLoc = staticMappings.getOpenMappedLocation( - current.getTrace(), ctx.getLocation(), current.getSnap()); + current.getTrace(), programLoc, current.getSnap()); if (traceLoc == null) { return false; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/bookmark/BookmarkPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/bookmark/BookmarkPlugin.java index 4ff5125375..bbb8091e44 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/bookmark/BookmarkPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/bookmark/BookmarkPlugin.java @@ -15,7 +15,7 @@ */ package ghidra.app.plugin.core.bookmark; -import static ghidra.framework.model.DomainObjectEvent.*; +import static ghidra.framework.model.DomainObjectEvent.RESTORED; import static ghidra.program.util.ProgramEvent.*; import java.awt.event.KeyEvent; @@ -34,13 +34,13 @@ import ghidra.app.CorePluginPackage; import ghidra.app.events.ProgramSelectionPluginEvent; import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.ProgramPlugin; +import ghidra.app.plugin.core.bookmark.BookmarkPlugin.BookmarkTransientState; import ghidra.app.services.*; import ghidra.framework.cmd.CompoundCmd; import ghidra.framework.model.DomainObjectListener; import ghidra.framework.model.DomainObjectListenerBuilder; import ghidra.framework.options.SaveState; -import ghidra.framework.plugintool.PluginInfo; -import ghidra.framework.plugintool.PluginTool; +import ghidra.framework.plugintool.*; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.program.model.address.*; import ghidra.program.model.listing.*; @@ -68,7 +68,8 @@ import resources.MultiIconBuilder; eventsProduced = { ProgramSelectionPluginEvent.class } ) //@formatter:on -public class BookmarkPlugin extends ProgramPlugin implements PopupActionProvider, BookmarkService { +public class BookmarkPlugin extends ProgramPlugin implements PopupActionProvider, BookmarkService, + PluginWithTransientState { private final static int MAX_DELETE_ACTIONS = 10; @@ -392,14 +393,16 @@ public class BookmarkPlugin extends ProgramPlugin implements PopupActionProvider bookmarkMgr = null; } + record BookmarkTransientState(FilterState filterState) {} + @Override - public Object getTransientState() { - return provider.getFilterState(); + public BookmarkTransientState getTransientState() { + return new BookmarkTransientState(provider.getFilterState()); } @Override - public void restoreTransientState(Object state) { - provider.restoreFilterState((FilterState) state); + public void restoreTransientState(BookmarkTransientState state) { + provider.restoreFilterState(state.filterState); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeBrowserPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeBrowserPlugin.java index dc2a3569fd..795c14eec3 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeBrowserPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/CodeBrowserPlugin.java @@ -26,6 +26,7 @@ import ghidra.app.CorePluginPackage; import ghidra.app.context.*; import ghidra.app.events.*; import ghidra.app.plugin.PluginCategoryNames; +import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin.CodeBrowserTransientState; import ghidra.app.services.*; import ghidra.app.util.viewer.format.FormatManager; import ghidra.app.util.viewer.listingpanel.ListingPanel; @@ -63,7 +64,8 @@ import ghidra.program.util.ProgramSelection; ViewChangedPluginEvent.class, ProgramHighlightPluginEvent.class }, eventsProduced = { ProgramLocationPluginEvent.class, ProgramSelectionPluginEvent.class }) //@formatter:on -public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin { +public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin + implements PluginWithTransientState { public CodeBrowserPlugin(PluginTool tool) { super(tool); @@ -181,38 +183,28 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin { private static final String DEFAULT_TREE_NAME = "Program Tree"; private static final String PROGRAM_TREE_OPTION_NAME = "Program Tree"; @@ -197,8 +199,8 @@ public class ProgramTreePlugin extends ProgramPlugin } /** - * Tells a plugin that it is no longer needed. The plugin should remove - * itself from anything that it is registered to and release any resources. + * Tells a plugin that it is no longer needed. The plugin should remove itself from anything + * that it is registered to and release any resources. */ @Override public void dispose() { @@ -260,8 +262,8 @@ public class ProgramTreePlugin extends ProgramPlugin } /** - * Read the data for the plugin upon deserialization; reads what should be - * the current selection in the tree. + * Read the data for the plugin upon deserialization; reads what should be the current selection + * in the tree. */ @Override public void readDataState(SaveState saveState) { @@ -337,24 +339,23 @@ public class ProgramTreePlugin extends ProgramPlugin componentProvider.treeViewsRestored(list); } + record ProgramTreeTransientState(SaveState ss) {} + @Override - public Object getTransientState() { + public ProgramTreeTransientState getTransientState() { SaveState ss = new SaveState(); writeDataState(ss); - return ss; + return new ProgramTreeTransientState(ss); } @Override - public void restoreTransientState(Object state) { - SaveState ss = (SaveState) state; - readDataState(ss); + public void restoreTransientState(ProgramTreeTransientState state) { + readDataState(state.ss); } @Override public void processEvent(PluginEvent event) { - - if (event instanceof TreeSelectionPluginEvent) { - TreeSelectionPluginEvent ev = (TreeSelectionPluginEvent) event; + if (event instanceof TreeSelectionPluginEvent ev) { String treeName = ev.getTreeName(); TreeViewProvider provider = providerMap.get(treeName); if (provider == null) { @@ -532,6 +533,7 @@ public class ProgramTreePlugin extends ProgramPlugin /** * Method renameView. + * * @param treeViewProvider the provider * @param newName the new name * @return true if renamed @@ -598,8 +600,7 @@ public class ProgramTreePlugin extends ProgramPlugin * Get the program tree for the given tree name. * * @param treeName name of tree in the program (also the name of the view) - * @return ProgramDnDTree tree, or null if there is no provider for the - * given name + * @return ProgramDnDTree tree, or null if there is no provider for the given name */ ProgramDnDTree getTree(String treeName) { TreeViewProvider provider = providerMap.get(treeName); @@ -623,8 +624,8 @@ public class ProgramTreePlugin extends ProgramPlugin } /** - * Notification from the program domain object change listener that a - * fragment was moved; update all the view maps. + * Notification from the program domain object change listener that a fragment was moved; update + * all the view maps. */ void fragmentMoved() { for (String treeName : providerMap.keySet()) { @@ -636,8 +637,8 @@ public class ProgramTreePlugin extends ProgramPlugin /** * The program was restored from an Undo/Redo operation so reload it * - * @param checkRoot if true, only rebuild the tree if the root node is invalid; if false, - * force a rebuild of the tree + * @param checkRoot if true, only rebuild the tree if the root node is invalid; if false, force + * a rebuild of the tree */ void reloadProgram(boolean checkRoot) { if (currentProgram == null) { @@ -676,8 +677,8 @@ public class ProgramTreePlugin extends ProgramPlugin } /** - * Remember expansion and selection state, and the reload the program - * because it just got restored from an undo operation. + * Remember expansion and selection state, and the reload the program because it just got + * restored from an undo operation. */ private void reloadTree(final ProgramDnDTree tree, boolean checkRoot) { if (tree == null) { @@ -769,8 +770,8 @@ public class ProgramTreePlugin extends ProgramPlugin } /** - * Return true if a tree with the given name exists in the program. If - * program is null and if the tree name is the default name, return true. + * Return true if a tree with the given name exists in the program. If program is null and if + * the tree name is the default name, return true. * * @param treeName tree name to look for * @return boolean @@ -795,8 +796,7 @@ public class ProgramTreePlugin extends ProgramPlugin /** * Set the program on each of the providers. * - * @param p program that is being opened; if p is null, then program is - * being closed. + * @param p program that is being opened; if p is null, then program is being closed. */ private void setProgram(Program p) { @@ -831,8 +831,8 @@ public class ProgramTreePlugin extends ProgramPlugin } /** - * Add tree views that are in the program; if no trees exist (unlikely), - * then add the default provider. + * Add tree views that are in the program; if no trees exist (unlikely), then add the default + * provider. */ private void addTreeViews() { deregisterService(ViewProviderService.class, defaultProvider); @@ -863,8 +863,8 @@ public class ProgramTreePlugin extends ProgramPlugin } /** - * Callback from the create default view action in the provider. Add a - * one-up number to the default name. + * Callback from the create default view action in the provider. Add a one-up number to the + * default name. */ private void createDefaultTreeView() { Listing listing = currentProgram.getListing(); @@ -891,9 +891,8 @@ public class ProgramTreePlugin extends ProgramPlugin } /** - * Method called when the "Open Tree View" icon is hit; creates a window - * relative to the event source (in this case, a button) and shows the list - * of trees currently in the Program. + * Method called when the "Open Tree View" icon is hit; creates a window relative to the event + * source (in this case, a button) and shows the list of trees currently in the Program. */ private void openView(Object sourceObject) { JButton button = sourceObject instanceof JButton ? (JButton) sourceObject : null; @@ -919,9 +918,8 @@ public class ProgramTreePlugin extends ProgramPlugin } /** - * Open an existing view in the program. If a provider already exists for - * the given tree name, make this the current view provider in the view - * manager service. + * Open an existing view in the program. If a provider already exists for the given tree name, + * make this the current view provider in the view manager service. * * @param treeName name of tree */ diff --git a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/AbstractByteViewerPlugin.java b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/AbstractByteViewerPlugin.java index 687b671511..698920a8f1 100644 --- a/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/AbstractByteViewerPlugin.java +++ b/Ghidra/Features/ByteViewer/src/main/java/ghidra/app/plugin/core/byteviewer/AbstractByteViewerPlugin.java @@ -21,12 +21,12 @@ import org.jdom2.Element; import ghidra.app.events.AbstractLocationPluginEvent; import ghidra.app.events.AbstractSelectionPluginEvent; +import ghidra.app.plugin.core.byteviewer.AbstractByteViewerPlugin.ByteViewerTransientState; import ghidra.app.services.*; import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainObject; import ghidra.framework.options.SaveState; -import ghidra.framework.plugintool.Plugin; -import ghidra.framework.plugintool.PluginTool; +import ghidra.framework.plugintool.*; import ghidra.program.model.listing.Program; import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramSelection; @@ -34,7 +34,7 @@ import ghidra.util.SystemUtilities; import utility.function.Callback; public abstract class AbstractByteViewerPlugin

- extends Plugin { + extends Plugin implements HasTransientState { protected Program currentProgram; private boolean areEventsDisabled; @@ -216,26 +216,20 @@ public abstract class AbstractByteViewerPlugin

{ - Object[] state = (Object[]) objectState; - connectedProvider.restoreLocation((SaveState) state[0]); - connectedProvider.setSelection((ProgramSelection) state[1]); + connectedProvider.restoreLocation(state.ss); + connectedProvider.setSelection(state.selection); }); } diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/Plugin.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/Plugin.java index d3a3cb329b..cae9b0166d 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/Plugin.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/Plugin.java @@ -28,6 +28,7 @@ import ghidra.framework.options.SaveState; import ghidra.framework.plugintool.util.*; import ghidra.util.Msg; import ghidra.util.SystemUtilities; +import ghidra.util.classfinder.ClassSearcher; import ghidra.util.classfinder.ExtensionPoint; /** @@ -44,7 +45,7 @@ import ghidra.util.classfinder.ExtensionPoint; *

  • Class name ends with "Plugin" and does not match any other Plugin, regardless of its * location in the package tree.
  • *
  • Have a {@link PluginInfo @PluginInfo()} annotation.
  • - *
  • Have a constructor with exactly 1 parameter: PluginTool.
  • + *
  • Have a constructor with exactly 1 parameter: {@link PluginTool}.
  • *
  • *
      *
    • public MyPlugin(PluginTool tool) { ... }
    • @@ -53,7 +54,9 @@ import ghidra.util.classfinder.ExtensionPoint; *
    • Usually overrides protected void init().
    • *
    *

    Class naming

    - * All Plugin Classes MUST END IN "Plugin". If not, the ClassSearcher will not find them. + *

    + * All Plugin Classes MUST END IN "Plugin". If not, the {@link ClassSearcher} will not find + * them. *

    * Some special Plugins marked with the {@link ProgramaticUseOnly} interface are manually * created and do not follow this naming requirement. @@ -66,8 +69,8 @@ import ghidra.util.classfinder.ExtensionPoint; *

  • Plugin base class constructor is called.
  • *
  • *
      - *
    1. Services listed in the @PluginInfo annotation are automatically added to dependency - * list
    2. + *
    3. Services listed in the {@code @}{@link PluginInfo} annotation are automatically + * added to dependency list
    4. *
    *
  • *
  • Your Plugin publishes any services listed in PluginInfo using @@ -87,7 +90,7 @@ import ghidra.util.classfinder.ExtensionPoint; *
      *
    1. Call {@link PluginTool#getService(Class)} to get service * implementations. (the service class being requested should already be - * listed in the @PluginInfo)
    2. + * listed in the PluginInfo) *
    3. Create Actions (optional)
    4. *
    5. Other initialization stuff.
    6. *
    @@ -126,9 +129,9 @@ import ghidra.util.classfinder.ExtensionPoint; * of its required services are loaded successfully and are available for use when your Plugin * calls the {@link PluginTool#getService(Class)} method. *

    - * Conversely, any services your Plugin advertises in @PluginInfo must be published via calls to - * {@link #registerServiceProvided(Class, Object) registerServiceProvided()} in your Plugin's - * constructor. + * Conversely, any services your Plugin advertises in {@code @PluginInfo} must be published via + * calls to {@link #registerServiceProvided(Class, Object) registerServiceProvided()} in your + * Plugin's constructor. *

    * Cyclic dependencies are not allowed and will cause the Plugin management code to fail to * load your Plugin. (i.e., PluginA requires a service that PluginB provides, which requires a @@ -139,18 +142,19 @@ import ghidra.util.classfinder.ExtensionPoint; * annotation that it {@link PluginInfo#servicesProvided() provides} an interface class. *

    * Your Plugin can either directly implement the interface in your Plugin class: - *

    - *   public class MyPlugin extends Plugin implements MyService {....} + *

    + * 	public class MyPlugin extends Plugin implements MyService {....}
    + * 
    *

    * or it may delegate the handling of the service interface to another object during its * constructor: - *

    - *   public MyPlugin(PluginTool tool) {
    - *     ...
    - *     MyService serviceObj = new MyService() { ... };
    - *     registerServiceProvided(MyService.class, serviceObj); - *
    - *   }
    + *

    + * 	public MyPlugin(PluginTool tool) {
    + * 		// ...
    + * 		MyService serviceObj = new MyService() { ... };
    + * 		registerServiceProvided(MyService.class, serviceObj);
    + * 	}
    + * 
    *

    * When your Plugin directly implements the advertised service interface, you should not * call {@link #registerServiceProvided(Class, Object) registerServiceProvided} for that service @@ -189,12 +193,14 @@ import ghidra.util.classfinder.ExtensionPoint; *

    Important interfaces Plugins often need to implement

    *
      *
    • {@link OptionsChangeListener} - to receive notification when a configuration option - * is changed by the user.
    • + * is changed by the user. *
    • {@link ApplicationLevelPlugin} - marks this Plugin as being suitable for inclusion in the * application-level tool.
    • *
    • {@link ApplicationLevelOnlyPlugin} - marks this Plugin as application-level only, not * usable in an application's sub-tools.
    • *
    • {@link ProgramaticUseOnly} - marks this Plugin as special and not for user configuration.
    • + *
    • {@link PluginWithTransientState} - allows this Plugin to save and restore per-program + * transient state.
    • *
    * */ @@ -442,6 +448,8 @@ public abstract class Plugin implements ExtensionPoint, PluginEventListener, Ser // do nothing by default; subclasses should override as needed } + // NOTE: get/restoreTransientState moved to PluginWithTransientState interface + /** * Tells the Plugin to write any data-dependent state to the * output stream. @@ -794,28 +802,6 @@ public abstract class Plugin implements ExtensionPoint, PluginEventListener, Ser return name.hashCode(); } - /** - * Provides the transient state object that was returned in the corresponding getTransientState() - * call. Plugins should override this method if they have state that needs to be saved as domain objects - * get switched between active and inactive. - * @param state the state object that was generated by this plugin's getTransientState() method. - */ - public void restoreTransientState(Object state) { - // do nothing by default; subclasses should override as needed - } - - /** - * Returns an object containing the plugins state. Plugins should override this method if - * they have state that they want to maintain between domain object state transitions (i.e. when the - * user tabs to a different domain object and back) Whatever object is returned will be fed back to - * the plugin after the tool state is switch back to the domain object that was active when the this - * method was called. - * @return Object to be return in the restoreTransientState() method. - */ - public Object getTransientState() { - return null; - } - /** * Notification that all plugins have had their data states restored. */ diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginWithTransientState.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginWithTransientState.java new file mode 100644 index 0000000000..b76f468e00 --- /dev/null +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/PluginWithTransientState.java @@ -0,0 +1,51 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.framework.plugintool; + +/** + * A {@link Plugin} that can save and restore transient state + *

    + * Plugins should implements this if they have state that needs to be saved as domain objects get + * switched between active and inactive, i.e., when the user tabs to a different domain object and + * back. Whatever object is returned by {@link #getTransientState()} state will be fed back to this + * plugin via {@link #restoreTransientState(Object)} after the tool state is switched back to the + * domain object that was active. + * + * @param the type used to record the state, usually a Java {@code record} + */ +public interface PluginWithTransientState { + /** + * Returns an object containing the plugins state. + * + * @return Object to be return in the restoreTransientState() method. + */ + public T getTransientState(); + + /** + * Provides the transient state object that was returned in the corresponding + * {@link #getTransientState()} call. + * + * @param state the state object that was generated by this plugin's getTransientState() method. + */ + public void restoreTransientState(T state); + + /** + * Already required/provided by {@link Plugin} + * + * @return true if the plugin has been disposed + */ + public boolean isDisposed(); +} diff --git a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/TransientToolState.java b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/TransientToolState.java index 2f63dc0df3..8f2a357216 100644 --- a/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/TransientToolState.java +++ b/Ghidra/Framework/Project/src/main/java/ghidra/framework/plugintool/util/TransientToolState.java @@ -1,13 +1,12 @@ /* ### * IP: GHIDRA - * REVIEWED: YES * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,45 +15,63 @@ */ package ghidra.framework.plugintool.util; -import ghidra.framework.plugintool.Plugin; - import java.util.*; +import ghidra.framework.plugintool.PluginWithTransientState; +import ghidra.framework.plugintool.Plugin; + public class TransientToolState { - private List states; + private List> states; + /** * Construct a TransientPluginState + * * @param plugins array of plugins to get transient state for */ public TransientToolState(List plugins) { - states = new ArrayList(); - Iterator it = plugins.iterator(); - while(it.hasNext()) { - Plugin plugin = it.next(); - Object state = plugin.getTransientState(); + states = new ArrayList<>(); + for (Plugin plugin : plugins) { + if (!(plugin instanceof PluginWithTransientState hasState)) { + continue; + } + PluginState state = PluginState.gather(hasState); + if (state == null) { + continue; + } if (state != null) { - states.add(new PluginState(plugin, state)); + states.add(state); } } - } - /** - * Restore the tool's state. - */ + } + + /** + * Restore the tool's state. + */ public void restoreTool() { - Iterator it = states.iterator(); - while(it.hasNext()) { - PluginState ps = it.next(); + Iterator> it = states.iterator(); + while (it.hasNext()) { + PluginState ps = it.next(); ps.restore(); } } - - private static class PluginState { - private Plugin plugin; - private Object state; - PluginState(Plugin p, Object state) { - this.plugin = p; + + private static class PluginState { + private PluginWithTransientState plugin; + private T state; + + PluginState(PluginWithTransientState plugin, T state) { + this.plugin = plugin; this.state = state; } + + static PluginState gather(PluginWithTransientState plugin) { + T state = plugin.getTransientState(); + if (state == null) { + return null; + } + return new PluginState<>(plugin, state); + } + void restore() { if (!plugin.isDisposed()) { plugin.restoreTransientState(state);