diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaFactoryTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaFactoryTest.java index 5c4c2048d6..220888f928 100644 --- a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaFactoryTest.java +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaFactoryTest.java @@ -15,8 +15,12 @@ */ package agent.frida.model.invm; -import agent.frida.model.AbstractModelForFridaFactoryTest; +import org.junit.experimental.categories.Category; +import agent.frida.model.AbstractModelForFridaFactoryTest; +import generic.test.category.NightlyCategory; + +@Category(NightlyCategory.class) // this may actually be an @PortSensitive test public class InVmModelForFridaFactoryTest extends AbstractModelForFridaFactoryTest { @Override public ModelHost modelHost() throws Throwable { diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaInterpreterTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaInterpreterTest.java index 17b36f4627..3b18537083 100644 --- a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaInterpreterTest.java +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaInterpreterTest.java @@ -17,11 +17,14 @@ package agent.frida.model.invm; import org.junit.Ignore; import org.junit.Test; +import org.junit.experimental.categories.Category; import agent.frida.model.AbstractModelForFridaInterpreterTest; +import generic.test.category.NightlyCategory; import ghidra.dbg.test.ProvidesTargetViaLaunchSpecimen; -public class InVmModelForFridaInterpreterTest extends AbstractModelForFridaInterpreterTest +@Category(NightlyCategory.class) // this may actually be an @PortSensitive test +public class InVmModelForFridaInterpreterTest extends AbstractModelForFridaInterpreterTest implements ProvidesTargetViaLaunchSpecimen { @Override public ModelHost modelHost() throws Throwable { @@ -61,4 +64,3 @@ public class InVmModelForFridaInterpreterTest extends AbstractModelForFridaInter } } - diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaMethodsTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaMethodsTest.java index 760d548974..20a862a90a 100644 --- a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaMethodsTest.java +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaMethodsTest.java @@ -15,8 +15,12 @@ */ package agent.frida.model.invm; -import agent.frida.model.AbstractModelForFridaMethodsTest; +import org.junit.experimental.categories.Category; +import agent.frida.model.AbstractModelForFridaMethodsTest; +import generic.test.category.NightlyCategory; + +@Category(NightlyCategory.class) // this may actually be an @PortSensitive test public class InVmModelForFridaMethodsTest extends AbstractModelForFridaMethodsTest { @Override public ModelHost modelHost() throws Throwable { diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootAttacherTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootAttacherTest.java index 8859c48c07..23e545f24b 100644 --- a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootAttacherTest.java +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootAttacherTest.java @@ -17,24 +17,27 @@ package agent.frida.model.invm; import org.junit.Ignore; import org.junit.Test; +import org.junit.experimental.categories.Category; import agent.frida.model.AbstractModelForFridaRootAttacherTest; +import generic.test.category.NightlyCategory; +@Category(NightlyCategory.class) // this may actually be an @PortSensitive test public class InVmModelForFridaRootAttacherTest extends AbstractModelForFridaRootAttacherTest { @Override public ModelHost modelHost() throws Throwable { return new InVmFridaModelHost(); } - + // NB: These tests need debugger rights, which means either: // (1) on macos, codesigning the executables // (2) on linux, "sudo su; echo 0 > /proc/sys/kernel/yama/ptrace_scope" - + @Override @Ignore // test requires ability to attach by object & frida version requires pid @Test public void testAttachByObjBogusThrowsException() throws Throwable { super.testAttachByObjBogusThrowsException(); } - + } diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootLauncherTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootLauncherTest.java index 1e89fe2b39..1f101aaeea 100644 --- a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootLauncherTest.java +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootLauncherTest.java @@ -15,8 +15,12 @@ */ package agent.frida.model.invm; -import agent.frida.model.AbstractModelForFridaRootLauncherTest; +import org.junit.experimental.categories.Category; +import agent.frida.model.AbstractModelForFridaRootLauncherTest; +import generic.test.category.NightlyCategory; + +@Category(NightlyCategory.class) // this may actually be an @PortSensitive test public class InVmModelForFridaRootLauncherTest extends AbstractModelForFridaRootLauncherTest { @Override public ModelHost modelHost() throws Throwable { diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaScenarioStackTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaScenarioStackTest.java index 1b24b51b67..a922224ec1 100644 --- a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaScenarioStackTest.java +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaScenarioStackTest.java @@ -15,8 +15,12 @@ */ package agent.frida.model.invm; -import agent.frida.model.AbstractModelForFridaScenarioStackTest; +import org.junit.experimental.categories.Category; +import agent.frida.model.AbstractModelForFridaScenarioStackTest; +import generic.test.category.NightlyCategory; + +@Category(NightlyCategory.class) // this may actually be an @PortSensitive test public class InVmModelForFridaScenarioStackTest extends AbstractModelForFridaScenarioStackTest { @Override public ModelHost modelHost() throws Throwable { diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaX64RegistersTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaX64RegistersTest.java index b0534cd62f..c5152c2aca 100644 --- a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaX64RegistersTest.java +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaX64RegistersTest.java @@ -15,13 +15,17 @@ */ package agent.frida.model.invm; -import agent.frida.model.AbstractModelForFridaX64RegistersTest; +import org.junit.experimental.categories.Category; +import agent.frida.model.AbstractModelForFridaX64RegistersTest; +import generic.test.category.NightlyCategory; + +@Category(NightlyCategory.class) // this may actually be an @PortSensitive test public class InVmModelForFridaX64RegistersTest extends AbstractModelForFridaX64RegistersTest { @Override public ModelHost modelHost() throws Throwable { return new InVmFridaModelHost(); } - + } diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/EnumEditor.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/EnumEditor.htm index 75944bace4..e6490270c0 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/EnumEditor.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/EnumEditor.htm @@ -98,6 +98,17 @@ the Data Type Manager display is updated to reflect the new name in the tree.

+ + +

Show In Data Type Manager

+ +
+

Select the icon in the toolbar to have the editor's + data type be highlighted in the Data Type Manager's tree. +

+ +
Change the Sort Order

As with most tables in Ghidra, you can change the sort order of a column by diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm index 69c6be97ae..dbb850fea7 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/StructureEditor.htm @@ -102,7 +102,17 @@ data items in the program will have changed due to the apply.

-

Closing the Editor

+ +

Show In Data Type Manager

+ +
+

Select the icon in the toolbar to have the editor's + data type be highlighted in the Data Type Manager's tree. +

+ +
+ +

Closing the Editor

