GP-1913 - Updated data type synchronization workflow by adding a new action and a home button to the structure editor. Added action to the composite editors and enum editor to show the type being edited in the Data Type Manager's tree.

This commit is contained in:
dragonmacher 2022-05-19 18:33:40 -04:00
parent d7f9cdfe5c
commit d9af59df1a
53 changed files with 955 additions and 453 deletions

View file

@ -15,8 +15,12 @@
*/ */
package agent.frida.model.invm; 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 { public class InVmModelForFridaFactoryTest extends AbstractModelForFridaFactoryTest {
@Override @Override
public ModelHost modelHost() throws Throwable { public ModelHost modelHost() throws Throwable {

View file

@ -17,10 +17,13 @@ package agent.frida.model.invm;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.experimental.categories.Category;
import agent.frida.model.AbstractModelForFridaInterpreterTest; import agent.frida.model.AbstractModelForFridaInterpreterTest;
import generic.test.category.NightlyCategory;
import ghidra.dbg.test.ProvidesTargetViaLaunchSpecimen; import ghidra.dbg.test.ProvidesTargetViaLaunchSpecimen;
@Category(NightlyCategory.class) // this may actually be an @PortSensitive test
public class InVmModelForFridaInterpreterTest extends AbstractModelForFridaInterpreterTest public class InVmModelForFridaInterpreterTest extends AbstractModelForFridaInterpreterTest
implements ProvidesTargetViaLaunchSpecimen { implements ProvidesTargetViaLaunchSpecimen {
@Override @Override
@ -61,4 +64,3 @@ public class InVmModelForFridaInterpreterTest extends AbstractModelForFridaInter
} }
} }

View file

@ -15,8 +15,12 @@
*/ */
package agent.frida.model.invm; 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 { public class InVmModelForFridaMethodsTest extends AbstractModelForFridaMethodsTest {
@Override @Override
public ModelHost modelHost() throws Throwable { public ModelHost modelHost() throws Throwable {

View file

@ -17,9 +17,12 @@ package agent.frida.model.invm;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.experimental.categories.Category;
import agent.frida.model.AbstractModelForFridaRootAttacherTest; 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 { public class InVmModelForFridaRootAttacherTest extends AbstractModelForFridaRootAttacherTest {
@Override @Override
public ModelHost modelHost() throws Throwable { public ModelHost modelHost() throws Throwable {

View file

@ -15,8 +15,12 @@
*/ */
package agent.frida.model.invm; 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 { public class InVmModelForFridaRootLauncherTest extends AbstractModelForFridaRootLauncherTest {
@Override @Override
public ModelHost modelHost() throws Throwable { public ModelHost modelHost() throws Throwable {

View file

@ -15,8 +15,12 @@
*/ */
package agent.frida.model.invm; 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 { public class InVmModelForFridaScenarioStackTest extends AbstractModelForFridaScenarioStackTest {
@Override @Override
public ModelHost modelHost() throws Throwable { public ModelHost modelHost() throws Throwable {

View file

@ -15,8 +15,12 @@
*/ */
package agent.frida.model.invm; 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 { public class InVmModelForFridaX64RegistersTest extends AbstractModelForFridaX64RegistersTest {
@Override @Override

View file

@ -98,6 +98,17 @@
the <i>Data Type Manager</i> display is updated to reflect the new name in the tree. the <i>Data Type Manager</i> display is updated to reflect the new name in the tree.
</p> </p>
</blockquote> </blockquote>
<H2>Show In Data Type Manager</H2>
<BLOCKQUOTE>
<P>Select the <IMG src="images/go-home.png" alt=""> icon in the toolbar to have the editor's
data type be highlighted in the Data Type Manager's tree.
</P>
</BLOCKQUOTE
<h2>Change the Sort Order</h2> <h2>Change the Sort Order</h2>
<blockquote> <blockquote>
<p>As with most tables in Ghidra, you can change the sort order of a column by <p>As with most tables in Ghidra, you can change the sort order of a column by

View file

@ -102,6 +102,16 @@
data items in the program will have changed due to the apply.</P> data items in the program will have changed due to the apply.</P>
</BLOCKQUOTE> </BLOCKQUOTE>
<H2><A name="Show_In_Data_Type_Manager"></A><A name="Structure_Editor_Show_In_Data_Type_Manager">Show In Data Type Manager</H2>
<BLOCKQUOTE>
<P>Select the <IMG src="images/go-home.png" alt=""> icon in the toolbar to have the editor's
data type be highlighted in the Data Type Manager's tree.
</P>
</BLOCKQUOTE>
<H2>Closing the Editor</H2> <H2>Closing the Editor</H2>
<BLOCKQUOTE> <BLOCKQUOTE>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before After
Before After

View file

@ -48,11 +48,8 @@ public class ApplyAction extends CompositeEditorTableAction {
try { try {
model.apply(); model.apply();
} }
catch (EmptyCompositeException e1) { catch (EmptyCompositeException | InvalidDataTypeException e) {
model.setStatus(e1.getMessage(), true); model.setStatus(e.getMessage(), true);
}
catch (InvalidDataTypeException e1) {
model.setStatus(e1.getMessage(), true);
} }
requestTableFocus(); requestTableFocus();
} }

View file

@ -45,7 +45,7 @@ abstract public class CompositeEditorTableAction extends DockingAction implement
public static final String EDIT_ACTION_PREFIX = "Editor: "; public static final String EDIT_ACTION_PREFIX = "Editor: ";
public CompositeEditorTableAction(CompositeEditorProvider provider, String name, String group, 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); super(name, provider.plugin.getName(), KeyBindingType.SHARED);
this.provider = provider; this.provider = provider;
model = provider.getModel(); model = provider.getModel();

View file

@ -15,53 +15,55 @@
*/ */
package ghidra.app.plugin.core.compositeeditor; package ghidra.app.plugin.core.compositeeditor;
import ghidra.program.model.data.*;
import docking.ComponentProvider; import docking.ComponentProvider;
import ghidra.program.model.data.*;
/** /**
* Interface implemented by data type editors. * Interface implemented by data type editors.
*
*
*/ */
public interface EditorProvider { public interface EditorProvider {
/** /**
* Get the name of this editor. * Get the name of this editor.
* @return the name of this editor
*/ */
public String getName(); public String getName();
/** /**
* Get the pathname of the data type being edited. * Get the pathname of the data type being edited.
* @return the pathname of the data type being edited
*/ */
public DataTypePath getDtPath(); public DataTypePath getDtPath();
/** /**
* Get the component provider for this editor. * Get the component provider for this editor.
* @return the component provider for this editor
*/ */
public ComponentProvider getComponentProvider(); public ComponentProvider getComponentProvider();
/** /**
* Get the datatype manager associated with this editor. * Get the datatype manager associated with this editor.
* @return the datatype manager associated with this editor
*/ */
public DataTypeManager getDataTypeManager(); 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. * @param domainObject the program or data type archive that was restored.
*/ */
public void domainObjectRestored(DataTypeManagerDomainObject domainObject); public void domainObjectRestored(DataTypeManagerDomainObject domainObject);
/** /**
* Return whether this editor is editing the data type with the given * Return whether this editor is editing the data type with the given path.
* path.
* @param dtPath path of a data type * @param dtPath path of a data type
* @return true if the data type for the pathname is being edited * @return true if the data type for the pathname is being edited
*/ */
public boolean isEditing(DataTypePath dtPath); public boolean isEditing(DataTypePath dtPath);
/** /**
* Add an editor listener that will be notified when the edit window is * Add an editor listener that will be notified when the edit window is closed.
* closed. * @param listener the listener
*/ */
public void addEditorListener(EditorListener listener); public void addEditorListener(EditorListener listener);
@ -72,6 +74,7 @@ public interface EditorProvider {
/** /**
* Returns whether changes need to be saved. * Returns whether changes need to be saved.
* @return whether changes need to be saved
*/ */
public boolean needsSave(); public boolean needsSave();

View file

@ -15,16 +15,13 @@
*/ */
package ghidra.app.plugin.core.compositeeditor; package ghidra.app.plugin.core.compositeeditor;
import javax.swing.SwingUtilities;
import docking.ActionContext; import docking.ActionContext;
import docking.action.MenuData; import docking.action.MenuData;
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService; import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.program.model.data.Composite; import ghidra.program.model.data.Composite;
import ghidra.program.model.data.DataTypeComponent; import ghidra.program.model.data.DataTypeComponent;
import ghidra.util.HelpLocation; import ghidra.util.*;
import ghidra.util.Msg;
/** /**
* An action to show references to the field in the currently selected editor row * 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(); String fieldName = getFieldName();
Composite composite = model.getOriginalComposite(); Composite composite = model.getOriginalComposite();
SwingUtilities.invokeLater( Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(composite, fieldName));
() -> service.findAndDisplayAppliedDataTypeAddresses(composite, fieldName));
} }
private String getFieldName() { private String getFieldName() {

View file

@ -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);
}
}

View file

@ -73,6 +73,8 @@ public class StructureEditorProvider extends CompositeEditorProvider {
new ShowComponentPathAction(this), new ShowComponentPathAction(this),
new AddBitFieldAction(this), new AddBitFieldAction(this),
new EditBitFieldAction(this), new EditBitFieldAction(this),
new ShowDataTypeInTreeAction(this),
// new ViewBitFieldAction(this) // new ViewBitFieldAction(this)
}; };
//@formatter:on //@formatter:on

View file

@ -65,7 +65,8 @@ public class UnionEditorProvider extends CompositeEditorProvider {
new EditFieldAction(this), new EditFieldAction(this),
new HexNumbersAction(this), new HexNumbersAction(this),
new AddBitFieldAction(this), new AddBitFieldAction(this),
new EditBitFieldAction(this) new EditBitFieldAction(this),
new ShowDataTypeInTreeAction(this)
}; };
//@formatter:on //@formatter:on
} }

View file

@ -38,7 +38,9 @@ import generic.util.Path;
import ghidra.app.CorePluginPackage; import ghidra.app.CorePluginPackage;
import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin; 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.archive.*;
import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager; import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager;
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; 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.data.*;
import ghidra.program.model.listing.DataTypeArchive; import ghidra.program.model.listing.DataTypeArchive;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation; import ghidra.util.*;
import ghidra.util.Msg;
import ghidra.util.datastruct.LRUMap; import ghidra.util.datastruct.LRUMap;
import ghidra.util.task.TaskLauncher; import ghidra.util.task.TaskLauncher;
@ -570,6 +571,10 @@ public class DataTypeManagerPlugin extends ProgramPlugin
return dataTypeManagerHandler.openArchive(archiveName); return dataTypeManagerHandler.openArchive(archiveName);
} }
public List<Archive> getAllArchives() {
return dataTypeManagerHandler.getAllArchives();
}
public void openProjectDataTypeArchive() { public void openProjectDataTypeArchive() {
if (openDialog == null) { if (openDialog == null) {
ActionListener listener = ev -> { ActionListener listener = ev -> {
@ -613,7 +618,9 @@ public class DataTypeManagerPlugin extends ProgramPlugin
@Override @Override
public void setDataTypeSelected(DataType dataType) { public void setDataTypeSelected(DataType dataType) {
if (provider.isVisible()) { 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; return null;
} }
DataTypesActionContext dtContext = (DataTypesActionContext) context; DataTypesActionContext dtContext = (DataTypesActionContext) context;
GTreeNode selectedNode = dtContext.getSelectedNode(); GTreeNode selectedNode = dtContext.getClickedNode();
if (!(selectedNode instanceof ArchiveNode)) { if (!(selectedNode instanceof ArchiveNode)) {
return null; return null;
} }

View file

@ -142,35 +142,38 @@ public class DataTypeSynchronizer {
} }
/** /**
* Commits a single program data type's changes to the associated source data type in the archive. * Commits a single program data type's changes to the associated source data type in the
* @param refDT the program data type * archive.
* @return true if the commit succeeds. * @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) { public static boolean commit(DataTypeManagerHandler dtmHandler, DataType dt) {
SourceArchive sourceArchive = refDT.getSourceArchive(); SourceArchive sourceArchive = dt.getSourceArchive();
DataTypeManager sourceDTM = dtmHandler.getDataTypeManager(sourceArchive); DataTypeManager sourceDTM = dtmHandler.getDataTypeManager(sourceArchive);
if (sourceDTM == null) { if (sourceDTM == null) {
return false; return false;
} }
commit(sourceDTM, refDT); commit(sourceDTM, dt);
return true; return true;
} }
/** /**
* Updates a single data type in the program to match the associated source data type from the * Updates a single data type in the program to match the associated source data type from the
* archive. * archive.
* @param dataType the program data type * @param dtmHandler the handler that manages data types
* @return true if the update succeeds. * @param dt the data type
* @return true if the update succeeds
*/ */
public static boolean update(DataTypeManagerHandler dtmHandler, DataType refDT) { public static boolean update(DataTypeManagerHandler dtmHandler, DataType dt) {
DataTypeManager dataTypeManager = refDT.getDataTypeManager(); DataTypeManager dataTypeManager = dt.getDataTypeManager();
SourceArchive sourceArchive = refDT.getSourceArchive(); SourceArchive sourceArchive = dt.getSourceArchive();
DataTypeManager sourceDTM = dtmHandler.getDataTypeManager(sourceArchive); DataTypeManager sourceDtm = dtmHandler.getDataTypeManager(sourceArchive);
if (dataTypeManager == null || sourceDTM == null) { if (dataTypeManager == null || sourceDtm == null) {
return false; return false;
} }
DataType sourceDT = sourceDTM.getDataType(sourceArchive, refDT.getUniversalID()); DataType sourceDt = sourceDtm.getDataType(sourceArchive, dt.getUniversalID());
update(dataTypeManager, sourceDT); update(dataTypeManager, sourceDt);
return true; return true;
} }
@ -355,8 +358,9 @@ public class DataTypeSynchronizer {
buffy.append("<TR BORDER=LEFT>"); buffy.append("<TR BORDER=LEFT>");
buffy.append("<TD VALIGN=\"TOP\">"); buffy.append("<TD VALIGN=\"TOP\">");
buffy.append("<B>").append(HTMLUtilities.escapeHTML(dataTypeManager.getName())).append( buffy.append("<B>")
"</B><HR NOSHADE>"); .append(HTMLUtilities.escapeHTML(dataTypeManager.getName()))
.append("</B><HR NOSHADE>");
buffy.append(htmlContent); buffy.append(htmlContent);
// horizontal spacer below the inner table in order to force a minimum width // horizontal spacer below the inner table in order to force a minimum width
@ -368,8 +372,9 @@ public class DataTypeSynchronizer {
buffy.append("</TD>"); buffy.append("</TD>");
buffy.append("<TD VALIGN=\"TOP\">"); buffy.append("<TD VALIGN=\"TOP\">");
buffy.append("<B>").append(HTMLUtilities.escapeHTML(sourceArchive.getName())).append( buffy.append("<B>")
"</B><HR NOSHADE>"); .append(HTMLUtilities.escapeHTML(sourceArchive.getName()))
.append("</B><HR NOSHADE>");
buffy.append(otherContent); buffy.append(otherContent);
@ -458,8 +463,8 @@ public class DataTypeSynchronizer {
return; return;
} }
int transactionID = dataTypeManager.startTransaction( int transactionID = dataTypeManager
"re-sync '" + sourceArchive.getName() + "' data types"); .startTransaction("re-sync '" + sourceArchive.getName() + "' data types");
try { try {
reSyncOutOfSyncInTimeOnlyDataTypes(); reSyncOutOfSyncInTimeOnlyDataTypes();
fixSyncForDifferingDataTypes(); fixSyncForDifferingDataTypes();

View file

@ -20,13 +20,15 @@ import java.util.List;
import javax.swing.tree.TreePath; import javax.swing.tree.TreePath;
import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode; import docking.widgets.tree.GTreeNode;
import ghidra.app.context.ProgramActionContext; 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.archive.ProjectArchive;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; import ghidra.app.plugin.core.datamgr.tree.*;
import ghidra.app.plugin.core.datamgr.tree.ProjectArchiveNode;
import ghidra.framework.main.datatable.DomainFileContext; import ghidra.framework.main.datatable.DomainFileContext;
import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainFile;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
public class DataTypesActionContext extends ProgramActionContext implements DomainFileContext { public class DataTypesActionContext extends ProgramActionContext implements DomainFileContext {
@ -53,7 +55,7 @@ public class DataTypesActionContext extends ProgramActionContext implements Doma
return isToolbarAction; return isToolbarAction;
} }
public GTreeNode getSelectedNode() { public GTreeNode getClickedNode() {
return clickedNode; return clickedNode;
} }
@ -85,4 +87,49 @@ public class DataTypesActionContext extends ProgramActionContext implements Doma
return true; return true;
} }
public List<GTreeNode> getSelectedNodes() {
Object contextObject = getContextObject();
GTree gTree = (GTree) contextObject;
return gTree.getSelectedNodes();
}
public List<DataTypeNode> getDisassociatableNodes() {
Object contextObject = getContextObject();
GTree gTree = (GTree) contextObject;
TreePath[] selectionPaths = gTree.getSelectionPaths();
return getDisassociatableNodes(selectionPaths);
}
private List<DataTypeNode> getDisassociatableNodes(TreePath[] paths) {
List<DataTypeNode> 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;
}
} }

View file

@ -38,6 +38,7 @@ import docking.widgets.textpane.GHtmlTextPane;
import docking.widgets.tree.*; import docking.widgets.tree.*;
import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin; import docking.widgets.tree.support.GTreeSelectionEvent.EventOrigin;
import ghidra.app.plugin.core.datamgr.actions.*; 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.archive.*;
import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.app.plugin.core.datamgr.tree.*;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
@ -220,6 +221,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
// key binding only // key binding only
addLocalAction(new ClearCutAction(plugin)); // Common addLocalAction(new ClearCutAction(plugin)); // Common
addLocalAction(new AssociateDataTypeAction(plugin));
addLocalAction(new CommitSingleDataTypeAction(plugin)); addLocalAction(new CommitSingleDataTypeAction(plugin));
addLocalAction(new UpdateSingleDataTypeAction(plugin)); addLocalAction(new UpdateSingleDataTypeAction(plugin));
addLocalAction(new RevertDataTypeAction(plugin)); addLocalAction(new RevertDataTypeAction(plugin));

View file

@ -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<GTreeNode> nodes) {
if (nodes.isEmpty()) {
return false;
}
for (GTreeNode node : nodes) {
if (!(node instanceof DataTypeNode)) {
return false;
}
}
return true;
}
private boolean isAlreadyAssociated(DataTypesActionContext dtContext) {
List<DataTypeNode> nodes = dtContext.getDisassociatableNodes();
return !nodes.isEmpty();
}
private boolean hasSingleModifiableSourceArchive(List<GTreeNode> 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<Archive> getDestinationArchives() {
List<Archive> archives = plugin.getAllArchives();
List<Archive> 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<GTreeNode> 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<Archive> 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<Archive> archivesBox = new GhidraComboBox<>();
private JTextField categoryField = new JTextField(20);
ChooseArchiveDialog(List<Archive> 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;
}
}
}

View file

@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.*;
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; 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.program.model.data.SourceArchive;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import java.util.List;
import docking.action.MenuData;
public class CommitAction extends SyncAction { 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, public CommitAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler,
DataTypeManagerHandler dataTypeManagerHandler, DataTypeManager dtm, DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive,
ArchiveNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) { boolean isEnabled) {
super("Commit Changes To Archive", plugin, dataTypeManagerHandler, dtm, archiveNode, super("Commit Changes To Archive", plugin, dataTypeManagerHandler, dtm, archiveNode,
sourceArchive, isEnabled); sourceArchive, isEnabled);

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.ImageIcon;
import javax.swing.tree.TreePath; import javax.swing.tree.TreePath;

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.*;
@ -35,7 +35,7 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.task.*; import ghidra.util.task.*;
public class DisassociateAction extends DockingAction { 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 SourceArchive sourceArchive;
private final DataTypeManager dtm; private final DataTypeManager dtm;

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.*;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -28,7 +28,8 @@ import docking.action.MenuData;
import docking.widgets.OptionDialog; import docking.widgets.OptionDialog;
import docking.widgets.tree.*; import docking.widgets.tree.*;
import ghidra.app.plugin.core.datamgr.*; 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.DataTypeArchiveGTree;
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode; import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils; import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
@ -56,10 +57,8 @@ public class DisassociateDataTypeAction extends DockingAction {
return false; return false;
} }
Object contextObject = context.getContextObject(); DataTypesActionContext dtContext = (DataTypesActionContext) context;
GTree gTree = (GTree) contextObject; List<DataTypeNode> nodes = dtContext.getDisassociatableNodes();
TreePath[] selectionPaths = gTree.getSelectionPaths();
List<DataTypeNode> nodes = getDisassociatableNodes(selectionPaths);
return !nodes.isEmpty(); return !nodes.isEmpty();
} }

View file

@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.DataTypeManagerPlugin;
import ghidra.app.plugin.core.datamgr.DataTypeSyncInfo; import ghidra.app.plugin.core.datamgr.DataTypeSyncInfo;
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; 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.program.model.data.SourceArchive;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import java.util.List;
import docking.action.MenuData;
public class RevertAction extends SyncAction { 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, public RevertAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler,
DataTypeManagerHandler dataTypeManagerHandler, DataTypeManager dtm, DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive,
ArchiveNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) { boolean isEnabled) {
super("Revert Datatype Changes", plugin, dataTypeManagerHandler, dtm, archiveNode, super("Revert Data Type Changes", plugin, dataTypeManagerHandler, dtm, archiveNode,
sourceArchive, isEnabled); sourceArchive, isEnabled);
setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() })); setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() }));
setHelpLocation(new HelpLocation(plugin.getName(), getHelpTopic())); setHelpLocation(new HelpLocation(plugin.getName(), getHelpTopic()));

View file

@ -13,14 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.plugin.core.datamgr.actions; package ghidra.app.plugin.core.datamgr.actions.associate;
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;
import javax.swing.tree.TreePath; import javax.swing.tree.TreePath;
@ -29,6 +22,12 @@ import docking.action.DockingAction;
import docking.action.MenuData; import docking.action.MenuData;
import docking.widgets.tree.GTree; import docking.widgets.tree.GTree;
import docking.widgets.tree.GTreeNode; 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 { public class RevertDataTypeAction extends DockingAction {
@ -37,7 +36,7 @@ public class RevertDataTypeAction extends DockingAction {
public RevertDataTypeAction(DataTypeManagerPlugin plugin) { public RevertDataTypeAction(DataTypeManagerPlugin plugin) {
super("Revert Data Type", plugin.getName()); super("Revert Data Type", plugin.getName());
this.plugin = plugin; this.plugin = plugin;
setPopupMenuData(new MenuData(new String[] { "Revert" }, "Sync")); setPopupMenuData(new MenuData(new String[] { "Revert Changes" }, "Sync"));
setEnabled(true); setEnabled(true);
} }
@ -68,8 +67,8 @@ public class RevertDataTypeAction extends DockingAction {
case UNKNOWN: case UNKNOWN:
return false; return false;
case COMMIT: case COMMIT:
return true;
case CONFLICT: case CONFLICT:
return true;
case IN_SYNC: case IN_SYNC:
case ORPHAN: case ORPHAN:
case UPDATE: case UPDATE:
@ -88,7 +87,6 @@ public class RevertDataTypeAction extends DockingAction {
} }
GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent(); GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent();
if (node instanceof DataTypeNode) {
DataTypeNode dataTypeNode = (DataTypeNode) node; DataTypeNode dataTypeNode = (DataTypeNode) node;
DataType dataType = dataTypeNode.getDataType(); DataType dataType = dataTypeNode.getDataType();
DataTypeManager dtm = dataType.getDataTypeManager(); DataTypeManager dtm = dataType.getDataTypeManager();
@ -100,17 +98,15 @@ public class RevertDataTypeAction extends DockingAction {
} }
DataTypeManager sourceDTM = handler.getDataTypeManager(sourceArchive); DataTypeManager sourceDTM = handler.getDataTypeManager(sourceArchive);
if (sourceDTM == null) { if (sourceDTM == null) {
Msg.showInfo(getClass(), gTree, "Revert Failed", "Source Archive not open: " + Msg.showInfo(getClass(), gTree, "Revert Failed",
sourceArchive.getName()); "Source Archive not open: " + sourceArchive.getName());
return; return;
} }
plugin.revert(dataType); plugin.revert(dataType);
// Source archive data type manager was already checked for null above. // Source archive data type manager was already checked for null above.
DataTypeSynchronizer synchronizer = DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, sourceArchive);
new DataTypeSynchronizer(handler, dtm, sourceArchive);
synchronizer.reSyncOutOfSyncInTimeOnlyDataTypes(); synchronizer.reSyncOutOfSyncInTimeOnlyDataTypes();
} }
}
} }

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.*;
@ -83,7 +83,6 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, sourceArchive);
if (!dtm.isUpdatable()) { if (!dtm.isUpdatable()) {
showRequiresArchiveOpenMessage(dtm.getName()); showRequiresArchiveOpenMessage(dtm.getName());
@ -103,6 +102,8 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
return; return;
} }
DataTypeSynchronizer synchronizer = new DataTypeSynchronizer(handler, dtm, sourceArchive);
//@formatter:off //@formatter:off
TaskBuilder.withTask(new SyncTask(synchronizer)) TaskBuilder.withTask(new SyncTask(synchronizer))
.setStatusTextAlignment(SwingConstants.LEADING) .setStatusTextAlignment(SwingConstants.LEADING)
@ -279,7 +280,7 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
case UNKNOWN: case UNKNOWN:
} }
} }
StringBuffer buf = new StringBuffer(); StringBuilder buf = new StringBuilder();
if (updateCount > 0) { if (updateCount > 0) {
buf.append("\nNumber of UPDATES remaining: " + updateCount); buf.append("\nNumber of UPDATES remaining: " + updateCount);
} }
@ -340,7 +341,7 @@ public abstract class SyncAction extends DockingAction implements Comparable<Syn
private void autoUpdateDataTypesThatHaveNoRealChanges(DataTypeSynchronizer synchronizer, private void autoUpdateDataTypesThatHaveNoRealChanges(DataTypeSynchronizer synchronizer,
List<DataTypeSyncInfo> outOfSynchInTimeOnlyList, boolean markArchiveSynchronized) { List<DataTypeSyncInfo> outOfSynchInTimeOnlyList, boolean markArchiveSynchronized) {
int transactionID = dtm.startTransaction("auto sync datatypes"); int transactionID = dtm.startTransaction("Auto-sync data types");
try { try {
for (DataTypeSyncInfo dataTypeSyncInfo : outOfSynchInTimeOnlyList) { for (DataTypeSyncInfo dataTypeSyncInfo : outOfSynchInTimeOnlyList) {
dataTypeSyncInfo.syncTimes(); dataTypeSyncInfo.syncTimes();

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.plugin.core.datamgr.actions; package ghidra.app.plugin.core.datamgr.actions.associate;
import docking.ActionContext; import docking.ActionContext;
import docking.action.DockingAction; import docking.action.DockingAction;

View file

@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.*;
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler; import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
import ghidra.app.plugin.core.datamgr.tree.ArchiveNode; 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.program.model.data.SourceArchive;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import java.util.List;
import docking.action.MenuData;
public class UpdateAction extends SyncAction { 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, public UpdateAction(DataTypeManagerPlugin plugin, DataTypeManagerHandler dataTypeManagerHandler,
DataTypeManagerHandler dataTypeManagerHandler, DataTypeManager dtm, DataTypeManager dtm, ArchiveNode archiveNode, SourceArchive sourceArchive,
ArchiveNode archiveNode, SourceArchive sourceArchive, boolean isEnabled) { boolean isEnabled) {
super("Update Datatypes From Archive", plugin, dataTypeManagerHandler, dtm, archiveNode, super("Update Data Types From Archive", plugin, dataTypeManagerHandler, dtm, archiveNode,
sourceArchive, isEnabled); sourceArchive, isEnabled);
setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() })); setPopupMenuData(new MenuData(new String[] { MENU_NAME, sourceArchive.getName() }));

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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.ImageIcon;
import javax.swing.tree.TreePath; import javax.swing.tree.TreePath;

View file

@ -34,6 +34,7 @@ import docking.widgets.OptionDialog;
import ghidra.app.plugin.core.compositeeditor.EditorListener; import ghidra.app.plugin.core.compositeeditor.EditorListener;
import ghidra.app.plugin.core.compositeeditor.EditorProvider; import ghidra.app.plugin.core.compositeeditor.EditorProvider;
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin; import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
import ghidra.app.services.DataTypeManagerService;
import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum; import ghidra.program.model.data.Enum;
@ -285,19 +286,28 @@ public class EnumEditorProvider extends ComponentProviderAdapter
deleteAction = deleteAction =
new EnumPluginAction("Delete Enum Value", e -> editorPanel.deleteSelectedEntries()); new EnumPluginAction("Delete Enum Value", e -> editorPanel.deleteSelectedEntries());
deleteAction.setEnabled(false); deleteAction.setEnabled(false);
deleteAction.setPopupMenuData( deleteAction
new MenuData(new String[] { "Delete" }, DELETE_ICON, editGroup)); .setPopupMenuData(new MenuData(new String[] { "Delete" }, DELETE_ICON, editGroup));
deleteAction.setToolBarData(new ToolBarData(DELETE_ICON, editGroup)); deleteAction.setToolBarData(new ToolBarData(DELETE_ICON, editGroup));
deleteAction.setDescription("Delete the selected enum entries"); deleteAction.setDescription("Delete the selected enum entries");
applyAction = new EnumPluginAction("Apply Enum Changes", e -> applyChanges()); applyAction = new EnumPluginAction("Apply Enum Changes", e -> applyChanges());
applyAction.setEnabled(false); 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"); 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, applyAction);
tool.addLocalAction(this, addAction); tool.addLocalAction(this, addAction);
tool.addLocalAction(this, deleteAction); tool.addLocalAction(this, deleteAction);
tool.addLocalAction(this, showEnumAction);
} }
private boolean applyChanges() { private boolean applyChanges() {
@ -338,6 +348,11 @@ public class EnumEditorProvider extends ComponentProviderAdapter
return true; 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. * 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 * @param editedEnum the enum to check for conflicts with

View file

@ -27,12 +27,13 @@ import ghidra.app.plugin.core.datamgr.archive.ProgramArchive;
import ghidra.app.plugin.core.datamgr.tree.*; import ghidra.app.plugin.core.datamgr.tree.*;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.util.*; 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.Task;
import ghidra.util.task.TaskMonitor; 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 { public class DataTypeTreeCopyMoveTask extends Task {
@ -48,11 +49,11 @@ public class DataTypeTreeCopyMoveTask extends Task {
} }
private DataTypeArchiveGTree gTree; private DataTypeArchiveGTree gTree;
private CategoryNode destinationNode; private Category destinationCategory;
private List<GTreeNode> droppedNodes; private List<GTreeNode> copyMoveNodes;
private Archive sourceArchive; private Archive sourceArchive;
private Archive destinationArchive; private Archive destinationArchive;
private boolean promptToAssociateTypes = true;
private ActionType actionType; private ActionType actionType;
private DataTypeConflictHandler conflictHandler; private DataTypeConflictHandler conflictHandler;
private List<String> errors = new ArrayList<>(); private List<String> errors = new ArrayList<>();
@ -65,19 +66,26 @@ public class DataTypeTreeCopyMoveTask extends Task {
public DataTypeTreeCopyMoveTask(CategoryNode destinationNode, List<GTreeNode> droppedNodeList, public DataTypeTreeCopyMoveTask(CategoryNode destinationNode, List<GTreeNode> droppedNodeList,
ActionType actionType, DataTypeArchiveGTree gTree, ActionType actionType, DataTypeArchiveGTree gTree,
DataTypeConflictHandler conflictHandler) { DataTypeConflictHandler conflictHandler) {
this(findArchive(destinationNode), destinationNode.getCategory(), droppedNodeList,
actionType, gTree, conflictHandler);
}
public DataTypeTreeCopyMoveTask(Archive destinationArchive, Category destinationCategory,
List<GTreeNode> droppedNodeList, ActionType actionType, DataTypeArchiveGTree gTree,
DataTypeConflictHandler conflictHandler) {
super("Drag/Drop", true, true, true); super("Drag/Drop", true, true, true);
this.destinationNode = destinationNode; this.destinationCategory = destinationCategory;
this.droppedNodes = droppedNodeList; this.copyMoveNodes = droppedNodeList;
this.actionType = actionType; this.actionType = actionType;
this.gTree = gTree; this.gTree = gTree;
this.conflictHandler = conflictHandler; this.conflictHandler = conflictHandler;
this.destinationArchive = findArchive(destinationNode); this.destinationArchive = destinationArchive;
GTreeNode firstNode = droppedNodes.get(0); GTreeNode firstNode = copyMoveNodes.get(0);
this.sourceArchive = findArchive(firstNode); this.sourceArchive = findArchive(firstNode);
} }
private Archive findArchive(GTreeNode node) { private static Archive findArchive(GTreeNode node) {
while (node != null) { while (node != null) {
if (node instanceof ArchiveNode) { if (node instanceof ArchiveNode) {
return ((ArchiveNode) node).getArchive(); return ((ArchiveNode) node).getArchive();
@ -87,10 +95,22 @@ public class DataTypeTreeCopyMoveTask extends Task {
return null; 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 @Override
public void run(TaskMonitor monitor) throws CancelledException { public void run(TaskMonitor monitor) throws CancelledException {
int nodeCount = droppedNodes.size(); int nodeCount = copyMoveNodes.size();
filterRedundantNodes(); filterRedundantNodes();
if (checkForDifferentSourceArchives()) { if (checkForDifferentSourceArchives()) {
@ -143,10 +163,10 @@ public class DataTypeTreeCopyMoveTask extends Task {
private boolean checkForDifferentSourceArchives() { private boolean checkForDifferentSourceArchives() {
for (GTreeNode node : droppedNodes) { for (GTreeNode node : copyMoveNodes) {
if (sourceArchive != findArchive(node)) { if (sourceArchive != findArchive(node)) {
Msg.showError(this, gTree, "Copy Failed", 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; return true;
} }
} }
@ -158,7 +178,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
DataTypeManager dtm = destinationArchive.getDataTypeManager(); DataTypeManager dtm = destinationArchive.getDataTypeManager();
int txId = dtm.startTransaction("Copy/Move Category/DataType"); int txId = dtm.startTransaction("Copy/Move Category/DataType");
try { try {
dragNodesToCategory(monitor); copyOrMoveNodesToCategory(monitor);
} }
finally { finally {
dtm.endTransaction(txId, true); dtm.endTransaction(txId, true);
@ -187,13 +207,13 @@ public class DataTypeTreeCopyMoveTask extends Task {
return; return;
} }
monitor.initialize(droppedNodes.size()); monitor.initialize(copyMoveNodes.size());
SourceArchive destination = destinationArchive.getDataTypeManager().getLocalSourceArchive(); SourceArchive destination = destinationArchive.getDataTypeManager().getLocalSourceArchive();
DataTypeManager dtm = sourceArchive.getDataTypeManager(); DataTypeManager dtm = sourceArchive.getDataTypeManager();
int txId = dtm.startTransaction("Associate Data Types"); int txId = dtm.startTransaction("Associate Data Types");
try { try {
for (GTreeNode node : droppedNodes) { for (GTreeNode node : copyMoveNodes) {
monitor.checkCanceled(); monitor.checkCanceled();
if (node instanceof DataTypeNode) { if (node instanceof DataTypeNode) {
@ -215,6 +235,10 @@ public class DataTypeTreeCopyMoveTask extends Task {
private boolean promptToAssociateTypes(TaskMonitor monitor) throws CancelledException { private boolean promptToAssociateTypes(TaskMonitor monitor) throws CancelledException {
if (!promptToAssociateTypes) {
return true; // do not prompt; always associate
}
if (!containsUnassociatedTypes(monitor)) { if (!containsUnassociatedTypes(monitor)) {
return false; // nothing to associate return false; // nothing to associate
} }
@ -230,8 +254,8 @@ public class DataTypeTreeCopyMoveTask extends Task {
private boolean containsUnassociatedTypes(TaskMonitor monitor) throws CancelledException { private boolean containsUnassociatedTypes(TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Checking for types to associate"); monitor.setMessage("Checking for types to associate");
monitor.initialize(droppedNodes.size()); monitor.initialize(copyMoveNodes.size());
for (GTreeNode node : droppedNodes) { for (GTreeNode node : copyMoveNodes) {
monitor.checkCanceled(); monitor.checkCanceled();
if (node instanceof DataTypeNode) { 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.setMessage("Drag/Drop Categories/Data Types");
monitor.initialize(droppedNodes.size()); monitor.initialize(copyMoveNodes.size());
Category toCategory = getCategory(destinationNode); Category toCategory = destinationCategory;
for (GTreeNode node : droppedNodes) { for (GTreeNode node : copyMoveNodes) {
if (monitor.isCancelled()) { if (monitor.isCancelled()) {
break; 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 dtName = dataType.getName();
String baseName = getBaseName(dtName); String baseName = getBaseName(dtName);
String copyName = getNextCopyName(destinationCategory, baseName); String copyName = getNextCopyName(toCategory, baseName);
try { try {
dataType.setName(copyName); dataType.setName(copyName);
} }
@ -386,12 +410,12 @@ public class DataTypeTreeCopyMoveTask extends Task {
return baseName; return baseName;
} }
String getNextCopyName(Category destinationCategory, String baseName) { String getNextCopyName(Category toCategory, String baseName) {
String format = "Copy_%d_of_" + baseName; String format = "Copy_%d_of_" + baseName;
for (int i = 1; i < 100; i++) { for (int i = 1; i < 100; i++) {
String copyName = String.format(format, i); String copyName = String.format(format, i);
if (destinationCategory.getDataType(copyName) == null) { if (toCategory.getDataType(copyName) == null) {
return copyName; return copyName;
} }
} }
@ -400,14 +424,14 @@ public class DataTypeTreeCopyMoveTask extends Task {
return String.format(format, System.currentTimeMillis()); 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) { if (node instanceof DataTypeNode) {
DataType dataType = ((DataTypeNode) node).getDataType(); DataType dataType = ((DataTypeNode) node).getDataType();
moveDataType(destinationCategory, dataType); moveDataType(toCategory, dataType);
} }
else if (node instanceof CategoryNode) { else if (node instanceof CategoryNode) {
Category category = ((CategoryNode) node).getCategory(); 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); 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 * 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 * type manager. This is false if copying a new type from the program to an
@ -490,7 +503,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
// filters out nodes with categories in their path // filters out nodes with categories in their path
private void filterRedundantNodes() { private void filterRedundantNodes() {
Set<GTreeNode> nodeSet = new HashSet<>(droppedNodes); Set<GTreeNode> nodeSet = new HashSet<>(copyMoveNodes);
List<GTreeNode> filteredList = new ArrayList<>(); List<GTreeNode> filteredList = new ArrayList<>();
for (GTreeNode node : nodeSet) { for (GTreeNode node : nodeSet) {
@ -499,7 +512,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
} }
} }
droppedNodes = filteredList; copyMoveNodes = filteredList;
} }
private boolean containsAncestor(Set<GTreeNode> nodeSet, GTreeNode node) { private boolean containsAncestor(Set<GTreeNode> nodeSet, GTreeNode node) {

View file

@ -22,7 +22,6 @@ import java.util.List;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.services.DataTypeQueryService; import ghidra.app.services.DataTypeQueryService;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.data.Enum; import ghidra.program.model.data.Enum;
@ -150,6 +149,7 @@ public class DataTypeUtils {
/** /**
* Returns the root folder icon. * Returns the root folder icon.
* @param expanded true to use the expanded icon; false to use the collapsed icon.
* @return the root folder icon. * @return the root folder icon.
*/ */
public static Icon getRootIcon(boolean expanded) { public static Icon getRootIcon(boolean expanded) {
@ -217,36 +217,6 @@ public class DataTypeUtils {
return closedArchiveFolderIcon; 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. * Returns the BuiltIn icon.
* *
@ -320,10 +290,10 @@ public class DataTypeUtils {
} }
/** /**
* Returns a sorted list of {@link DataType}s that have names which start with the given * Returns a sorted list of {@link DataType}s that have names which start with the given search
* search string. The list is sorted according to {@link #DATA_TYPE_LOOKUP_COMPARATOR}. * string. The list is sorted according to {@link #DATA_TYPE_LOOKUP_COMPARATOR}.
* *
@param searchString The name of the DataTypes to match. * @param searchString The name of the DataTypes to match.
* @param dataService The service from which the data types will be taken. * @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. * string.
@ -350,10 +320,13 @@ public class DataTypeUtils {
} }
/** /**
* Changes the give text to prepare it or use in searching for data types. Clients should * 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 * call this method to make sure that the given text is suitable for use when searching the
* the data type values returned by {@link #getExactMatchingDataTypes(String, DataTypeManagerService)} * data type values returned by
* and {@link #getStartsWithMatchingDataTypes(String, DataTypeManagerService)}. * {@link #getExactMatchingDataTypes(String, DataTypeQueryService)} and
* {@link #getStartsWithMatchingDataTypes(String, DataTypeQueryService)}.
* @param searchText the search text
* @return the updated text
*/ */
public static String prepareSearchText(String searchText) { public static String prepareSearchText(String searchText) {
return searchText.replaceAll(" ", ""); return searchText.replaceAll(" ", "");
@ -376,12 +349,14 @@ public class DataTypeUtils {
/** /**
* Get the base data type for the specified data type. * Get the base data type for the specified data type.
* <br>For example, the base data type for Word*[5] is Word. *
* For a pointer, the base data type is the type being pointed to * <p>For example, the base data type for Word*[5] is Word. For a pointer, the base data type
* or the pointer itself if it is pointing at nothing. * is the type being pointed to or the pointer itself if it is pointing at nothing.
* <br>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] * <p>If "INT" is a typedef on a "dword" then INT[7][3] would have a base data type of dword.
* you should call getNamedBasedDataType(DataType) instead. * 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. * @param dt the data type whose base data type is to be determined.
* @return the base data type. * @return the base data type.
*/ */
@ -409,15 +384,17 @@ public class DataTypeUtils {
} }
/** /**
* Get the named base data type for the specified data type. * Get the named base data type for the specified data type. This method intentionally does
* This method intentionally does not drill down into typedefs. * not drill down into typedefs.
* <br>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 * <p>For example, the named base data type for Word*[5] is Word. For a pointer, the named
* or the pointer itself if it is pointing at nothing. * base data type is the type being pointed to or the pointer itself if it is pointing at
* <br>If "INT" is a typedef on a "dword", then INT[7][3] would * nothing.
* have a named base data type of INT. *
* If you wanted to get the dword from INT[7][3] * <p>If "INT" is a typedef on a "dword", then INT[7][3] would have a named base data type of
* you should call getBasedDataType(DataType) instead. * 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. * @param dt the data type whose named base data type is to be determined.
* @return the base data type. * @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 * Create a copy of the chain of data types that eventually lead to a named
* data type. * data type.
* <p> * <p>
* Returns a {@link DataType#copy(DataTypeManager) copy()} of the first named data * Returns a {@link DataType#copy(DataTypeManager) copy()} of the first named data type found
* type found in the pointer / array type chain, and returns an identical chain of * in the pointer / array type chain, and returns an identical chain of pointer / arrays up to
* pointer / arrays up to the copied named type. * the copied named type.
* <p> *
* @param dataType data type to be copied * @param dataType data type to be copied
* @param dtm data type manager * @param dtm data type manager
* @return deep copy of dataType * @return deep copy of dataType
@ -475,39 +452,16 @@ public class DataTypeUtils {
msg = "The Program is not modifiable!\n"; msg = "The Program is not modifiable!\n";
} }
else if (dtm instanceof FileArchiveBasedDataTypeManager) { else if (dtm instanceof FileArchiveBasedDataTypeManager) {
msg = msg = "The archive file is not modifiable!\nYou must open the archive for editing\n" +
"The archive file is not modifiable!\nYou must open the archive for editing\n before performing this operation."; "before performing this operation.\n" + dtm.getName();
} }
else { else {
msg = msg = "The project archive is not modifiable!\nYou must check out the archive\n" +
"The project archive is not modifiable!\nYou must check out the archive\n before performing this operation."; "before performing this operation.\n" + dtm.getName();
} }
Msg.showInfo(DataTypeUtils.class, parent, title, msg); 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 );
// }
} }
//================================================================================================== //==================================================================================================

View file

@ -15,15 +15,14 @@
*/ */
package ghidra.app.plugin.core.compositeeditor; package ghidra.app.plugin.core.compositeeditor;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.task.TaskMonitorAdapter; import ghidra.util.task.TaskMonitor;
public class StructureEditorLockedEnablementTest extends AbstractStructureEditorTest { public class StructureEditorLockedEnablementTest extends AbstractStructureEditorTest {
@ -34,7 +33,7 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
try { try {
DataTypeManager dataTypeManager = cat.getDataTypeManager(); DataTypeManager dataTypeManager = cat.getDataTypeManager();
if (dt.getDataTypeManager() != dataTypeManager) { if (dt.getDataTypeManager() != dataTypeManager) {
dt = (Structure) dt.clone(dataTypeManager); dt = dt.clone(dataTypeManager);
} }
CategoryPath categoryPath = cat.getCategoryPath(); CategoryPath categoryPath = cat.getCategoryPath();
if (!dt.getCategoryPath().equals(categoryPath)) { if (!dt.getCategoryPath().equals(categoryPath)) {
@ -69,7 +68,7 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
Structure desiredEmptyStructure = emptyStructure; Structure desiredEmptyStructure = emptyStructure;
int txID = program.startTransaction("Removing emptyStruct from DTM."); int txID = program.startTransaction("Removing emptyStruct from DTM.");
try { try {
programDTM.remove(emptyStructure, TaskMonitorAdapter.DUMMY_MONITOR); programDTM.remove(emptyStructure, TaskMonitor.DUMMY);
if (emptyStructure.getDataTypeManager() != catDTM) { if (emptyStructure.getDataTypeManager() != catDTM) {
desiredEmptyStructure = (Structure) emptyStructure.copy(catDTM); desiredEmptyStructure = (Structure) emptyStructure.copy(catDTM);
desiredEmptyStructure.setCategoryPath(pgmTestCat.getCategoryPath()); desiredEmptyStructure.setCategoryPath(pgmTestCat.getCategoryPath());
@ -154,7 +153,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
for (CompositeEditorTableAction action : actions) { for (CompositeEditorTableAction action : actions) {
if ((action instanceof EditFieldAction) || (action instanceof AddBitFieldAction) || if ((action instanceof EditFieldAction) || (action instanceof AddBitFieldAction) ||
(action instanceof InsertUndefinedAction) || (action instanceof PointerAction) || (action instanceof InsertUndefinedAction) || (action instanceof PointerAction) ||
(action instanceof HexNumbersAction)) { (action instanceof HexNumbersAction) ||
(action instanceof ShowDataTypeInTreeAction)) {
checkEnablement(action, true); checkEnablement(action, true);
} }
else if (action instanceof FavoritesAction) { else if (action instanceof FavoritesAction) {
@ -203,7 +203,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
(action instanceof DuplicateMultipleAction) || (action instanceof ClearAction) || (action instanceof DuplicateMultipleAction) || (action instanceof ClearAction) ||
(action instanceof DeleteAction) || (action instanceof ArrayAction) || (action instanceof DeleteAction) || (action instanceof ArrayAction) ||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) || (action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
(action instanceof CreateInternalStructureAction)) { (action instanceof CreateInternalStructureAction) ||
(action instanceof ShowDataTypeInTreeAction)) {
checkEnablement(action, true); checkEnablement(action, true);
} }
else { else {
@ -232,7 +233,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
(action instanceof MoveDownAction) || (action instanceof ClearAction) || (action instanceof MoveDownAction) || (action instanceof ClearAction) ||
(action instanceof DeleteAction) || (action instanceof ArrayAction) || (action instanceof DeleteAction) || (action instanceof ArrayAction) ||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) || (action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
(action instanceof CreateInternalStructureAction)) { (action instanceof CreateInternalStructureAction) ||
(action instanceof ShowDataTypeInTreeAction)) {
checkEnablement(action, true); checkEnablement(action, true);
} }
else { else {
@ -261,7 +263,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
(action instanceof DuplicateAction) || (action instanceof DuplicateAction) ||
(action instanceof DuplicateMultipleAction) || (action instanceof ArrayAction) || (action instanceof DuplicateMultipleAction) || (action instanceof ArrayAction) ||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) || (action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
(action instanceof CreateInternalStructureAction)) { (action instanceof CreateInternalStructureAction) ||
(action instanceof ShowDataTypeInTreeAction)) {
checkEnablement(action, true); checkEnablement(action, true);
} }
else { else {
@ -289,7 +292,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
(action instanceof MoveDownAction) || (action instanceof ClearAction) || (action instanceof MoveDownAction) || (action instanceof ClearAction) ||
(action instanceof DeleteAction) || (action instanceof ArrayAction) || (action instanceof DeleteAction) || (action instanceof ArrayAction) ||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) || (action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
(action instanceof CreateInternalStructureAction)) { (action instanceof CreateInternalStructureAction) ||
(action instanceof ShowDataTypeInTreeAction)) {
checkEnablement(action, true); checkEnablement(action, true);
} }
else { else {
@ -313,7 +317,8 @@ public class StructureEditorLockedEnablementTest extends AbstractStructureEditor
checkEnablement(action, len <= numBytes); checkEnablement(action, len <= numBytes);
} }
else if ((action instanceof CycleGroupAction) || (action instanceof ClearAction) || 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); checkEnablement(action, true);
} }
else { else {

View file

@ -118,7 +118,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
if ((action instanceof FavoritesAction) || (action instanceof CycleGroupAction) || if ((action instanceof FavoritesAction) || (action instanceof CycleGroupAction) ||
(action instanceof EditFieldAction) || (action instanceof InsertUndefinedAction) || (action instanceof EditFieldAction) || (action instanceof InsertUndefinedAction) ||
(action instanceof AddBitFieldAction) || (action instanceof PointerAction) || (action instanceof AddBitFieldAction) || (action instanceof PointerAction) ||
(action instanceof HexNumbersAction)) { (action instanceof HexNumbersAction) ||
(action instanceof ShowDataTypeInTreeAction)) {
checkEnablement(action, true); checkEnablement(action, true);
} }
else { else {
@ -151,7 +152,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
(action instanceof DuplicateMultipleAction) || (action instanceof DeleteAction) || (action instanceof DuplicateMultipleAction) || (action instanceof DeleteAction) ||
(action instanceof ArrayAction) || (action instanceof PointerAction) || (action instanceof ArrayAction) || (action instanceof PointerAction) ||
(action instanceof HexNumbersAction) || (action instanceof HexNumbersAction) ||
(action instanceof CreateInternalStructureAction)) { (action instanceof CreateInternalStructureAction) ||
(action instanceof ShowDataTypeInTreeAction)) {
checkEnablement(action, true); checkEnablement(action, true);
} }
else if (action instanceof FavoritesAction) { else if (action instanceof FavoritesAction) {
@ -190,7 +192,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
(action instanceof MoveUpAction) || (action instanceof ClearAction) || (action instanceof MoveUpAction) || (action instanceof ClearAction) ||
(action instanceof DeleteAction) || (action instanceof ArrayAction) || (action instanceof DeleteAction) || (action instanceof ArrayAction) ||
(action instanceof PointerAction) || (action instanceof HexNumbersAction) || (action instanceof PointerAction) || (action instanceof HexNumbersAction) ||
(action instanceof CreateInternalStructureAction)) { (action instanceof CreateInternalStructureAction) ||
(action instanceof ShowDataTypeInTreeAction)) {
checkEnablement(action, true); checkEnablement(action, true);
} }
else if (action instanceof FavoritesAction) { else if (action instanceof FavoritesAction) {
@ -230,7 +233,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
(action instanceof DuplicateMultipleAction) || (action instanceof DeleteAction) || (action instanceof DuplicateMultipleAction) || (action instanceof DeleteAction) ||
(action instanceof ArrayAction) || (action instanceof PointerAction) || (action instanceof ArrayAction) || (action instanceof PointerAction) ||
(action instanceof HexNumbersAction) || (action instanceof HexNumbersAction) ||
(action instanceof CreateInternalStructureAction)) { (action instanceof CreateInternalStructureAction) ||
(action instanceof ShowDataTypeInTreeAction)) {
checkEnablement(action, true); checkEnablement(action, true);
} }
else if (action instanceof FavoritesAction) { else if (action instanceof FavoritesAction) {
@ -269,7 +273,8 @@ public class StructureEditorUnlockedEnablementTest extends AbstractStructureEdit
if ((action instanceof FavoritesAction) || (action instanceof CycleGroupAction) || if ((action instanceof FavoritesAction) || (action instanceof CycleGroupAction) ||
(action instanceof EditFieldAction) || (action instanceof InsertUndefinedAction) || (action instanceof EditFieldAction) || (action instanceof InsertUndefinedAction) ||
(action instanceof AddBitFieldAction) || (action instanceof PointerAction) || (action instanceof AddBitFieldAction) || (action instanceof PointerAction) ||
(action instanceof HexNumbersAction)) { (action instanceof HexNumbersAction) ||
(action instanceof ShowDataTypeInTreeAction)) {
checkEnablement(action, true); checkEnablement(action, true);
} }
else { else {

View file

@ -110,9 +110,9 @@ public class DataTypeTestUtils {
public static ArchiveNode openArchive(String archiveName, boolean checkout, public static ArchiveNode openArchive(String archiveName, boolean checkout,
DataTypeManagerPlugin plugin) throws Exception { DataTypeManagerPlugin plugin) throws Exception {
ArchiveNode openArchive = openArchive(archiveName, checkout, false, plugin); ArchiveNode archiveNode = openArchive(archiveName, checkout, false, plugin);
waitForTree(plugin); waitForTree(plugin);
return openArchive; return archiveNode;
} }
private static void waitForTree(DataTypeManagerPlugin plugin) { private static void waitForTree(DataTypeManagerPlugin plugin) {

View file

@ -93,7 +93,7 @@ public class DeveloperDumpAllTypesScript extends GhidraScript {
} }
String userChoice = String userChoice =
OptionDialog.showInputChoiceDialog(null, "Choose a Data Type Manager or Cancel", 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) { if (userChoice == null) {
return null; return null;
} }

View file

@ -59,8 +59,8 @@ import docking.widgets.GComponent;
* @param <E> the item type * @param <E> the item type
*/ */
public class GhidraComboBox<E> extends JComboBox<E> implements GComponent { public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
private ArrayList<ActionListener> listeners = new ArrayList<>(); private List<ActionListener> listeners = new ArrayList<>();
private ArrayList<DocumentListener> docListeners = new ArrayList<>(); private List<DocumentListener> docListeners = new ArrayList<>();
private boolean setSelectedFlag = false; private boolean setSelectedFlag = false;
private boolean forwardEnter; private boolean forwardEnter;
@ -70,7 +70,6 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
* Default constructor. * Default constructor.
*/ */
public GhidraComboBox() { public GhidraComboBox() {
super();
init(); init();
} }
@ -256,6 +255,13 @@ public class GhidraComboBox<E> extends JComboBox<E> implements GComponent {
model.addElement(obj); model.addElement(obj);
} }
public void addToModel(Collection<E> items) {
DefaultComboBoxModel<E> model = (DefaultComboBoxModel<E>) getModel();
for (E e : items) {
model.addElement(e);
}
}
public boolean containsItem(E obj) { public boolean containsItem(E obj) {
DefaultComboBoxModel<E> model = (DefaultComboBoxModel<E>) getModel(); DefaultComboBoxModel<E> model = (DefaultComboBoxModel<E>) getModel();
return model.getIndexOf(obj) != -1; return model.getIndexOf(obj) != -1;

View file

@ -28,7 +28,6 @@ import docking.widgets.label.GHtmlLabel;
/** /**
* A dialog that has text fields to get user input. * A dialog that has text fields to get user input.
*
*/ */
public class InputWithChoicesDialog extends DialogComponentProvider { public class InputWithChoicesDialog extends DialogComponentProvider {
@ -37,19 +36,17 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
private boolean allowEdits; private boolean allowEdits;
/** /**
* Creates a provider for a generic input dialog with the specified title, * Creates a provider for a generic input dialog with the specified title, a label and a
* a label and a editable comboBox pre-populated with selectable values. The user * editable comboBox pre-populated with selectable values. The user can check the value of
* can check the value of {@link #isCanceled()} to know whether or not * {@link #isCanceled()} to know whether or not the user canceled the operation. To get the
* the user canceled the operation. To get the user selected value use the * user selected value use the {@link #getValue()} value(s) entered by the user. If the user
* {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then * cancelled the operation, then null will be returned from {@link #getValue()}.
* null will be returned from <code>getValue()</code>.
* <P>
* *
* @param dialogTitle used as the name of the dialog's title bar * @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 label value to use for the label of the text field
* @param optionValues values to populate the combo box * @param optionValues values to populate the combo box
* @param initialValue the initial value - can be null * @param initialValue the initial value; may be null
* @param messageIcon the icon to display on the dialog--can be null * @param messageIcon the icon to display on the dialog; may be null
*/ */
public InputWithChoicesDialog(String dialogTitle, String label, String[] optionValues, public InputWithChoicesDialog(String dialogTitle, String label, String[] optionValues,
String initialValue, Icon messageIcon) { 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, * Creates a provider for a generic input dialog with the specified title, a label and a
* a label and a editable comboBox pre-populated with selectable values. The user * editable comboBox pre-populated with selectable values. The user can check the value of
* can check the value of {@link #isCanceled()} to know whether or not * {@link #isCanceled()} to know whether or not the user canceled the operation. To get the
* the user canceled the operation. To get the user selected value use the * user selected value use the {@link #getValue()} value(s) entered by the user. If the user
* {@link #getValue()} value(s) entered by the user. If the user cancelled the operation, then * cancelled the operation, then null will be returned from {@link #getValue()}.
* null will be returned from <code>getValue()</code>.
* <P>
* *
* @param dialogTitle used as the name of the dialog's title bar * @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 label value to use for the label of the text field
* @param optionValues values to populate the combo box * @param optionValues values to populate the combo box
* @param initialValue the initial value - can be null * @param initialValue the initial value; may be null
* @param allowEdits true allows the user to add custom entries to the combo box by entering text * @param allowEdits true allows the user to add custom entries by entering text
* @param messageIcon the icon to display on the dialog--can be null * @param messageIcon the icon to display on the dialog; may be null
*/ */
public InputWithChoicesDialog(String dialogTitle, String label, String[] optionValues, public InputWithChoicesDialog(String dialogTitle, String label, String[] optionValues,
String initialValue, boolean allowEdits, Icon messageIcon) { 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, private void buildMainPanel(String labelText, String[] optionValues, String initialValue,
Icon messageIcon) { Icon messageIcon) {
@ -168,13 +163,15 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
/** /**
* Returns if this dialog is canceled. * Returns if this dialog is canceled.
* @return true if canceled
*/ */
public boolean isCanceled() { public boolean isCanceled() {
return 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() { public String getValue() {
if (isCanceled) { if (isCanceled) {
@ -192,8 +189,7 @@ public class InputWithChoicesDialog extends DialogComponentProvider {
/** /**
* Set the current choice to value. * Set the current choice to value.
* @param value updated choice * @param value updated choice
* @throws NoSuchElementException if choice does not permit edits and value is * @throws NoSuchElementException if edits not permitted and value is not a valid choice
* not a valid choice.
*/ */
public void setValue(String value) { public void setValue(String value) {
combo.setSelectedItem(value); combo.setSelectedItem(value);

View file

@ -27,6 +27,7 @@ import java.util.*;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.swing.*; import javax.swing.*;
import javax.swing.Timer; import javax.swing.Timer;
@ -916,6 +917,13 @@ public class GTree extends JPanel implements BusyListener {
return paths; return paths;
} }
public List<GTreeNode> getSelectedNodes() {
TreePath[] paths = getSelectionPaths();
return Arrays.stream(paths)
.map(tp -> (GTreeNode) tp.getLastPathComponent())
.collect(Collectors.toList());
}
public boolean isExpanded(TreePath treePath) { public boolean isExpanded(TreePath treePath) {
return tree.isExpanded(treePath); return tree.isExpanded(treePath);
} }

View file

@ -15,8 +15,9 @@
*/ */
package generic.test; package generic.test;
import static org.junit.Assert.assertNull; import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail; import static org.hamcrest.core.StringContains.*;
import static org.junit.Assert.*;
import java.io.File; import java.io.File;
import java.util.*; import java.util.*;
@ -298,6 +299,15 @@ public abstract class AbstractGTest {
} }
} }
public static <T> void assertContainsString(String expected, String actual) {
assertThat(actual, containsString(expected));
}
public static <T> 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, private static String printListFailureMessage(String message, List<?> expected,
List<?> actual) { List<?> actual) {

View file

@ -416,6 +416,14 @@ public interface DataTypeManager {
*/ */
public void findEnumValueNames(long value, Set<String> enumValueNames); public void findEnumValueNames(long value, Set<String> 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); public DataType getDataType(SourceArchive sourceArchive, UniversalID datatypeID);
/** /**
@ -529,6 +537,7 @@ public interface DataTypeManager {
* @deprecated the method {@link DataType#getParents()} should be used instead. * @deprecated the method {@link DataType#getParents()} should be used instead.
* Use of {@link Set} implementations for containing DataTypes is also inefficient. * Use of {@link Set} implementations for containing DataTypes is also inefficient.
*/ */
@Deprecated
public Set<DataType> getDataTypesContaining(DataType dataType); public Set<DataType> getDataTypesContaining(DataType dataType);
/** /**

View file

@ -227,7 +227,7 @@ public class StructureFactory {
return newStructure; 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 // dataLength number of components
private static void initializeStructureFromContext(Structure structure, private static void initializeStructureFromContext(Structure structure,
DataTypeProviderContext context, int dataLength) { DataTypeProviderContext context, int dataLength) {
@ -249,8 +249,8 @@ public class StructureFactory {
} }
for (DataTypeComponent dataComp : dataComps) { for (DataTypeComponent dataComp : dataComps) {
structure.add(dataComp.getDataType(), dataComp.getLength(), structure.add(dataComp.getDataType(), dataComp.getLength(), dataComp.getFieldName(),
dataComp.getFieldName(), dataComp.getComment()); dataComp.getComment());
} }
} }
} }