Select the Close dockable component icon in diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png index 4eceff2d80..ece40a8b8a 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png and b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditor.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditor.png index 194fbb1ffd..61e5901d18 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditor.png and b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditor.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorPacked.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorPacked.png index ddaf0e6785..2b6ac798c4 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorPacked.png and b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorPacked.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorWithFlexArray.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorWithFlexArray.png index 8fcb4e4528..81cecbcb7c 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorWithFlexArray.png and b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/StructureEditorWithFlexArray.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditor.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditor.png index 02c3152875..d78f214182 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditor.png and b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditor.png differ diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditorPacked.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditorPacked.png index 7fc56dced9..66b0cecfdb 100644 Binary files a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditorPacked.png and b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/UnionEditorPacked.png differ diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ApplyAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ApplyAction.java index 65948da6a4..cbae61492c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ApplyAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ApplyAction.java @@ -23,7 +23,7 @@ import ghidra.program.model.data.InvalidDataTypeException; import resources.ResourceManager; /** - * ApplyAction is an action for applying editor changes. + * ApplyAction is an action for applying editor changes. */ public class ApplyAction extends CompositeEditorTableAction { @@ -48,11 +48,8 @@ public class ApplyAction extends CompositeEditorTableAction { try { model.apply(); } - catch (EmptyCompositeException e1) { - model.setStatus(e1.getMessage(), true); - } - catch (InvalidDataTypeException e1) { - model.setStatus(e1.getMessage(), true); + catch (EmptyCompositeException | InvalidDataTypeException e) { + model.setStatus(e.getMessage(), true); } requestTableFocus(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorTableAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorTableAction.java index 92c209d453..3d8ee05b99 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorTableAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/CompositeEditorTableAction.java @@ -45,7 +45,7 @@ abstract public class CompositeEditorTableAction extends DockingAction implement public static final String EDIT_ACTION_PREFIX = "Editor: "; public CompositeEditorTableAction(CompositeEditorProvider provider, String name, String group, - String[] popupPath, String[] menuPath, ImageIcon icon) { + String[] popupPath, String[] menuPath, Icon icon) { super(name, provider.plugin.getName(), KeyBindingType.SHARED); this.provider = provider; model = provider.getModel(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorProvider.java index 061fad90ed..1986a759cf 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/EditorProvider.java @@ -15,53 +15,55 @@ */ package ghidra.app.plugin.core.compositeeditor; -import ghidra.program.model.data.*; import docking.ComponentProvider; +import ghidra.program.model.data.*; /** * Interface implemented by data type editors. - * - * */ public interface EditorProvider { /** * Get the name of this editor. + * @return the name of this editor */ public String getName(); /** * Get the pathname of the data type being edited. + * @return the pathname of the data type being edited */ public DataTypePath getDtPath(); /** * Get the component provider for this editor. + * @return the component provider for this editor */ public ComponentProvider getComponentProvider(); /** * Get the datatype manager associated with this editor. + * @return the datatype manager associated with this editor */ public DataTypeManager getDataTypeManager(); /** - * Notification that the data type manager domain object (program or data type archive) was restored. + * Notification that the data type manager domain object (program or data type archive) was + * restored. * @param domainObject the program or data type archive that was restored. */ public void domainObjectRestored(DataTypeManagerDomainObject domainObject); /** - * Return whether this editor is editing the data type with the given - * path. + * Return whether this editor is editing the data type with the given path. * @param dtPath path of a data type * @return true if the data type for the pathname is being edited */ public boolean isEditing(DataTypePath dtPath); /** - * Add an editor listener that will be notified when the edit window is - * closed. + * Add an editor listener that will be notified when the edit window is closed. + * @param listener the listener */ public void addEditorListener(EditorListener listener); @@ -72,6 +74,7 @@ public interface EditorProvider { /** * Returns whether changes need to be saved. + * @return whether changes need to be saved */ public boolean needsSave(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/FindReferencesToField.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/FindReferencesToField.java index b830d1e819..42f5a3c728 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/FindReferencesToField.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/FindReferencesToField.java @@ -15,16 +15,13 @@ */ package ghidra.app.plugin.core.compositeeditor; -import javax.swing.SwingUtilities; - import docking.ActionContext; import docking.action.MenuData; import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService; import ghidra.app.util.HelpTopics; import ghidra.program.model.data.Composite; import ghidra.program.model.data.DataTypeComponent; -import ghidra.util.HelpLocation; -import ghidra.util.Msg; +import ghidra.util.*; /** * An action to show references to the field in the currently selected editor row @@ -56,8 +53,7 @@ public class FindReferencesToField extends CompositeEditorTableAction { String fieldName = getFieldName(); Composite composite = model.getOriginalComposite(); - SwingUtilities.invokeLater( - () -> service.findAndDisplayAppliedDataTypeAddresses(composite, fieldName)); + Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(composite, fieldName)); } private String getFieldName() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowDataTypeInTreeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowDataTypeInTreeAction.java new file mode 100644 index 0000000000..4fe4b8b260 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/ShowDataTypeInTreeAction.java @@ -0,0 +1,60 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.compositeeditor; + +import javax.swing.Icon; + +import docking.ActionContext; +import docking.action.ToolBarData; +import ghidra.app.services.DataTypeManagerService; +import ghidra.program.model.data.*; +import resources.ResourceManager; + +/** + * Shows the editor's data type in the UI using the {@link DataTypeManagerService}. + */ +public class ShowDataTypeInTreeAction extends CompositeEditorTableAction { + + // This action should go after the row-based actions, which have this group: + // 3_COMPONENT_EDITOR_ACTION + private static final String TOOLBAR_GROUP = "4_COMPONENT_EDITOR_ACTION"; + private static final Icon ICON = ResourceManager.loadImage("images/go-home.png"); + + public ShowDataTypeInTreeAction(CompositeEditorProvider provider) { + super(provider, "Show In Data Type Manager", TOOLBAR_GROUP, null /*popupPath*/, + null /*menuPath*/, ICON); + + setToolBarData( + new ToolBarData(ResourceManager.loadImage("images/go-home.png"), TOOLBAR_GROUP)); + } + + @Override + public void actionPerformed(ActionContext context) { + DataTypeManagerService dtmService = tool.getService(DataTypeManagerService.class); + DataTypeManager dtm = provider.getDataTypeManager(); + DataTypePath path = provider.getDtPath(); + DataType dt = dtm.getDataType(path); + dtmService.setDataTypeSelected(dt); + } + + @Override + public void adjustEnablement() { + DataTypeManager dtm = provider.getDataTypeManager(); + DataTypePath path = provider.getDtPath(); + DataType dt = dtm.getDataType(path); + setEnabled(dt != null); + } +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java index a3eafc0c3f..43b2e35865 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/StructureEditorProvider.java @@ -55,24 +55,26 @@ public class StructureEditorProvider extends CompositeEditorProvider { return new CompositeEditorTableAction[] { new ApplyAction(this), // new ToggleLockAction(this), - new InsertUndefinedAction(this), - new MoveUpAction(this), + new InsertUndefinedAction(this), + new MoveUpAction(this), new MoveDownAction(this), - new ClearAction(this), - new DuplicateAction(this), + new ClearAction(this), + new DuplicateAction(this), new DuplicateMultipleAction(this), - new DeleteAction(this), - new PointerAction(this), + new DeleteAction(this), + new PointerAction(this), new ArrayAction(this), new FindReferencesToField(this), new UnpackageAction(this), - new EditComponentAction(this), - new EditFieldAction(this), + new EditComponentAction(this), + new EditFieldAction(this), new HexNumbersAction(this), new CreateInternalStructureAction(this), new ShowComponentPathAction(this), new AddBitFieldAction(this), new EditBitFieldAction(this), + new ShowDataTypeInTreeAction(this), + // new ViewBitFieldAction(this) }; //@formatter:on diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProvider.java index d76688f114..3ac09cc3c9 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/compositeeditor/UnionEditorProvider.java @@ -51,21 +51,22 @@ public class UnionEditorProvider extends CompositeEditorProvider { @Override protected CompositeEditorTableAction[] createActions() { //@formatter:off - return new CompositeEditorTableAction[] { - new ApplyAction(this), + return new CompositeEditorTableAction[] { + new ApplyAction(this), new MoveUpAction(this), - new MoveDownAction(this), - new DuplicateAction(this), + new MoveDownAction(this), + new DuplicateAction(this), new DuplicateMultipleAction(this), - new DeleteAction(this), - new PointerAction(this), + new DeleteAction(this), + new PointerAction(this), new ArrayAction(this), - new ShowComponentPathAction(this), + new ShowComponentPathAction(this), new EditComponentAction(this), - new EditFieldAction(this), + new EditFieldAction(this), new HexNumbersAction(this), new AddBitFieldAction(this), - new EditBitFieldAction(this) + new EditBitFieldAction(this), + new ShowDataTypeInTreeAction(this) }; //@formatter:on } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java index 797304dd7b..b4b2713e61 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeManagerPlugin.java @@ -38,7 +38,9 @@ import generic.util.Path; import ghidra.app.CorePluginPackage; import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.ProgramPlugin; -import ghidra.app.plugin.core.datamgr.actions.*; +import ghidra.app.plugin.core.datamgr.actions.RecentlyOpenedArchiveAction; +import ghidra.app.plugin.core.datamgr.actions.UpdateSourceArchiveNamesAction; +import ghidra.app.plugin.core.datamgr.actions.associate.*; import ghidra.app.plugin.core.datamgr.archive.*; import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager; import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; @@ -60,8 +62,7 @@ import ghidra.program.model.address.AddressSetView; import ghidra.program.model.data.*; import ghidra.program.model.listing.DataTypeArchive; import ghidra.program.model.listing.Program; -import ghidra.util.HelpLocation; -import ghidra.util.Msg; +import ghidra.util.*; import ghidra.util.datastruct.LRUMap; import ghidra.util.task.TaskLauncher; @@ -570,6 +571,10 @@ public class DataTypeManagerPlugin extends ProgramPlugin return dataTypeManagerHandler.openArchive(archiveName); } + public List getAllArchives() { + return dataTypeManagerHandler.getAllArchives(); + } + public void openProjectDataTypeArchive() { if (openDialog == null) { ActionListener listener = ev -> { @@ -613,7 +618,9 @@ public class DataTypeManagerPlugin extends ProgramPlugin @Override public void setDataTypeSelected(DataType dataType) { if (provider.isVisible()) { - provider.setDataTypeSelected(dataType); + // this is a service method, ensure it is on the Swing thread, since it interacts with + // Swing components + Swing.runIfSwingOrRunLater(() -> provider.setDataTypeSelected(dataType)); } } @@ -725,7 +732,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin return null; } DataTypesActionContext dtContext = (DataTypesActionContext) context; - GTreeNode selectedNode = dtContext.getSelectedNode(); + GTreeNode selectedNode = dtContext.getClickedNode(); if (!(selectedNode instanceof ArchiveNode)) { return null; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java index 3e6a3a15b1..97f6561be6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypeSynchronizer.java @@ -48,9 +48,9 @@ public class DataTypeSynchronizer { private int localTransactionID; /** - * Creates a DataTypeSynchronizer to be used for synchronizing data types between a program + * Creates a DataTypeSynchronizer to be used for synchronizing data types between a program * and an archive. - * @param dataTypeManagerHandler the handler that manages all the open data type managers + * @param dataTypeManagerHandler the handler that manages all the open data type managers * whether built-in, program, project data type archive or file data type archive. * @param dataTypeManager the program data type manager. * @param source the data type source archive information indicating the associated archive for @@ -142,35 +142,38 @@ public class DataTypeSynchronizer { } /** - * Commits a single program data type's changes to the associated source data type in the archive. - * @param refDT the program data type - * @return true if the commit succeeds. + * Commits a single program data type's changes to the associated source data type in the + * archive. + * @param dtmHandler the handler that manages data types + * @param dt the program data type + * @return true if the commit succeeds */ - public static boolean commit(DataTypeManagerHandler dtmHandler, DataType refDT) { - SourceArchive sourceArchive = refDT.getSourceArchive(); + public static boolean commit(DataTypeManagerHandler dtmHandler, DataType dt) { + SourceArchive sourceArchive = dt.getSourceArchive(); DataTypeManager sourceDTM = dtmHandler.getDataTypeManager(sourceArchive); if (sourceDTM == null) { return false; } - commit(sourceDTM, refDT); + commit(sourceDTM, dt); return true; } /** * Updates a single data type in the program to match the associated source data type from the * archive. - * @param dataType the program data type - * @return true if the update succeeds. + * @param dtmHandler the handler that manages data types + * @param dt the data type + * @return true if the update succeeds */ - public static boolean update(DataTypeManagerHandler dtmHandler, DataType refDT) { - DataTypeManager dataTypeManager = refDT.getDataTypeManager(); - SourceArchive sourceArchive = refDT.getSourceArchive(); - DataTypeManager sourceDTM = dtmHandler.getDataTypeManager(sourceArchive); - if (dataTypeManager == null || sourceDTM == null) { + public static boolean update(DataTypeManagerHandler dtmHandler, DataType dt) { + DataTypeManager dataTypeManager = dt.getDataTypeManager(); + SourceArchive sourceArchive = dt.getSourceArchive(); + DataTypeManager sourceDtm = dtmHandler.getDataTypeManager(sourceArchive); + if (dataTypeManager == null || sourceDtm == null) { return false; } - DataType sourceDT = sourceDTM.getDataType(sourceArchive, refDT.getUniversalID()); - update(dataTypeManager, sourceDT); + DataType sourceDt = sourceDtm.getDataType(sourceArchive, dt.getUniversalID()); + update(dataTypeManager, sourceDt); return true; } @@ -216,7 +219,7 @@ public class DataTypeSynchronizer { } /** - * If the indicated data type is associated with a source archive, this will remove the + * If the indicated data type is associated with a source archive, this will remove the * association. * @param dataType the data type to be disassociated from a source archive. */ @@ -343,7 +346,7 @@ public class DataTypeSynchronizer { String htmlContent = diffs[0].getHTMLContentString(); String otherContent = diffs[1].getHTMLContentString(); - // this string allows us to force both tables to be the same width, which is + // this string allows us to force both tables to be the same width, which is // aesthetically pleasing String spacerString = createHTMLSpacerString(htmlContent, otherContent); StringBuilder buffy = new StringBuilder(); @@ -355,8 +358,9 @@ public class DataTypeSynchronizer { buffy.append(""); buffy.append(""); - buffy.append("").append(HTMLUtilities.escapeHTML(dataTypeManager.getName())).append( - "


"); + buffy.append("") + .append(HTMLUtilities.escapeHTML(dataTypeManager.getName())) + .append("
"); buffy.append(htmlContent); // horizontal spacer below the inner table in order to force a minimum width @@ -368,8 +372,9 @@ public class DataTypeSynchronizer { buffy.append(""); buffy.append(""); - buffy.append("").append(HTMLUtilities.escapeHTML(sourceArchive.getName())).append( - "
"); + buffy.append("") + .append(HTMLUtilities.escapeHTML(sourceArchive.getName())) + .append("
"); buffy.append(otherContent); @@ -391,12 +396,12 @@ public class DataTypeSynchronizer { return ToolTipUtils.getHTMLRepresentation(sourceDT); } - /** + /** * Compares the two HTML strings to find the widest *rendered* text and then creates * an HTML string of spaces that is wide enough to represent that width. */ private static String createHTMLSpacerString(String htmlContent, String otherHTMLContent) { - // unfortunately, to get the displayed widths, we have to have rendered content, which + // unfortunately, to get the displayed widths, we have to have rendered content, which // is what the JLabels below are doing for us JLabel label1 = new GDHtmlLabel("" + htmlContent); JLabel label2 = new GDHtmlLabel("" + otherHTMLContent); @@ -446,9 +451,9 @@ public class DataTypeSynchronizer { } /** - * Adjusts the data type and source archive info for an associated source archive if its sync - * state is incorrect. It makes sure that a data type that is the same as the associated - * archive one is in-sync. It also makes sure that a data type that differs from the archive + * Adjusts the data type and source archive info for an associated source archive if its sync + * state is incorrect. It makes sure that a data type that is the same as the associated + * archive one is in-sync. It also makes sure that a data type that differs from the archive * one can be committed or updated. */ public void reSyncDataTypes() { @@ -458,8 +463,8 @@ public class DataTypeSynchronizer { return; } - int transactionID = dataTypeManager.startTransaction( - "re-sync '" + sourceArchive.getName() + "' data types"); + int transactionID = dataTypeManager + .startTransaction("re-sync '" + sourceArchive.getName() + "' data types"); try { reSyncOutOfSyncInTimeOnlyDataTypes(); fixSyncForDifferingDataTypes(); @@ -498,7 +503,7 @@ public class DataTypeSynchronizer { /** * This method is to correct a problem where a data type ends up differing from its associated * data type in the archive, but its timestamp information indicates that it is in sync. - * It changes the timestamp info on the data type and the info about the source archive so + * It changes the timestamp info on the data type and the info about the source archive so * the user will be able to commit/update the data type to correctly put it back in sync. */ private void fixSyncForDifferingDataTypes() { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesActionContext.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesActionContext.java index 20553829ec..bb14af45ef 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesActionContext.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesActionContext.java @@ -20,13 +20,15 @@ import java.util.List; import javax.swing.tree.TreePath; +import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; import ghidra.app.context.ProgramActionContext; +import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive; import ghidra.app.plugin.core.datamgr.archive.ProjectArchive; -import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; -import ghidra.app.plugin.core.datamgr.tree.ProjectArchiveNode; +import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.framework.main.datatable.DomainFileContext; import ghidra.framework.model.DomainFile; +import ghidra.program.model.data.*; import ghidra.program.model.listing.Program; public class DataTypesActionContext extends ProgramActionContext implements DomainFileContext { @@ -53,7 +55,7 @@ public class DataTypesActionContext extends ProgramActionContext implements Doma return isToolbarAction; } - public GTreeNode getSelectedNode() { + public GTreeNode getClickedNode() { return clickedNode; } @@ -85,4 +87,49 @@ public class DataTypesActionContext extends ProgramActionContext implements Doma return true; } + public List getSelectedNodes() { + Object contextObject = getContextObject(); + GTree gTree = (GTree) contextObject; + return gTree.getSelectedNodes(); + } + + public List getDisassociatableNodes() { + + Object contextObject = getContextObject(); + GTree gTree = (GTree) contextObject; + TreePath[] selectionPaths = gTree.getSelectionPaths(); + return getDisassociatableNodes(selectionPaths); + } + + private List getDisassociatableNodes(TreePath[] paths) { + + List nodes = new ArrayList<>(); + for (TreePath treePath : paths) { + DataTypeNode node = getDisassociatableNode(treePath); + if (node != null) { + nodes.add(node); + } + } + return nodes; + } + + private DataTypeNode getDisassociatableNode(TreePath path) { + GTreeNode node = (GTreeNode) path.getLastPathComponent(); + if (!(node instanceof DataTypeNode)) { + return null; + } + + DataTypeNode dataTypeNode = (DataTypeNode) node; + DataType dataType = dataTypeNode.getDataType(); + DataTypeManager dataTypeManager = dataType.getDataTypeManager(); + SourceArchive sourceArchive = dataType.getSourceArchive(); + if (sourceArchive == null || dataTypeManager == null || + sourceArchive.equals(BuiltInSourceArchive.INSTANCE) || + sourceArchive.getSourceArchiveID().equals(dataTypeManager.getUniversalID())) { + + return null; + } + return dataTypeNode; + } + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java index 40f56b32f5..8d6445e231 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/DataTypesProvider.java @@ -38,6 +38,7 @@ import docking.widgets.textpane.GHtmlTextPane; import docking.widgets.tree.*; import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin; import ghidra.app.plugin.core.datamgr.actions.*; +import ghidra.app.plugin.core.datamgr.actions.associate.*; import ghidra.app.plugin.core.datamgr.archive.*; import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; @@ -220,6 +221,7 @@ public class DataTypesProvider extends ComponentProviderAdapter { // key binding only addLocalAction(new ClearCutAction(plugin)); // Common + addLocalAction(new AssociateDataTypeAction(plugin)); addLocalAction(new CommitSingleDataTypeAction(plugin)); addLocalAction(new UpdateSingleDataTypeAction(plugin)); addLocalAction(new RevertDataTypeAction(plugin)); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PasteAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PasteAction.java index 2a74d5f445..05e584ba1a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PasteAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/PasteAction.java @@ -4,9 +4,9 @@ * 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. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/AssociateDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/AssociateDataTypeAction.java new file mode 100644 index 0000000000..3e6a4f8b51 --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/AssociateDataTypeAction.java @@ -0,0 +1,322 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.datamgr.actions.associate; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.util.List; +import java.util.stream.Collectors; + +import javax.swing.*; + +import org.apache.commons.lang3.StringUtils; + +import docking.*; +import docking.action.DockingAction; +import docking.action.MenuData; +import docking.widgets.OptionDialog; +import docking.widgets.combobox.GhidraComboBox; +import docking.widgets.label.GLabel; +import docking.widgets.tree.GTreeNode; +import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; +import ghidra.app.plugin.core.datamgr.DataTypesActionContext; +import ghidra.app.plugin.core.datamgr.archive.*; +import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; +import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; +import ghidra.app.plugin.core.datamgr.util.DataTypeTreeCopyMoveTask; +import ghidra.app.plugin.core.datamgr.util.DataTypeTreeCopyMoveTask.ActionType; +import ghidra.program.model.data.*; +import ghidra.util.Msg; +import ghidra.util.layout.PairLayout; +import ghidra.util.task.TaskLauncher; + +/** + * Allows the user to associate the selected action with a source archive. An associate data type + * allows users to push changes to the source archive and to pull updates from the source archive. + */ +public class AssociateDataTypeAction extends DockingAction { + + private DataTypeManagerPlugin plugin; + + public AssociateDataTypeAction(DataTypeManagerPlugin plugin) { + super("Associate With Archive", plugin.getName()); + this.plugin = plugin; + + setPopupMenuData(new MenuData(new String[] { "Associate With Archive" }, null, "Sync")); + } + + @Override + public boolean isEnabledForContext(ActionContext context) { + if (!(context instanceof DataTypesActionContext)) { + return false; + } + + return hasOnlyDtNodes(((DataTypesActionContext) context).getSelectedNodes()); + } + + private boolean hasOnlyDtNodes(List nodes) { + if (nodes.isEmpty()) { + return false; + } + for (GTreeNode node : nodes) { + if (!(node instanceof DataTypeNode)) { + return false; + } + } + return true; + } + + private boolean isAlreadyAssociated(DataTypesActionContext dtContext) { + + List nodes = dtContext.getDisassociatableNodes(); + return !nodes.isEmpty(); + } + + private boolean hasSingleModifiableSourceArchive(List nodes) { + + Archive sourceArchive = null; + for (GTreeNode node : nodes) { + Archive archive = findArchive(node); + if (sourceArchive == null) { + sourceArchive = archive; + continue; + } + + if (sourceArchive != archive) { + return false; + } + } + + if (sourceArchive != null && sourceArchive.isModifiable()) { + return true; + } + + return false; + } + + private static Archive findArchive(GTreeNode node) { + while (node != null) { + if (node instanceof ArchiveNode) { + return ((ArchiveNode) node).getArchive(); + } + node = node.getParent(); + } + return null; + } + + private List getDestinationArchives() { + + List archives = plugin.getAllArchives(); + List sourceArchives = archives.stream() + .filter(a -> !(a instanceof ProgramArchive)) + .filter(a -> !(a instanceof BuiltInArchive)) + .sorted((a1, a2) -> a1.getName().compareToIgnoreCase(a2.getName())) + .collect(Collectors.toList()); + + return sourceArchives; + } + + @Override + public void actionPerformed(ActionContext context) { + + List nodes = ((DataTypesActionContext) context).getSelectedNodes(); + + if (!hasSingleModifiableSourceArchive(nodes)) { + Msg.showInfo(this, getProviderComponent(), "Multiple Source Archives", + "The currently selected nodes are from multiple archives.\n" + + "Please select only nodes from a single archvie."); + return; + } + + if (isAlreadyAssociated((DataTypesActionContext) context)) { + Msg.showInfo(this, getProviderComponent(), "Already Associated", + "One or more of the currently selected nodes are already associated\n" + + "with a source archive."); + return; + } + + List archives = getDestinationArchives(); + if (archives.isEmpty()) { + Msg.showInfo(this, getProviderComponent(), "No Source Archives Open", + "No source archives open. Please open the desired source archive."); + return; + } + + ChooseArchiveDialog dialog = new ChooseArchiveDialog(archives); + dialog.show(); + if (dialog.isCancelled()) { + return; + } + + Archive destinationArchive = dialog.getArchive(); + Category destinationCategory = dialog.getCategory(); + + DataTypeTreeCopyMoveTask task = + new DataTypeTreeCopyMoveTask(destinationArchive, destinationCategory, nodes, + ActionType.COPY, plugin.getProvider().getGTree(), plugin.getConflictHandler()); + task.setPromptToAssociateTypes(false); // do not prompt the user; they have already decided + TaskLauncher.launch(task); + } + + private JComponent getProviderComponent() { + return plugin.getProvider().getComponent(); + } + + private class ChooseArchiveDialog extends DialogComponentProvider { + + private Category category; + private Archive archive; + + // default to true to handle the case the user presses Escape or presses the x button + private boolean isCancelled = true; + + private GhidraComboBox archivesBox = new GhidraComboBox<>(); + private JTextField categoryField = new JTextField(20); + + ChooseArchiveDialog(List archives) { + super("Choose New Source Archive", true); + + addWorkPanel(buildWorkPanel()); + + archivesBox.addToModel(archives); + categoryField.setText("/"); + + addOKButton(); + addCancelButton(); + } + + private JComponent buildWorkPanel() { + + archivesBox.setRenderer(new DefaultListCellRenderer() { + + @Override + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + + JLabel renderer = (JLabel) super.getListCellRendererComponent(list, value, + index, isSelected, cellHasFocus); + Archive a = (Archive) value; + renderer.setText(a.getName()); + return renderer; + } + + }); + + JPanel panel = new JPanel(new BorderLayout()); + + JPanel archivePanel = new JPanel(new PairLayout()); + archivePanel.add(new GLabel("New Source Archive: ")); + archivePanel.add(archivesBox); + + JPanel categoryPanel = new JPanel(new PairLayout()); + categoryPanel.add(new GLabel("Destination Category: ")); + categoryPanel.add(categoryField); + + panel.add(archivePanel, BorderLayout.NORTH); + panel.add(categoryPanel, BorderLayout.SOUTH); + + return panel; + } + + @Override + protected void okCallback() { + + clearStatusText(); + + archive = (Archive) archivesBox.getSelectedItem(); + if (archive == null) { + setStatusText("Please choose an archive"); + return; + } + + if (!archive.isModifiable()) { + setStatusText( + "Archive is not modifiable. You must first open this archive for edit."); + return; + } + + if (!updateCategory()) { + return; + } + + isCancelled = false; + close(); + } + + private boolean updateCategory() { + + String categoryText = categoryField.getText(); + if (StringUtils.isBlank(categoryText)) { + setStatusText("Category must be specified. Use '/' for the root."); + return false; + } + + DataTypeManager dtm = archive.getDataTypeManager(); + CategoryPath categoryPath = new CategoryPath(categoryText); + category = dtm.getCategory(categoryPath); + if (category != null) { + return true; + } + + int choice = OptionDialog.showYesNoDialog(null, "Create Category?", + "Category '" + categoryText + "' does not exist. Create it now?"); + if (choice != OptionDialog.YES_OPTION) { + setStatusText("Category does not exist"); + return false; + } + + boolean noErrors = false; + int tx = dtm.startTransaction("Create Category"); + try { + category = dtm.createCategory(categoryPath); + noErrors = true; + } + finally { + dtm.endTransaction(tx, noErrors); + } + + if (category == null) { + setStatusText("Unable to create category"); + return false; + } + return true; + } + + @Override + protected void cancelCallback() { + super.cancelCallback(); + } + + boolean isCancelled() { + return isCancelled; + } + + void show() { + JComponent parent = getProviderComponent(); + DockingWindowManager.showDialog(parent, this); + } + + Archive getArchive() { + return archive; + } + + Category getCategory() { + return category; + } + + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CommitAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitAction.java similarity index 89% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CommitAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitAction.java index a1834dd7d5..821b306751 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CommitAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitAction.java @@ -13,8 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.datamgr.actions; +package ghidra.app.plugin.core.datamgr.actions.associate; +import java.util.List; + +import docking.action.MenuData; import ghidra.app.plugin.core.datamgr.*; import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; @@ -22,17 +25,13 @@ import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.SourceArchive; import ghidra.util.HelpLocation; -import java.util.List; - -import docking.action.MenuData; - public class CommitAction extends SyncAction { - public static final String MENU_NAME = "Commit Datatypes To"; + public static final String MENU_NAME = "Commit Data Types To"; - public CommitAction(DataTypeManagerPlugin plugin, - DataTypeManagerHandler dataTypeManagerHandler, DataTypeManager dtm, - ArchiveNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) { + public CommitAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler, + DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive, + boolean isEnabled) { super("Commit Changes To Archive", plugin, dataTypeManagerHandler, dtm, archiveNode, sourceArchive, isEnabled); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CommitSingleDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitSingleDataTypeAction.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CommitSingleDataTypeAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitSingleDataTypeAction.java index 309bc10f7c..63b169a023 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/CommitSingleDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/CommitSingleDataTypeAction.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.datamgr.actions; +package ghidra.app.plugin.core.datamgr.actions.associate; import javax.swing.ImageIcon; import javax.swing.tree.TreePath; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DisassociateAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateAction.java similarity index 97% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DisassociateAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateAction.java index 4076e06145..caa919cda8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DisassociateAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateAction.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.datamgr.actions; +package ghidra.app.plugin.core.datamgr.actions.associate; import java.util.*; @@ -35,7 +35,7 @@ import ghidra.util.exception.CancelledException; import ghidra.util.task.*; public class DisassociateAction extends DockingAction { - public static final String MENU_NAME = "Disassociate Datatypes From"; + public static final String MENU_NAME = "Disassociate Data Types From"; private final SourceArchive sourceArchive; private final DataTypeManager dtm; @@ -96,12 +96,12 @@ public class DisassociateAction extends DockingAction { } //@formatter:off - MonitoredRunnable r = + MonitoredRunnable r = monitor -> doDisassociate(synchronizer, typesToDisassociate, allAssociatedTypes, monitor); new TaskBuilder("Disassociate From Archive", r) .setStatusTextAlignment(SwingConstants.LEADING) .launchModal() - ; + ; //@formatter:on } @@ -110,10 +110,10 @@ public class DisassociateAction extends DockingAction { TaskMonitor monitor) { // - // Note: we collapse the node before performing this work because there is a + // Note: we collapse the node before performing this work because there is a // potential for a large number of events to be generated. Further, if the // given archive node has many children (like 10s of thousands), then the - // copious events generated herein could lock the UI. By closing the node, + // copious events generated herein could lock the UI. By closing the node, // the tree is not invalidating/validating its cache as a result of these // events. // diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DisassociateDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateDataTypeAction.java similarity index 93% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DisassociateDataTypeAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateDataTypeAction.java index 8f29c558d7..677cf7a780 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/DisassociateDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/DisassociateDataTypeAction.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.datamgr.actions; +package ghidra.app.plugin.core.datamgr.actions.associate; import java.util.*; import java.util.Map.Entry; @@ -28,7 +28,8 @@ import docking.action.MenuData; import docking.widgets.OptionDialog; import docking.widgets.tree.*; import ghidra.app.plugin.core.datamgr.*; -import ghidra.app.plugin.core.datamgr.archive.*; +import ghidra.app.plugin.core.datamgr.archive.BuiltInSourceArchive; +import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; @@ -56,10 +57,8 @@ public class DisassociateDataTypeAction extends DockingAction { return false; } - Object contextObject = context.getContextObject(); - GTree gTree = (GTree) contextObject; - TreePath[] selectionPaths = gTree.getSelectionPaths(); - List nodes = getDisassociatableNodes(selectionPaths); + DataTypesActionContext dtContext = (DataTypesActionContext) context; + List nodes = dtContext.getDisassociatableNodes(); return !nodes.isEmpty(); } @@ -127,11 +126,11 @@ public class DisassociateDataTypeAction extends DockingAction { } //@formatter:off - MonitoredRunnable r = + MonitoredRunnable r = monitor -> doDisassociate(nodes, monitor); new TaskBuilder("Disassociate From Archive", r) .setStatusTextAlignment(SwingConstants.LEADING) - .launchModal(); + .launchModal(); //@formatter:on } @@ -159,10 +158,10 @@ public class DisassociateDataTypeAction extends DockingAction { nodes.stream().map(node -> node.getDataType()).collect(Collectors.toList()); // - // Note: we collapse the node before performing this work because there is a + // Note: we collapse the node before performing this work because there is a // potential for a large number of events to be generated. Further, if the // given archive node has many children (like 10s of thousands), then the - // copious events generated herein could lock the UI. By closing the node, + // copious events generated herein could lock the UI. By closing the node, // the tree is not invalidating/validating its cache as a result of these // events. // @@ -189,7 +188,7 @@ public class DisassociateDataTypeAction extends DockingAction { monitor.initialize(dataTypes.size()); //@formatter:off - Map> managersToTypes = + Map> managersToTypes = dataTypes.stream() .collect( Collectors.groupingBy(dt -> dt.getDataTypeManager())) @@ -209,7 +208,7 @@ public class DisassociateDataTypeAction extends DockingAction { // we must process these by their source //@formatter:off - Map> sourceToTypes = + Map> sourceToTypes = dataTypes.stream() .collect( Collectors.groupingBy(dt -> dt.getSourceArchive())) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RevertAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertAction.java similarity index 85% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RevertAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertAction.java index 85494ab86f..7d10931cde 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RevertAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertAction.java @@ -13,8 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.datamgr.actions; +package ghidra.app.plugin.core.datamgr.actions.associate; +import java.util.List; + +import docking.action.MenuData; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datamgr.DataTypeSyncInfo; import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; @@ -23,19 +26,15 @@ import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.SourceArchive; import ghidra.util.HelpLocation; -import java.util.List; - -import docking.action.MenuData; - public class RevertAction extends SyncAction { - public static final String MENU_NAME = "Revert Datatypes From"; + public static final String MENU_NAME = "Revert Data Types From"; - public RevertAction(DataTypeManagerPlugin plugin, - DataTypeManagerHandler dataTypeManagerHandler, DataTypeManager dtm, - ArchiveNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) { + public RevertAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler, + DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive, + boolean isEnabled) { - super("Revert Datatype Changes", plugin, dataTypeManagerHandler, dtm, archiveNode, + super("Revert Data Type Changes", plugin, dataTypeManagerHandler, dtm, archiveNode, sourceArchive, isEnabled); setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() })); setHelpLocation(new HelpLocation(plugin.getName(), getHelpTopic())); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RevertDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertDataTypeAction.java similarity index 71% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RevertDataTypeAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertDataTypeAction.java index 5728508c7a..8367123c41 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/RevertDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/RevertDataTypeAction.java @@ -13,14 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.datamgr.actions; - -import ghidra.app.plugin.core.datamgr.*; -import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; -import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; -import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; -import ghidra.program.model.data.*; -import ghidra.util.Msg; +package ghidra.app.plugin.core.datamgr.actions.associate; import javax.swing.tree.TreePath; @@ -29,6 +22,12 @@ import docking.action.DockingAction; import docking.action.MenuData; import docking.widgets.tree.GTree; import docking.widgets.tree.GTreeNode; +import ghidra.app.plugin.core.datamgr.*; +import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; +import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; +import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; +import ghidra.program.model.data.*; +import ghidra.util.Msg; public class RevertDataTypeAction extends DockingAction { @@ -37,7 +36,7 @@ public class RevertDataTypeAction extends DockingAction { public RevertDataTypeAction(DataTypeManagerPlugin plugin) { super("Revert Data Type", plugin.getName()); this.plugin = plugin; - setPopupMenuData(new MenuData(new String[] { "Revert" }, "Sync")); + setPopupMenuData(new MenuData(new String[] { "Revert Changes" }, "Sync")); setEnabled(true); } @@ -68,8 +67,8 @@ public class RevertDataTypeAction extends DockingAction { case UNKNOWN: return false; case COMMIT: - return true; case CONFLICT: + return true; case IN_SYNC: case ORPHAN: case UPDATE: @@ -88,29 +87,26 @@ public class RevertDataTypeAction extends DockingAction { } GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent(); - if (node instanceof DataTypeNode) { - DataTypeNode dataTypeNode = (DataTypeNode) node; - DataType dataType = dataTypeNode.getDataType(); - DataTypeManager dtm = dataType.getDataTypeManager(); - DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); - SourceArchive sourceArchive = dataType.getSourceArchive(); - if (!dtm.isUpdatable()) { - DataTypeUtils.showUnmodifiableArchiveErrorMessage(gTree, "Revert Failed", dtm); - return; - } - DataTypeManager sourceDTM = handler.getDataTypeManager(sourceArchive); - if (sourceDTM == null) { - Msg.showInfo(getClass(), gTree, "Revert Failed", "Source Archive not open: " + - sourceArchive.getName()); - return; - } - plugin.revert(dataType); - - // Source archive data type manager was already checked for null above. - DataTypeSynchronizer synchronizer = - new DataTypeSynchronizer(handler, dtm, sourceArchive); - synchronizer.reSyncOutOfSyncInTimeOnlyDataTypes(); + DataTypeNode dataTypeNode = (DataTypeNode) node; + DataType dataType = dataTypeNode.getDataType(); + DataTypeManager dtm = dataType.getDataTypeManager(); + DataTypeManagerHandler handler = plugin.getDataTypeManagerHandler(); + SourceArchive sourceArchive = dataType.getSourceArchive(); + if (!dtm.isUpdatable()) { + DataTypeUtils.showUnmodifiableArchiveErrorMessage(gTree, "Revert Failed", dtm); + return; } + DataTypeManager sourceDTM = handler.getDataTypeManager(sourceArchive); + if (sourceDTM == null) { + Msg.showInfo(getClass(), gTree, "Revert Failed", + "Source Archive not open: " + sourceArchive.getName()); + return; + } + plugin.revert(dataType); + + // Source archive data type manager was already checked for null above. + DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, sourceArchive); + synchronizer.reSyncOutOfSyncInTimeOnlyDataTypes(); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SyncAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/SyncAction.java similarity index 97% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SyncAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/SyncAction.java index 043a0447a9..33cef5094d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SyncAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/SyncAction.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.datamgr.actions; +package ghidra.app.plugin.core.datamgr.actions.associate; import java.util.*; @@ -83,7 +83,6 @@ public abstract class SyncAction extends DockingAction implements Comparable outOfSyncInfos) { String status = getStatusMessage(outOfSyncInfos); Msg.showInfo(getClass(), plugin.getTool().getToolFrame(), "No Data Type Changes", - "No datatypes found to " + getOperationName() + " for archive \"" + archiveName + + "No data types found to " + getOperationName() + " for archive \"" + archiveName + "\".\n\n" + status); } @@ -279,7 +280,7 @@ public abstract class SyncAction extends DockingAction implements Comparable 0) { buf.append("\nNumber of UPDATES remaining: " + updateCount); } @@ -298,7 +299,7 @@ public abstract class SyncAction extends DockingAction implements Comparable outOfSynchInTimeOnlyList, boolean markArchiveSynchronized) { - int transactionID = dtm.startTransaction("auto sync datatypes"); + int transactionID = dtm.startTransaction("Auto-sync data types"); try { for (DataTypeSyncInfo dataTypeSyncInfo : outOfSynchInTimeOnlyList) { dataTypeSyncInfo.syncTimes(); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SyncRefreshAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/SyncRefreshAction.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SyncRefreshAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/SyncRefreshAction.java index 3d0da6815d..b677d48e7b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/SyncRefreshAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/SyncRefreshAction.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.datamgr.actions; +package ghidra.app.plugin.core.datamgr.actions.associate; import docking.ActionContext; import docking.action.DockingAction; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UpdateAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/UpdateAction.java similarity index 85% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UpdateAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/UpdateAction.java index 5f1ed5f9dd..8b3eeb1ace 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UpdateAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/UpdateAction.java @@ -13,8 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.datamgr.actions; +package ghidra.app.plugin.core.datamgr.actions.associate; +import java.util.List; + +import docking.action.MenuData; import ghidra.app.plugin.core.datamgr.*; import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; @@ -22,18 +25,14 @@ import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.SourceArchive; import ghidra.util.HelpLocation; -import java.util.List; - -import docking.action.MenuData; - public class UpdateAction extends SyncAction { - public static final String MENU_NAME = "Update Datatypes From"; + public static final String MENU_NAME = "Update Data Types From"; - public UpdateAction(DataTypeManagerPlugin plugin, - DataTypeManagerHandler dataTypeManagerHandler, DataTypeManager dtm, - ArchiveNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) { + public UpdateAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler, + DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive, + boolean isEnabled) { - super("Update Datatypes From Archive", plugin, dataTypeManagerHandler, dtm, archiveNode, + super("Update Data Types From Archive", plugin, dataTypeManagerHandler, dtm, archiveNode, sourceArchive, isEnabled); setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() })); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UpdateSingleDataTypeAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/UpdateSingleDataTypeAction.java similarity index 98% rename from Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UpdateSingleDataTypeAction.java rename to Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/UpdateSingleDataTypeAction.java index b7af91fb5b..a604a2e470 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/UpdateSingleDataTypeAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/actions/associate/UpdateSingleDataTypeAction.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ghidra.app.plugin.core.datamgr.actions; +package ghidra.app.plugin.core.datamgr.actions.associate; import javax.swing.ImageIcon; import javax.swing.tree.TreePath; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java index f005ca919b..5a65c0eaf7 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/archive/DataTypeManagerHandler.java @@ -4,9 +4,9 @@ * 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. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEditorProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEditorProvider.java index 2ca3cbf75c..e82d4a6388 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEditorProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEditorProvider.java @@ -34,6 +34,7 @@ import docking.widgets.OptionDialog; import ghidra.app.plugin.core.compositeeditor.EditorListener; import ghidra.app.plugin.core.compositeeditor.EditorProvider; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; +import ghidra.app.services.DataTypeManagerService; import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; @@ -81,7 +82,7 @@ public class EnumEditorProvider extends ComponentProviderAdapter private long originalEnumID = -1; /** - * Construct a new enum editor provider. + * Construct a new enum editor provider. * @param plugin owner of this provider * @param enumDT enum data type */ @@ -251,7 +252,7 @@ public class EnumEditorProvider extends ComponentProviderAdapter //================================================================================================== // Private Methods -//================================================================================================== +//================================================================================================== private void updateTitle(DataType dataType) { setTitle(getName() + " - " + getProviderSubTitle(dataType)); @@ -285,19 +286,28 @@ public class EnumEditorProvider extends ComponentProviderAdapter deleteAction = new EnumPluginAction("Delete Enum Value", e -> editorPanel.deleteSelectedEntries()); deleteAction.setEnabled(false); - deleteAction.setPopupMenuData( - new MenuData(new String[] { "Delete" }, DELETE_ICON, editGroup)); + deleteAction + .setPopupMenuData(new MenuData(new String[] { "Delete" }, DELETE_ICON, editGroup)); deleteAction.setToolBarData(new ToolBarData(DELETE_ICON, editGroup)); deleteAction.setDescription("Delete the selected enum entries"); applyAction = new EnumPluginAction("Apply Enum Changes", e -> applyChanges()); applyAction.setEnabled(false); - applyAction.setToolBarData(new ToolBarData(APPLY_ICON, "ApplyChanges")); + String firstGroup = "ApplyChanges"; + applyAction.setToolBarData(new ToolBarData(APPLY_ICON, firstGroup)); applyAction.setDescription("Apply changes to Enum"); + EnumPluginAction showEnumAction = + new EnumPluginAction("Show In Data Type Manager", e -> showDataEnumInTree()); + showEnumAction.setEnabled(true); + String thirdGroup = "FThirdGroup"; + showEnumAction.setToolBarData( + new ToolBarData(ResourceManager.loadImage("images/go-home.png"), thirdGroup)); + tool.addLocalAction(this, applyAction); tool.addLocalAction(this, addAction); tool.addLocalAction(this, deleteAction); + tool.addLocalAction(this, showEnumAction); } private boolean applyChanges() { @@ -338,6 +348,11 @@ public class EnumEditorProvider extends ComponentProviderAdapter return true; } + private void showDataEnumInTree() { + DataTypeManagerService dtmService = tool.getService(DataTypeManagerService.class); + dtmService.setDataTypeSelected(originalEnum); + } + /** * Checks to see if the new changes to the enum will affect equates based off of it. * @param editedEnum the enum to check for conflicts with @@ -467,9 +482,9 @@ public class EnumEditorProvider extends ComponentProviderAdapter /** * Prompts the user if the editor has unsaved changes. Saves the changes if * the user indicates to do so. - * @return CANCEL (0) if the user canceled; - * SAVE (1) if the user saved changes; - * NO_SAVE (2) if the user did not save changes or no save was required; + * @return CANCEL (0) if the user canceled; + * SAVE (1) if the user saved changes; + * NO_SAVE (2) if the user did not save changes or no save was required; * ERROR (3) if there was an error when the changes were applied. */ private int saveChangesForCloseEvent(boolean allowCancel) { @@ -502,7 +517,7 @@ public class EnumEditorProvider extends ComponentProviderAdapter //================================================================================================== // Inner Classes -//================================================================================================== +//================================================================================================== private class MyDataTypeManagerChangeListener extends DataTypeManagerChangeListenerAdapter { @@ -547,7 +562,7 @@ public class EnumEditorProvider extends ComponentProviderAdapter @Override public void categoryRemoved(DataTypeManager dtm, CategoryPath path) { - // should never get this callback, as we should first have gotten a + // should never get this callback, as we should first have gotten a // dataTypeRemoved(), which will dispose this editor } @@ -665,11 +680,11 @@ public class EnumEditorProvider extends ComponentProviderAdapter DataType dataType = dtm.getDataType(otherPath); if (dataType == null) { - // - // Unusual Code Alert!: + // + // Unusual Code Alert!: // Must have been deleted and we have not yet processed the event...return true // here to signal that the types are the same so that clients will continue the - // updating process. The types may not really be the same, but the fallout is + // updating process. The types may not really be the same, but the fallout is // only that there will be more updating than is necessary. // return true; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeDragNDropHandler.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeDragNDropHandler.java index 4da07d673f..4d76378819 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeDragNDropHandler.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/tree/DataTypeDragNDropHandler.java @@ -4,9 +4,9 @@ * 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. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java index 99ee1297c4..5f11b43d9d 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeTreeCopyMoveTask.java @@ -27,12 +27,13 @@ import ghidra.app.plugin.core.datamgr.archive.ProgramArchive; import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.program.model.data.*; import ghidra.util.*; -import ghidra.util.exception.*; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.DuplicateNameException; import ghidra.util.task.Task; import ghidra.util.task.TaskMonitor; /** - * Task for handling drop operations. + * Task for copying and moving data type nodes within the Data Types tree. */ public class DataTypeTreeCopyMoveTask extends Task { @@ -48,11 +49,11 @@ public class DataTypeTreeCopyMoveTask extends Task { } private DataTypeArchiveGTree gTree; - private CategoryNode destinationNode; - private List droppedNodes; + private Category destinationCategory; + private List copyMoveNodes; private Archive sourceArchive; private Archive destinationArchive; - + private boolean promptToAssociateTypes = true; private ActionType actionType; private DataTypeConflictHandler conflictHandler; private List errors = new ArrayList<>(); @@ -65,19 +66,26 @@ public class DataTypeTreeCopyMoveTask extends Task { public DataTypeTreeCopyMoveTask(CategoryNode destinationNode, List droppedNodeList, ActionType actionType, DataTypeArchiveGTree gTree, DataTypeConflictHandler conflictHandler) { + this(findArchive(destinationNode), destinationNode.getCategory(), droppedNodeList, + actionType, gTree, conflictHandler); + } + + public DataTypeTreeCopyMoveTask(Archive destinationArchive, Category destinationCategory, + List droppedNodeList, ActionType actionType, DataTypeArchiveGTree gTree, + DataTypeConflictHandler conflictHandler) { super("Drag/Drop", true, true, true); - this.destinationNode = destinationNode; - this.droppedNodes = droppedNodeList; + this.destinationCategory = destinationCategory; + this.copyMoveNodes = droppedNodeList; this.actionType = actionType; this.gTree = gTree; this.conflictHandler = conflictHandler; - this.destinationArchive = findArchive(destinationNode); + this.destinationArchive = destinationArchive; - GTreeNode firstNode = droppedNodes.get(0); + GTreeNode firstNode = copyMoveNodes.get(0); this.sourceArchive = findArchive(firstNode); } - private Archive findArchive(GTreeNode node) { + private static Archive findArchive(GTreeNode node) { while (node != null) { if (node instanceof ArchiveNode) { return ((ArchiveNode) node).getArchive(); @@ -87,10 +95,22 @@ public class DataTypeTreeCopyMoveTask extends Task { return null; } + /** + * Any types being newly copied/moved to a suitable archive are eligible for 'association', + * which means changes between the two archives will be tracked. True, the default, signals to + * prompt before associating types; false signals not to prompt the user, but to always + * associate types. + * + * @param prompt true to prompt; false to not prompt + */ + public void setPromptToAssociateTypes(boolean prompt) { + this.promptToAssociateTypes = prompt; + } + @Override public void run(TaskMonitor monitor) throws CancelledException { - int nodeCount = droppedNodes.size(); + int nodeCount = copyMoveNodes.size(); filterRedundantNodes(); if (checkForDifferentSourceArchives()) { @@ -143,10 +163,10 @@ public class DataTypeTreeCopyMoveTask extends Task { private boolean checkForDifferentSourceArchives() { - for (GTreeNode node : droppedNodes) { + for (GTreeNode node : copyMoveNodes) { if (sourceArchive != findArchive(node)) { Msg.showError(this, gTree, "Copy Failed", - "All dragged data types must be from the same archive!"); + "All data types must be from the same archive!"); return true; } } @@ -158,7 +178,7 @@ public class DataTypeTreeCopyMoveTask extends Task { DataTypeManager dtm = destinationArchive.getDataTypeManager(); int txId = dtm.startTransaction("Copy/Move Category/DataType"); try { - dragNodesToCategory(monitor); + copyOrMoveNodesToCategory(monitor); } finally { dtm.endTransaction(txId, true); @@ -187,13 +207,13 @@ public class DataTypeTreeCopyMoveTask extends Task { return; } - monitor.initialize(droppedNodes.size()); + monitor.initialize(copyMoveNodes.size()); SourceArchive destination = destinationArchive.getDataTypeManager().getLocalSourceArchive(); DataTypeManager dtm = sourceArchive.getDataTypeManager(); - int txId = dtm.startTransaction("Associate DataTypes"); + int txId = dtm.startTransaction("Associate Data Types"); try { - for (GTreeNode node : droppedNodes) { + for (GTreeNode node : copyMoveNodes) { monitor.checkCanceled(); if (node instanceof DataTypeNode) { @@ -215,6 +235,10 @@ public class DataTypeTreeCopyMoveTask extends Task { private boolean promptToAssociateTypes(TaskMonitor monitor) throws CancelledException { + if (!promptToAssociateTypes) { + return true; // do not prompt; always associate + } + if (!containsUnassociatedTypes(monitor)) { return false; // nothing to associate } @@ -230,8 +254,8 @@ public class DataTypeTreeCopyMoveTask extends Task { private boolean containsUnassociatedTypes(TaskMonitor monitor) throws CancelledException { monitor.setMessage("Checking for types to associate"); - monitor.initialize(droppedNodes.size()); - for (GTreeNode node : droppedNodes) { + monitor.initialize(copyMoveNodes.size()); + for (GTreeNode node : copyMoveNodes) { monitor.checkCanceled(); if (node instanceof DataTypeNode) { @@ -296,13 +320,13 @@ public class DataTypeTreeCopyMoveTask extends Task { } } - private void dragNodesToCategory(TaskMonitor monitor) { + private void copyOrMoveNodesToCategory(TaskMonitor monitor) { monitor.setMessage("Drag/Drop Categories/Data Types"); - monitor.initialize(droppedNodes.size()); + monitor.initialize(copyMoveNodes.size()); - Category toCategory = getCategory(destinationNode); - for (GTreeNode node : droppedNodes) { + Category toCategory = destinationCategory; + for (GTreeNode node : copyMoveNodes) { if (monitor.isCancelled()) { break; } @@ -360,10 +384,10 @@ public class DataTypeTreeCopyMoveTask extends Task { } } - private void renameAsCopy(Category destinationCategory, DataType dataType) { + private void renameAsCopy(Category toCategory, DataType dataType) { String dtName = dataType.getName(); String baseName = getBaseName(dtName); - String copyName = getNextCopyName(destinationCategory, baseName); + String copyName = getNextCopyName(toCategory, baseName); try { dataType.setName(copyName); } @@ -386,12 +410,12 @@ public class DataTypeTreeCopyMoveTask extends Task { return baseName; } - String getNextCopyName(Category destinationCategory, String baseName) { + String getNextCopyName(Category toCategory, String baseName) { String format = "Copy_%d_of_" + baseName; for (int i = 1; i < 100; i++) { String copyName = String.format(format, i); - if (destinationCategory.getDataType(copyName) == null) { + if (toCategory.getDataType(copyName) == null) { return copyName; } } @@ -400,14 +424,14 @@ public class DataTypeTreeCopyMoveTask extends Task { return String.format(format, System.currentTimeMillis()); } - private void moveNode(Category destinationCategory, GTreeNode node, TaskMonitor monitor) { + private void moveNode(Category toCategory, GTreeNode node, TaskMonitor monitor) { if (node instanceof DataTypeNode) { DataType dataType = ((DataTypeNode) node).getDataType(); - moveDataType(destinationCategory, dataType); + moveDataType(toCategory, dataType); } else if (node instanceof CategoryNode) { Category category = ((CategoryNode) node).getCategory(); - moveCategory(destinationCategory, category, monitor); + moveCategory(toCategory, category, monitor); } } @@ -457,17 +481,6 @@ public class DataTypeTreeCopyMoveTask extends Task { toCategory.copyCategory(category, conflictHandler, monitor); } - private Category getCategory(GTreeNode node) { - if (node instanceof ArchiveNode) { - return ((ArchiveNode) node).getArchive().getDataTypeManager().getRootCategory(); - } - if (node instanceof CategoryNode) { - return ((CategoryNode) node).getCategory(); - } - throw new AssertException( - "Expected node to be either an ArchiveNode or CategoryNode but was " + node.getClass()); - } - /** * Returns true if the given data type's source archive is the same as it's current data * type manager. This is false if copying a new type from the program to an @@ -483,14 +496,14 @@ public class DataTypeTreeCopyMoveTask extends Task { } private int askToAssociateDataTypes() { - return OptionDialog.showYesNoCancelDialog(gTree, "Associate DataTypes?", - "Do you want to associate local datatypes with the target archive?"); + return OptionDialog.showYesNoCancelDialog(gTree, "Associate Data Types?", + "Do you want to associate local data types with the target archive?"); } // filters out nodes with categories in their path private void filterRedundantNodes() { - Set nodeSet = new HashSet<>(droppedNodes); + Set nodeSet = new HashSet<>(copyMoveNodes); List filteredList = new ArrayList<>(); for (GTreeNode node : nodeSet) { @@ -499,7 +512,7 @@ public class DataTypeTreeCopyMoveTask extends Task { } } - droppedNodes = filteredList; + copyMoveNodes = filteredList; } private boolean containsAncestor(Set nodeSet, GTreeNode node) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtils.java index fa2a72fb2a..69a6462812 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/util/DataTypeUtils.java @@ -22,7 +22,6 @@ import java.util.List; import javax.swing.Icon; import javax.swing.ImageIcon; -import ghidra.app.services.DataTypeManagerService; import ghidra.app.services.DataTypeQueryService; import ghidra.program.model.data.*; import ghidra.program.model.data.Enum; @@ -150,6 +149,7 @@ public class DataTypeUtils { /** * Returns the root folder icon. + * @param expanded true to use the expanded icon; false to use the collapsed icon. * @return the root folder icon. */ public static Icon getRootIcon(boolean expanded) { @@ -159,7 +159,7 @@ public class DataTypeUtils { /** * Returns the open folder icon. - * + * * @param disabled True returns a disabled icon; false returns the normal icon. * @return the open folder icon. */ @@ -174,7 +174,7 @@ public class DataTypeUtils { /** * Returns the closed folder icon. - * + * * @param disabled True returns a disabled icon; false returns the normal icon. * @return the closed folder icon. */ @@ -189,7 +189,7 @@ public class DataTypeUtils { /** * Returns the open archive folder icon. - * + * * @param isLocked True means to return the checked-out open archive folder icon * @return the open archive folder icon. */ @@ -204,7 +204,7 @@ public class DataTypeUtils { /** * Returns the closed folder icon. - * + * * @param isLocked True means to return the checked-out closed folder icon * @return the closed folder icon. */ @@ -217,39 +217,9 @@ public class DataTypeUtils { return closedArchiveFolderIcon; } -// /** -// * Returns the open archive folder icon. -// * -// * @param isLocked True means to return the checked-out open archive folder icon -// * @return the open archive folder icon. -// */ -// public static Icon getOpenProjectArchiveFolder( boolean isLocked ) { -// loadImages(); -// if ( isLocked ) { -// return lockedOpenProjectArchiveFolderIcon; -// } -// -// return openProjectArchiveFolderIcon; -// } -// -// /** -// * Returns the closed folder icon. -// * -// * @param isLocked True means to return the checked-out closed folder icon -// * @return the closed folder icon. -// */ -// public static Icon getClosedProjectArchiveFolder( boolean isLocked ) { -// loadImages(); -// if ( isLocked ) { -// return lockedClosedProjectArchiveFolderIcon; -// } -// -// return closedProjectArchiveFolderIcon; -// } -// /** * Returns the BuiltIn icon. - * + * * @param disabled True returns a disabled icon; false returns the normal icon. * @return the BuiltIn icon. */ @@ -264,7 +234,7 @@ public class DataTypeUtils { /** * Returns the favorites icon. - * + * * @param disabled True returns a disabled icon; false returns the normal icon. * @return the favorites icon. */ @@ -279,7 +249,7 @@ public class DataTypeUtils { /** * Finds the icon associated with the provided data type. - * + * * @param dataType The data type for which to find an icon. * @param disabled True returns a disabled icon; false returns the normal icon. * @return the icon associated with the provided data type. @@ -302,7 +272,7 @@ public class DataTypeUtils { /** * Returns an icon that adds highlighting to the provided icon. - * + * * @param baseIcon The icon to highlight. * @return the highlighted icon. */ @@ -320,12 +290,12 @@ public class DataTypeUtils { } /** - * Returns a sorted list of {@link DataType}s that have names which start with the given - * search string. The list is sorted according to {@link #DATA_TYPE_LOOKUP_COMPARATOR}. - * - @param searchString The name of the DataTypes to match. + * Returns a sorted list of {@link DataType}s that have names which start with the given search + * string. The list is sorted according to {@link #DATA_TYPE_LOOKUP_COMPARATOR}. + * + * @param searchString The name of the DataTypes to match. * @param dataService The service from which the data types will be taken. - * @return A sorted list of {@link DataType}s that have names which start with the given search + * @return A sorted list of {@link DataType}s that have names which start with the given search * string. */ public static List getStartsWithMatchingDataTypes(String searchString, @@ -335,13 +305,13 @@ public class DataTypeUtils { } /** - * Returns a sorted list of {@link DataType}s that have names which match the given search - * string. The list is sorted according to {@link #DATA_TYPE_LOOKUP_COMPARATOR}. - * + * Returns a sorted list of {@link DataType}s that have names which match the given search + * string. The list is sorted according to {@link #DATA_TYPE_LOOKUP_COMPARATOR}. + * * @param searchString The name of the DataTypes to match. * @param dataService The service from which the data types will be taken. - * @return A sorted list of {@link DataType}s that have names which match the given search - * string. + * @return A sorted list of {@link DataType}s that have names which match the given search + * string. */ public static List getExactMatchingDataTypes(String searchString, DataTypeQueryService dataService) { @@ -350,10 +320,13 @@ public class DataTypeUtils { } /** - * Changes the give text to prepare it or use in searching for data types. Clients should - * call this method to make sure that the given text is suitable for use when searching - * the data type values returned by {@link #getExactMatchingDataTypes(String, DataTypeManagerService)} - * and {@link #getStartsWithMatchingDataTypes(String, DataTypeManagerService)}. + * Changes the given text to prepare it for use in searching for data types. Clients should + * call this method to make sure that the given text is suitable for use when searching the + * data type values returned by + * {@link #getExactMatchingDataTypes(String, DataTypeQueryService)} and + * {@link #getStartsWithMatchingDataTypes(String, DataTypeQueryService)}. + * @param searchText the search text + * @return the updated text */ public static String prepareSearchText(String searchText) { return searchText.replaceAll(" ", ""); @@ -376,12 +349,14 @@ public class DataTypeUtils { /** * Get the base data type for the specified data type. - *
For example, the base data type for Word*[5] is Word. - * For a pointer, the base data type is the type being pointed to - * or the pointer itself if it is pointing at nothing. - *
If "INT" is a typedef on a "dword" then INT[7][3] would have a base data type of dword. - * If you wanted to get the INT from INT[7][3] - * you should call getNamedBasedDataType(DataType) instead. + * + *

For example, the base data type for Word*[5] is Word. For a pointer, the base data type + * is the type being pointed to or the pointer itself if it is pointing at nothing. + * + *

If "INT" is a typedef on a "dword" then INT[7][3] would have a base data type of dword. + * If you wanted to get the INT from INT[7][3] you should call getNamedBasedDataType(DataType) + * instead. + * * @param dt the data type whose base data type is to be determined. * @return the base data type. */ @@ -409,15 +384,17 @@ public class DataTypeUtils { } /** - * Get the named base data type for the specified data type. - * This method intentionally does not drill down into typedefs. - *
For example, the named base data type for Word*[5] is Word. - * For a pointer, the named base data type is the type being pointed to - * or the pointer itself if it is pointing at nothing. - *
If "INT" is a typedef on a "dword", then INT[7][3] would - * have a named base data type of INT. - * If you wanted to get the dword from INT[7][3] - * you should call getBasedDataType(DataType) instead. + * Get the named base data type for the specified data type. This method intentionally does + * not drill down into typedefs. + * + *

For example, the named base data type for Word*[5] is Word. For a pointer, the named + * base data type is the type being pointed to or the pointer itself if it is pointing at + * nothing. + * + *

If "INT" is a typedef on a "dword", then INT[7][3] would have a named base data type of + * INT. If you wanted to get the dword from INT[7][3] you should call + * getBasedDataType(DataType) instead. + * * @param dt the data type whose named base data type is to be determined. * @return the base data type. */ @@ -444,10 +421,10 @@ public class DataTypeUtils { * Create a copy of the chain of data types that eventually lead to a named * data type. *

- * Returns a {@link DataType#copy(DataTypeManager) copy()} of the first named data - * type found in the pointer / array type chain, and returns an identical chain of - * pointer / arrays up to the copied named type. - *

+ * Returns a {@link DataType#copy(DataTypeManager) copy()} of the first named data type found + * in the pointer / array type chain, and returns an identical chain of pointer / arrays up to + * the copied named type. + * * @param dataType data type to be copied * @param dtm data type manager * @return deep copy of dataType @@ -475,39 +452,16 @@ public class DataTypeUtils { msg = "The Program is not modifiable!\n"; } else if (dtm instanceof FileArchiveBasedDataTypeManager) { - msg = - "The archive file is not modifiable!\nYou must open the archive for editing\n before performing this operation."; + msg = "The archive file is not modifiable!\nYou must open the archive for editing\n" + + "before performing this operation.\n" + dtm.getName(); } else { - msg = - "The project archive is not modifiable!\nYou must check out the archive\n before performing this operation."; + msg = "The project archive is not modifiable!\nYou must check out the archive\n" + + "before performing this operation.\n" + dtm.getName(); } Msg.showInfo(DataTypeUtils.class, parent, title, msg); - } -// For testing: -// public static void main( String[] args ) { -// JFrame frame = new JFrame(); -// JPanel panel = new JPanel(); -// -// JLabel label1 = new GDLabel(); -// Icon icon = getOpenFolderIcon( false ); -// label1.setIcon( icon ); -// -// JLabel label2 = new GDLabel(); -// Icon icon2 = ResourceManager.getDisabledIcon( (ImageIcon) icon ); -// label2.setIcon( icon2 ); -// -// panel.add( label1 ); -// panel.add( label2 ); -// -// frame.getContentPane().add( panel ); -// -// frame.pack(); -// frame.setVisible( true ); -// frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); -// } } //================================================================================================== diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedEnablementTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedEnablementTest.java index e3ffdb8c5c..00347d9e36 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedEnablementTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorLockedEnablementTest.java @@ -15,15 +15,14 @@ */ package ghidra.app.plugin.core.compositeeditor; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import org.junit.Assert; import org.junit.Test; import ghidra.program.model.data.*; import ghidra.util.exception.DuplicateNameException; -import ghidra.util.task.TaskMonitorAdapter; +import ghidra.util.task.TaskMonitor; public class StructureEditorLockedEnablementTest extends AbstractStructureEditorTest { @@ -34,7 +33,7 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor try { DataTypeManager dataTypeManager = cat.getDataTypeManager(); if (dt.getDataTypeManager() != dataTypeManager) { - dt = (Structure) dt.clone(dataTypeManager); + dt = dt.clone(dataTypeManager); } CategoryPath categoryPath = cat.getCategoryPath(); if (!dt.getCategoryPath().equals(categoryPath)) { @@ -69,7 +68,7 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor Structure desiredEmptyStructure = emptyStructure; int txID = program.startTransaction("Removing emptyStruct from DTM."); try { - programDTM.remove(emptyStructure, TaskMonitorAdapter.DUMMY_MONITOR); + programDTM.remove(emptyStructure, TaskMonitor.DUMMY); if (emptyStructure.getDataTypeManager() != catDTM) { desiredEmptyStructure = (Structure) emptyStructure.copy(catDTM); desiredEmptyStructure.setCategoryPath(pgmTestCat.getCategoryPath()); @@ -154,7 +153,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor for (CompositeEditorTableAction action : actions) { if ((action instanceof EditFieldAction) || (action instanceof AddBitFieldAction) || (action instanceof InsertUndefinedAction) || (action instanceof PointerAction) || - (action instanceof HexNumbersAction)) { + (action instanceof HexNumbersAction) || + (action instanceof ShowDataTypeInTreeAction)) { checkEnablement(action, true); } else if (action instanceof FavoritesAction) { @@ -203,7 +203,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor (action instanceof DuplicateMultipleAction) || (action instanceof ClearAction) || (action instanceof DeleteAction) || (action instanceof ArrayAction) || (action instanceof PointerAction) || (action instanceof HexNumbersAction) || - (action instanceof CreateInternalStructureAction)) { + (action instanceof CreateInternalStructureAction) || + (action instanceof ShowDataTypeInTreeAction)) { checkEnablement(action, true); } else { @@ -232,7 +233,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor (action instanceof MoveDownAction) || (action instanceof ClearAction) || (action instanceof DeleteAction) || (action instanceof ArrayAction) || (action instanceof PointerAction) || (action instanceof HexNumbersAction) || - (action instanceof CreateInternalStructureAction)) { + (action instanceof CreateInternalStructureAction) || + (action instanceof ShowDataTypeInTreeAction)) { checkEnablement(action, true); } else { @@ -261,7 +263,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor (action instanceof DuplicateAction) || (action instanceof DuplicateMultipleAction) || (action instanceof ArrayAction) || (action instanceof PointerAction) || (action instanceof HexNumbersAction) || - (action instanceof CreateInternalStructureAction)) { + (action instanceof CreateInternalStructureAction) || + (action instanceof ShowDataTypeInTreeAction)) { checkEnablement(action, true); } else { @@ -289,7 +292,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor (action instanceof MoveDownAction) || (action instanceof ClearAction) || (action instanceof DeleteAction) || (action instanceof ArrayAction) || (action instanceof PointerAction) || (action instanceof HexNumbersAction) || - (action instanceof CreateInternalStructureAction)) { + (action instanceof CreateInternalStructureAction) || + (action instanceof ShowDataTypeInTreeAction)) { checkEnablement(action, true); } else { @@ -313,7 +317,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor checkEnablement(action, len <= numBytes); } else if ((action instanceof CycleGroupAction) || (action instanceof ClearAction) || - (action instanceof DeleteAction) || (action instanceof HexNumbersAction)) { + (action instanceof DeleteAction) || (action instanceof HexNumbersAction) || + (action instanceof ShowDataTypeInTreeAction)) { checkEnablement(action, true); } else { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedEnablementTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedEnablementTest.java index f1d07eaf13..2b66f0bd8c 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedEnablementTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/compositeeditor/StructureEditorUnlockedEnablementTest.java @@ -118,7 +118,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit if ((action instanceof FavoritesAction) || (action instanceof CycleGroupAction) || (action instanceof EditFieldAction) || (action instanceof InsertUndefinedAction) || (action instanceof AddBitFieldAction) || (action instanceof PointerAction) || - (action instanceof HexNumbersAction)) { + (action instanceof HexNumbersAction) || + (action instanceof ShowDataTypeInTreeAction)) { checkEnablement(action, true); } else { @@ -151,7 +152,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit (action instanceof DuplicateMultipleAction) || (action instanceof DeleteAction) || (action instanceof ArrayAction) || (action instanceof PointerAction) || (action instanceof HexNumbersAction) || - (action instanceof CreateInternalStructureAction)) { + (action instanceof CreateInternalStructureAction) || + (action instanceof ShowDataTypeInTreeAction)) { checkEnablement(action, true); } else if (action instanceof FavoritesAction) { @@ -190,7 +192,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit (action instanceof MoveUpAction) || (action instanceof ClearAction) || (action instanceof DeleteAction) || (action instanceof ArrayAction) || (action instanceof PointerAction) || (action instanceof HexNumbersAction) || - (action instanceof CreateInternalStructureAction)) { + (action instanceof CreateInternalStructureAction) || + (action instanceof ShowDataTypeInTreeAction)) { checkEnablement(action, true); } else if (action instanceof FavoritesAction) { @@ -230,7 +233,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit (action instanceof DuplicateMultipleAction) || (action instanceof DeleteAction) || (action instanceof ArrayAction) || (action instanceof PointerAction) || (action instanceof HexNumbersAction) || - (action instanceof CreateInternalStructureAction)) { + (action instanceof CreateInternalStructureAction) || + (action instanceof ShowDataTypeInTreeAction)) { checkEnablement(action, true); } else if (action instanceof FavoritesAction) { @@ -269,7 +273,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit if ((action instanceof FavoritesAction) || (action instanceof CycleGroupAction) || (action instanceof EditFieldAction) || (action instanceof InsertUndefinedAction) || (action instanceof AddBitFieldAction) || (action instanceof PointerAction) || - (action instanceof HexNumbersAction)) { + (action instanceof HexNumbersAction) || + (action instanceof ShowDataTypeInTreeAction)) { checkEnablement(action, true); } else { diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeTestUtils.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeTestUtils.java index 06bf20e522..8f67e5c6f1 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeTestUtils.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/DataTypeTestUtils.java @@ -110,9 +110,9 @@ public class DataTypeTestUtils { public static ArchiveNode openArchive(String archiveName, boolean checkout, DataTypeManagerPlugin plugin) throws Exception { - ArchiveNode openArchive = openArchive(archiveName, checkout, false, plugin); + ArchiveNode archiveNode = openArchive(archiveName, checkout, false, plugin); waitForTree(plugin); - return openArchive; + return archiveNode; } private static void waitForTree(DataTypeManagerPlugin plugin) { diff --git a/Ghidra/Features/PDB/developer_scripts/DeveloperDumpAllTypesScript.java b/Ghidra/Features/PDB/developer_scripts/DeveloperDumpAllTypesScript.java index a38798ef98..269f8198ac 100644 --- a/Ghidra/Features/PDB/developer_scripts/DeveloperDumpAllTypesScript.java +++ b/Ghidra/Features/PDB/developer_scripts/DeveloperDumpAllTypesScript.java @@ -93,7 +93,7 @@ public class DeveloperDumpAllTypesScript extends GhidraScript { } String userChoice = OptionDialog.showInputChoiceDialog(null, "Choose a Data Type Manager or Cancel", - "Choose", names, initialDtmChoice, OptionDialog.CANCEL_OPTION); + "Choose", names, initialDtmChoice, OptionDialog.PLAIN_MESSAGE); if (userChoice == null) { return null; } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/combobox/GhidraComboBox.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/combobox/GhidraComboBox.java index 6610667fc5..2e86771b9e 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/combobox/GhidraComboBox.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/combobox/GhidraComboBox.java @@ -30,24 +30,24 @@ import docking.widgets.GComponent; /** * GhidraComboBox adds the following features: - * + * *

* 1) ActionListeners are only invoked when the <Enter> key is pressed within the text-field * of the combo-box. In normal JComboBox case, the ActionListeners are notified when an item is * selected from the list. - * + * *

* 2) Adds the auto-completion feature. As a user types in the field, the combo box suggest the * nearest matching entry in the combo box model. - * + * *

* It also fixes the following bug: - * + * *

* A normal JComboBox has a problem (feature?) that if you have a dialog with a button and * JComboBox and you edit the comboText field and then hit the button, the button sometimes does * not work. - * + * *

* When the combobox loses focus, and its text has changed, it generates an actionPerformed event * as though the user pressed <Enter> in the combo text field. This has a bizarre effect if @@ -55,12 +55,12 @@ import docking.widgets.GComponent; * enablement state of the button that you pressed (which caused the text field to lose focus) in * that you end up changing the button's internal state(by calling setEnabled(true or false)) in * the middle of the button press. - * + * * @param the item type */ public class GhidraComboBox extends JComboBox implements GComponent { - private ArrayList listeners = new ArrayList<>(); - private ArrayList docListeners = new ArrayList<>(); + private List listeners = new ArrayList<>(); + private List docListeners = new ArrayList<>(); private boolean setSelectedFlag = false; private boolean forwardEnter; @@ -70,7 +70,6 @@ public class GhidraComboBox extends JComboBox implements GComponent { * Default constructor. */ public GhidraComboBox() { - super(); init(); } @@ -164,7 +163,7 @@ public class GhidraComboBox extends JComboBox implements GComponent { * {@link GhidraComboBox} will add an action listener to handle <Enter> actions. *

* To re-enable the default behavior, set the forwardEnter value to true. - * + * * @param forwardEnter true to enable default <Enter> key handling. */ public void setEnterKeyForwarding(boolean forwardEnter) { @@ -198,7 +197,7 @@ public class GhidraComboBox extends JComboBox implements GComponent { * editor used. By default the editor for combo boxes is a text field. This method is * a convenience for the user to set the number of columns on that text field, which updates * the preferred size of the combo box. - * + * * @param columnCount The number of columns for the text field editor * @see JTextField#setColumns(int) */ @@ -216,11 +215,11 @@ public class GhidraComboBox extends JComboBox implements GComponent { *

  • The user deletes the text
  • *
  • setSelectedItem(Object) method is called with the same item
  • * - * + * * In that above series of steps, the text will still be empty, as the user deleted it *and* * the call to setSelectedItem(Object) had no effect because the base class assumed that the * item is already selected. - * + * *

    * This method exists to make sure, in that case, that the text of the field matches the * selected item. @@ -256,6 +255,13 @@ public class GhidraComboBox extends JComboBox implements GComponent { model.addElement(obj); } + public void addToModel(Collection items) { + DefaultComboBoxModel model = (DefaultComboBoxModel) getModel(); + for (E e : items) { + model.addElement(e); + } + } + public boolean containsItem(E obj) { DefaultComboBoxModel model = (DefaultComboBoxModel) getModel(); return model.getIndexOf(obj) != -1; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/InputWithChoicesDialog.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/InputWithChoicesDialog.java index 384065103c..62f5ad048e 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/InputWithChoicesDialog.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/dialogs/InputWithChoicesDialog.java @@ -28,7 +28,6 @@ import docking.widgets.label.GHtmlLabel; /** * A dialog that has text fields to get user input. - * */ public class InputWithChoicesDialog extends DialogComponentProvider { @@ -37,19 +36,17 @@ public class InputWithChoicesDialog extends DialogComponentProvider { private boolean allowEdits; /** - * Creates a provider for a generic input dialog with the specified title, - * a label and a editable comboBox pre-populated with selectable values. The user - * can check the value of {@link #isCanceled()} to know whether or not - * the user canceled the operation. To get the user selected value use the - * {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then - * null will be returned from getValue(). - *

    + * Creates a provider for a generic input dialog with the specified title, a label and a + * editable comboBox pre-populated with selectable values. The user can check the value of + * {@link #isCanceled()} to know whether or not the user canceled the operation. To get the + * user selected value use the {@link #getValue()} value(s) entered by the user. If the user + * cancelled the operation, then null will be returned from {@link #getValue()}. * * @param dialogTitle used as the name of the dialog's title bar * @param label value to use for the label of the text field * @param optionValues values to populate the combo box - * @param initialValue the initial value - can be null - * @param messageIcon the icon to display on the dialog--can be null + * @param initialValue the initial value; may be null + * @param messageIcon the icon to display on the dialog; may be null */ public InputWithChoicesDialog(String dialogTitle, String label, String[] optionValues, String initialValue, Icon messageIcon) { @@ -67,20 +64,18 @@ public class InputWithChoicesDialog extends DialogComponentProvider { } /** - * Creates a provider for a generic input dialog with the specified title, - * a label and a editable comboBox pre-populated with selectable values. The user - * can check the value of {@link #isCanceled()} to know whether or not - * the user canceled the operation. To get the user selected value use the - * {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then - * null will be returned from getValue(). - *

    + * Creates a provider for a generic input dialog with the specified title, a label and a + * editable comboBox pre-populated with selectable values. The user can check the value of + * {@link #isCanceled()} to know whether or not the user canceled the operation. To get the + * user selected value use the {@link #getValue()} value(s) entered by the user. If the user + * cancelled the operation, then null will be returned from {@link #getValue()}. * * @param dialogTitle used as the name of the dialog's title bar * @param label value to use for the label of the text field * @param optionValues values to populate the combo box - * @param initialValue the initial value - can be null - * @param allowEdits true allows the user to add custom entries to the combo box by entering text - * @param messageIcon the icon to display on the dialog--can be null + * @param initialValue the initial value; may be null + * @param allowEdits true allows the user to add custom entries by entering text + * @param messageIcon the icon to display on the dialog; may be null */ public InputWithChoicesDialog(String dialogTitle, String label, String[] optionValues, String initialValue, boolean allowEdits, Icon messageIcon) { @@ -103,7 +98,7 @@ public class InputWithChoicesDialog extends DialogComponentProvider { } /** - * completes the construction of the gui for this dialog + * Completes the construction of the gui for this dialog */ private void buildMainPanel(String labelText, String[] optionValues, String initialValue, Icon messageIcon) { @@ -168,13 +163,15 @@ public class InputWithChoicesDialog extends DialogComponentProvider { /** * Returns if this dialog is canceled. + * @return true if canceled */ public boolean isCanceled() { return isCanceled; } /** - * return the value of the first combo box + * Return the value of the first combo box. + * @return the value */ public String getValue() { if (isCanceled) { @@ -192,8 +189,7 @@ public class InputWithChoicesDialog extends DialogComponentProvider { /** * Set the current choice to value. * @param value updated choice - * @throws NoSuchElementException if choice does not permit edits and value is - * not a valid choice. + * @throws NoSuchElementException if edits not permitted and value is not a valid choice */ public void setValue(String value) { combo.setSelectedItem(value); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTree.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTree.java index 6c38df4a05..19cd4ed0be 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTree.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/GTree.java @@ -27,6 +27,7 @@ import java.util.*; import java.util.List; import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.stream.Collectors; import javax.swing.*; import javax.swing.Timer; @@ -334,7 +335,7 @@ public class GTree extends JPanel implements BusyListener { *

    * Note: See the usage note at the header of this class concerning how tree state * is used relative to the equals() method. - * + * * @param state the state to restore * * @see #getTreeState() @@ -582,7 +583,7 @@ public class GTree extends JPanel implements BusyListener { * been replaced by a new node that is equal, but a different instance. One way this happens * is if the tree is filtered and therefor the displayed nodes are clones of the model nodes. * This can also happen if the tree nodes are rebuilt for some reason. - * + * * @param node the node * @return the corresponding model node in the tree. If the tree is filtered the viewed node * will be a clone of the corresponding model node. @@ -596,7 +597,7 @@ public class GTree extends JPanel implements BusyListener { * been replaced by a new node that is equal, but a different instance. One way this happens * is if the tree is filtered and therefor the displayed nodes are clones of the model nodes. * This can also happen if the tree nodes are rebuilt for some reason. - * + * * @param path the path of the node * @return the corresponding model node in the tree. If the tree is filtered the viewed node * will be a clone of the corresponding model node. @@ -609,7 +610,7 @@ public class GTree extends JPanel implements BusyListener { * Gets the view node for the given node. This is useful to translate to a tree path that is * valid for the currently displayed tree. (Remember that if the tree is filtered, then the * displayed nodes are clones of the model nodes.) - * + * * @param node the node * @return the current node in the displayed (possibly filtered) tree */ @@ -621,7 +622,7 @@ public class GTree extends JPanel implements BusyListener { * Gets the view node for the given path. This is useful to translate to a tree path that is * valid for the currently displayed tree. (Remember that if the tree is filtered, then the * displayed nodes are clones of the model nodes.) - * + * * @param path the path of the node * @return the current node in the displayed (possibly filtered) tree */ @@ -760,7 +761,7 @@ public class GTree extends JPanel implements BusyListener { /** * Returns true if the given JTree is the actual JTree used by this GTree. - * + * * @param jTree the tree to test * @return true if the given JTree is the actual JTree used by this GTree. */ @@ -773,7 +774,7 @@ public class GTree extends JPanel implements BusyListener { *

    * NOTE: if this method is not called from the Swing thread, then the root node will be set * later on the Swing thread. That is, this method will return before the work has been done. - * + * * @param rootNode The node to set as the new root. */ public void setRootNode(GTreeNode rootNode) { @@ -916,6 +917,13 @@ public class GTree extends JPanel implements BusyListener { return paths; } + public List getSelectedNodes() { + TreePath[] paths = getSelectionPaths(); + return Arrays.stream(paths) + .map(tp -> (GTreeNode) tp.getLastPathComponent()) + .collect(Collectors.toList()); + } + public boolean isExpanded(TreePath treePath) { return tree.isExpanded(treePath); } @@ -1023,20 +1031,20 @@ public class GTree extends JPanel implements BusyListener { * becomes available to the model. This method will ensure that the named child passes any * current filter in order for the child to appear in the tree. This effect is temporary and * will be undone when next the filter changes. - * + * *

    This method is intended to be used by clients using an asynchronous node model, where * new nodes will get created by application-level events. Such clients may wish to perform * work when newly created nodes become available. This method simplifies the concurrent * nature of the GTree, asynchronous nodes and the processing of asynchronous application-level * events by providing a callback mechanism for clients. This method is non-blocking. - * + * *

    Note: this method assumes that the given parent node is in the view and not filtered * out of the view. This method makes no attempt to ensure the given parent node passes any * existing filter. - * + * *

    Note: this method will not wait forever for the given node to appear. It will eventually * give up if the node never arrives. - * + * * @param parent the model's parent node. If the view's parent node is passed, it will * be translated to the model node. * @param childName the name of the desired child @@ -1046,17 +1054,17 @@ public class GTree extends JPanel implements BusyListener { Consumer consumer) { /* - + If the GTree were to use Java's CompletableStage API, then the code below could be written thusly: - + tree.getNewNode(modelParent, newName) .thenCompose(newModelChild -> { tree.ignoreFilter(newModelChild); return tree.getNewNode(viewParent, newName); )) .thenAccept(consumer); - + */ // ensure we operate on the model node which will always have the given child not the view @@ -1075,7 +1083,7 @@ public class GTree extends JPanel implements BusyListener { * Requests that the node with the given name, in the given parent, be edited. This operation * is asynchronous. This request will be buffered as needed to wait for the given node to be * added to the parent, up to a timeout period. - * + * * @param parent the parent node * @param childName the name of the child to edit */ @@ -1101,7 +1109,7 @@ public class GTree extends JPanel implements BusyListener { /** * Requests that the node be edited. This operation is asynchronous. - * + * * @param node the node to edit */ public void startEditing(GTreeNode node) { @@ -1269,7 +1277,7 @@ public class GTree extends JPanel implements BusyListener { /** * Used to run tree tasks. This method is not meant for general clients of this tree, but * rather for tasks to tell the tree to perform subtasks. - * + * * @param task the task to run */ public void runTask(GTreeTask task) { diff --git a/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGTest.java b/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGTest.java index 6dd9e43a2b..6a7ff342e8 100644 --- a/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGTest.java +++ b/Ghidra/Framework/Generic/src/main/java/generic/test/AbstractGTest.java @@ -15,8 +15,9 @@ */ package generic.test; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringContains.*; +import static org.junit.Assert.*; import java.io.File; import java.util.*; @@ -298,6 +299,15 @@ public abstract class AbstractGTest { } } + public static void assertContainsString(String expected, String actual) { + assertThat(actual, containsString(expected)); + } + + public static void assertContainsStringIgnoringCase(String expected, String actual) { + // newer hamcrest versions have containsStringIgnoringCase() + assertThat(actual.toLowerCase(), containsString(expected.toLowerCase())); + } + private static String printListFailureMessage(String message, List expected, List actual) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java index b3deabd11a..de5f472d80 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DataTypeManager.java @@ -68,7 +68,7 @@ public interface DataTypeManager { /** * Returns a unique name not currently used by any other dataType or category * with the same baseName - * + * * @param path the path of the name * @param baseName the base name to be made unique * @return a unique name starting with baseName @@ -90,7 +90,7 @@ public interface DataTypeManager { * Returns a data type after adding it to this data manager. * The returned dataType will be in a category in this dataTypeManager * that is equivalent to the category of the passed in dataType. - * + * * @param dataType the dataType to be resolved. * @param handler used to resolve conflicts with existing dataTypes. * @return an equivalent dataType that "belongs" to this dataTypeManager. @@ -120,7 +120,7 @@ public interface DataTypeManager { /** * Adds all data types to the specified list.] - * + * * @param list the result list into which the types will be placed */ public void getAllDataTypes(List list); @@ -181,7 +181,7 @@ public interface DataTypeManager { * there is also a category "b" under category "a". A better solution is to use * the {@link #getDataType(DataTypePath)} method because the DataTypePath keeps the * category and datatype name separate. - * + * * @param dataTypePath path * @return the dataType or null if it isn't found */ @@ -206,7 +206,7 @@ public interface DataTypeManager { /** * Returns the dataTypeId for the given dataType. If the dataType is not * currently in the dataTypeManger, it will be added - * + * * @param dt the data type * @return the ID of the resolved type */ @@ -215,7 +215,7 @@ public interface DataTypeManager { /** * Returns the dataTypeId for the given dataType. If the dataType does not exist, * a -1 will be returned - * + * * @param dt the datatype to get an id for * @return the ID of the type */ @@ -224,7 +224,7 @@ public interface DataTypeManager { /** * Returns the dataType associated with the given dataTypeId or null if the dataTypeId is * not valid - * + * * @param dataTypeID the ID * @return the type */ @@ -232,7 +232,7 @@ public interface DataTypeManager { /** * Returns the Category with the given id - * + * * @param categoryID id of the desired category * @return the category */ @@ -240,7 +240,7 @@ public interface DataTypeManager { /** * Get the category that has the given path - * + * * @param path the path * @return the category if defined, otherwise null */ @@ -282,7 +282,7 @@ public interface DataTypeManager { /** * Return true if the given dataType exists in this data type manager - * + * * @param dataType the type * @return true if the type is in this manager */ @@ -290,7 +290,7 @@ public interface DataTypeManager { /** * Create a category for the given path; returns the current category if it already exits - * + * * @param path the path * @return the category */ @@ -351,7 +351,7 @@ public interface DataTypeManager { /** * Returns a default sized pointer to the given datatype. The pointer size is established * dynamically based upon the data organization established by the compiler specification. - * + * * @param datatype the pointed to data type * @return the pointer */ @@ -361,7 +361,7 @@ public interface DataTypeManager { * Returns a pointer of the given size to the given datatype. * Note: It is preferred to use default sized pointers when possible (i.e., size=-1, * see {@link #getPointer(DataType)}) instead of explicitly specifying the size value. - * + * * @param datatype the pointed to data type * @param size the size of the pointer to be created or -1 for a default sized pointer * @return the pointer @@ -416,6 +416,14 @@ public interface DataTypeManager { */ public void findEnumValueNames(long value, Set enumValueNames); + /** + * Finds the data type using the given source archive and id. + * + * @param sourceArchive the optional source archive; required when the type is associated with + * that source archive + * @param datatypeID the type's id + * @return the type or null + */ public DataType getDataType(SourceArchive sourceArchive, UniversalID datatypeID); /** @@ -433,7 +441,7 @@ public interface DataTypeManager { /** * Returns the source archive for the given ID - * + * * @param sourceID the ID * @return the archive; null if the ID is null; null if the archive does not exist */ @@ -447,7 +455,7 @@ public interface DataTypeManager { /** * Returns all data types within this manager that have as their source the given archive - * + * * @param sourceArchive the archive * @return the types */ @@ -461,7 +469,7 @@ public interface DataTypeManager { /** * Change the given data type so that its source archive is the given archive - * + * * @param datatype the type * @param archive the archive */ @@ -508,7 +516,7 @@ public interface DataTypeManager { /** * Removes the source archive from this manager. This will disassociate all data types in * this manager from the given archive. - * + * * @param sourceArchive the archive */ public void removeSourceArchive(SourceArchive sourceArchive); @@ -529,17 +537,18 @@ public interface DataTypeManager { * @deprecated the method {@link DataType#getParents()} should be used instead. * Use of {@link Set} implementations for containing DataTypes is also inefficient. */ + @Deprecated public Set getDataTypesContaining(DataType dataType); /** - * Determine if settings are supported for BuiltIn datatypes within this + * Determine if settings are supported for BuiltIn datatypes within this * datatype manager. * @return true if BuiltIn Settings are permitted */ public boolean allowsDefaultBuiltInSettings(); /** - * Determine if settings are supported for datatype components within this + * Determine if settings are supported for datatype components within this * datatype manager (i.e., for structure and union components). * @return true if BuiltIn Settings are permitted */ diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureFactory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureFactory.java index 8769663e0b..149b48b7f9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureFactory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/StructureFactory.java @@ -23,8 +23,8 @@ import ghidra.program.model.listing.*; /** * Creates and initializes {@link Structure} objects. - * - * + * + * */ public class StructureFactory { public static final String DEFAULT_STRUCTURE_NAME = "struct"; @@ -33,14 +33,14 @@ public class StructureFactory { * Creates a {@link StructureDataType} instance based upon the information * provided. The instance will not be placed in memory. *

    - * This method is just a pass-through method for + * This method is just a pass-through method for * {@link #createStructureDataType(Program,Address,int,String,boolean)} * equivalent to calling: *

     	 *      Structure newStructure = StructureFactory.createStructureDataType(
     	 *          program, address, dataLength, DEFAULT_STRUCTURE_NAME, true );
     	 * 
    - * + * * @param program The program to which the structure will belong. * @param address The address of the structure. * @param dataLength The number of components to add to the structure. @@ -50,7 +50,7 @@ public class StructureFactory { *
  • if dataLength is not greater than zero *
  • if the number of components to add exceeds the available * address space - *
  • if there are any instructions in the provided + *
  • if there are any instructions in the provided * address space *
  • if there are no data components to add to the structure * @@ -63,7 +63,7 @@ public class StructureFactory { /** * Creates a {@link StructureDataType} instance based upon the information * provided. The instance will not be placed in memory. - * + * * @param program The program to which the structure will belong. * @param address The address of the structure. * @param dataLength The number of components to add to the structure. @@ -77,7 +77,7 @@ public class StructureFactory { *
  • if dataLength is not greater than zero *
  • if the number of components to add exceeds the available * address space - *
  • if there are any instructions in the provided + *
  • if there are any instructions in the provided * address space *
  • if there are no data components to add to the structure * @@ -125,18 +125,18 @@ public class StructureFactory { } /** - * Creates a {@link StructureDataType} instance, which is inside of - * another structure, based upon the information provided. The instance + * Creates a {@link StructureDataType} instance, which is inside of + * another structure, based upon the information provided. The instance * will not be placed in memory. *

    - * This method is just a pass-through method for + * This method is just a pass-through method for * {@link #createStructureDataTypeInStrucuture(Program,Address,int[],int[],String,boolean)} * equivalent to calling: *

     	 *      Structure newStructure = StructureFactory.createStructureDataTypeInStrucuture(
     	 *          program, address, fromPath, toPath, DEFAULT_STRUCTURE_NAME, true );
     	 * 
    - * + * * @param program The program to which the structure will belong. * @param address The address of the structure. * @param fromPath The path to the first element in the parent structure @@ -160,10 +160,10 @@ public class StructureFactory { } /** - * Creates a {@link StructureDataType} instance, which is inside of - * another structure, based upon the information provided. The instance + * Creates a {@link StructureDataType} instance, which is inside of + * another structure, based upon the information provided. The instance * will not be placed in memory. - * + * * @param program The program to which the structure will belong. * @param address The address of the structure. * @param fromPath The path to the first element in the parent structure @@ -227,7 +227,7 @@ public class StructureFactory { return newStructure; } - // uses the provided context to initiailze the provided structure with + // uses the provided context to initialize the provided structure with // dataLength number of components private static void initializeStructureFromContext(Structure structure, DataTypeProviderContext context, int dataLength) { @@ -249,8 +249,8 @@ public class StructureFactory { } for (DataTypeComponent dataComp : dataComps) { - structure.add(dataComp.getDataType(), dataComp.getLength(), - dataComp.getFieldName(), dataComp.getComment()); + structure.add(dataComp.getDataType(), dataComp.getLength(), dataComp.getFieldName(), + dataComp.getComment()); } } }