GP-1627 - Data Types - Updated the Paste Action to work when pasting ont a data type node in the Data Types provider

This commit is contained in:
dragonmacher 2022-01-28 15:59:35 -05:00
parent 073c726885
commit 2cefdb7688
14 changed files with 687 additions and 502 deletions

View file

@ -899,7 +899,6 @@ src/main/resources/help/Dummy_HelpSet.hs||GHIDRA||reviewed||END|
src/main/resources/help/empty.htm||GHIDRA||||END|
src/main/resources/images/2leftarrow.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
src/main/resources/images/2rightarrow.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
src/main/resources/images/ArmedMarkSelection.png||GHIDRA||||END|
src/main/resources/images/Array.png||GHIDRA||||END|
src/main/resources/images/B.gif||GHIDRA||||END|
src/main/resources/images/BookShelf.png||GHIDRA||||END|

View file

@ -18,13 +18,6 @@
program. Allowing the user to build libraries of data types and to share them between programs,
projects, and different users is a long term goal for Ghidra.</P>
<P>Prior to Ghidra 4.3, sharing data types even between programs in the same project was very
difficult. (users would have to drag data types, one at a time, from one program or archive to
another to propagate changes to data types). As of Ghidra 4.3, significant progress has been
made to make sharing data types easier, but the inherent complexity of this problem requires
users to understand a number of basic concepts to take advantage of the new features.<BR>
</P>
<BLOCKQUOTE>
<H2>Topics</H2>
@ -1008,7 +1001,7 @@
<LI><I>Rename</I> the data type that you are dragging to have ".conflict" appended to
it to make a unique name.</LI>
<LI><I>Replace</I> the existing data type with the one you are dragging (or, pasting);
<LI><I>Replace</I> the existing data type with the new one;
this means any use of the existing data types is <B><I>replaced</I></B> with the new
data type; the existing data type is deleted.</LI>
@ -1023,30 +1016,9 @@
<BLOCKQUOTE>
<P>A data type can be replaced by another data type. This means that every occurrence of
the original data type in a program is replaced by the new data type and the original
data type is deleted. There are two ways to replace a data type.</P>
data type is deleted. To replace a data type, right-click on the type to be replaced
and select the <B><I>Replace Data Type...</I></B> action.</P>
<TABLE style="width: 100%; text-align: left;" border="0" cellpadding="2" cellspacing="2">
<TBODY>
<TR valign="top">
<TD style="vertical-align: top; width: 130px;">1. <B>Drag-N-Drop</B></TD>
<TD style="vertical-align: top;">Click on the replacement data type and drag it
onto the data type to be replaced.</TD>
</TR>
<TR valign="top">
<TD style="vertical-align: top; width: 130px;">2. <B>Copy/Paste</B></TD>
<TD style="vertical-align: top;">Right-click on the replacement data type to be
moved and select the <I><B>Cut</B></I> action. Next, right-click on the data type
to be replaced and select the <I><B>Paste</B></I> action.</TD>
</TR>
</TBODY>
</TABLE>
</BLOCKQUOTE>
<BLOCKQUOTE>
<P>Either way, a confirmation dialog will appear.</P>
</BLOCKQUOTE>
<H3><A name="Favorites"></A>Setting Favorite Data Types</H3>

View file

@ -524,19 +524,6 @@
<H2>Miscellaneous Actions</H2>
<BLOCKQUOTE>
<H3><A name="Cut"></A>Cut</H3>
<BLOCKQUOTE>
<P>The <I><B>Cut</B></I> action can be be used to
<A href="data_type_manager_description.htm#MoveDataType">move selected
data types</A> and/or
<A href="data_type_manager_description.htm#MoveCategory">move selected categories</A>. The
<I><B>Cut</B></I> action only primes the selected nodes to be moved. The
<I><B>Paste</B></I> action must be used to complete the move. Any other
<I><B>Cut</B></I> or <I><B>Copy</B></I> action will cancel the previous
<I><B>Cut.</B></I> The <I><B>Cut</B></I> action can also be used to <A href=
"data_type_manager_description.htm#ReplaceDataType">replace one data type for another</A>.</P>
</BLOCKQUOTE>
<H3><A name="Copy"></A>Copy</H3>
@ -552,23 +539,18 @@
</P>
</BLOCKQUOTE>
<H3><A name="Paste"></A>Paste</H3>
<H3><A name="Cut"></A>Cut</H3>
<BLOCKQUOTE>
<P>The<I><B>Paste</B></I> action is used to complete a move or copy operation as
initiated by either the <I><B>Copy</B></I> or <I><B>Cut</B></I> action. The node that
is selected will be the destination for whatever nodes where selected when the
<I><B>Copy</B></I> or <I><B>Paste</B></I> was invoked.</P>
</BLOCKQUOTE>
<H3><A name="Rename"></A>Rename</H3>
<BLOCKQUOTE>
<P>The <I><B>Rename</B></I> action is used to
<A href="data_type_manager_description.htm#RenameCategory">rename a
selected category</A> or to
<A href="data_type_manager_description.htm#RenameDataType">rename a selected data
type</A>.</P>
<P>The <I><B>Cut</B></I> action can be be used to
<A href="data_type_manager_description.htm#MoveDataType">move selected
data types</A> and/or
<A href="data_type_manager_description.htm#MoveCategory">move selected categories</A>. The
<I><B>Cut</B></I> action only primes the selected nodes to be moved. The
<I><B>Paste</B></I> action must be used to complete the move. Any other
<I><B>Cut</B></I> or <I><B>Copy</B></I> action will cancel the previous
<I><B>Cut.</B></I> The <I><B>Cut</B></I> action can also be used to <A href=
"data_type_manager_description.htm#ReplaceDataType">replace one data type for another</A>.</P>
</BLOCKQUOTE>
<H3><A name="Delete"></A>Delete</H3>
@ -581,6 +563,48 @@
will appear before actually deleting the selected data types and categories.</P>
</BLOCKQUOTE>
<H3><A name="Paste"></A>Paste</H3>
<BLOCKQUOTE>
<P>The <I><B>Paste</B></I> action is used to complete a move or copy operation as
initiated by either the <I><B>Copy</B></I> or <I><B>Cut</B></I> action. The category
node (or the parent category of a data type node) that is selected will be the
destination for whatever nodes where selected when the <I><B>Copy</B></I> or
<I><B>Paste</B></I> was invoked.
</P>
<P>Any conflicts encountered while pasting will be resolved according to the current
<A HREF="#conflict_mode">conflict resolution mode</A>.
</P>
</BLOCKQUOTE>
<H3><A name="Rename"></A>Rename</H3>
<BLOCKQUOTE>
<P>The <I><B>Rename</B></I> action is used to
<A href="data_type_manager_description.htm#RenameCategory">rename a
selected category</A> or to
<A href="data_type_manager_description.htm#RenameDataType">rename a selected data
type</A>.</P>
</BLOCKQUOTE>
<H3><A name="Replace_Data_Type"></A>Replace Data Type...</H3>
<BLOCKQUOTE>
<P>The <I><B>Replace Data Type...</B></I> action is used to
<A href="data_type_manager_description.htm#ReplaceDataType">replace</A> a
selected data type and all occurrences in the program.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<BLOCKQUOTE>
<H3><A name="Collapse_All"></A>Collapse All</H3>
<BLOCKQUOTE>
@ -588,9 +612,7 @@
sub-tree and select the <I><B>Collapse All</B></I> action. If this action is invoked
via the local toolbar, then the entire tree is collapsed.</P>
</BLOCKQUOTE>
</BLOCKQUOTE>
<BLOCKQUOTE>
<H3><A name="Expand_All"></A>Expand All</H3>
</BLOCKQUOTE>

View file

@ -148,6 +148,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
addLocalAction(new CutAction(plugin));
addLocalAction(new CopyAction(plugin));
addLocalAction(new PasteAction(plugin));
addLocalAction(new ReplaceDataTypeAction(plugin));
addLocalAction(new DeleteAction(plugin));
addLocalAction(new DeleteArchiveAction(plugin));
addLocalAction(new RenameAction(plugin));
@ -181,8 +182,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
addLocalAction(new FindDataTypesBySizeAction(plugin, "2"));
addLocalAction(new FindStructuresByOffsetAction(plugin, "3"));
addLocalAction(new FindStructuresBySizeAction(plugin, "4"));
includeDataMembersInSearchAction =
new IncludeDataTypesInFilterAction(plugin, this, "5");
includeDataMembersInSearchAction = new IncludeDataTypesInFilterAction(plugin, this, "5");
addLocalAction(includeDataMembersInSearchAction);
addLocalAction(new ApplyFunctionDataTypesAction(plugin)); // Tree

View file

@ -48,9 +48,9 @@ public class PasteAction extends DockingAction {
this.clipboard = plugin.getClipboard();
this.tool = plugin.getTool();
setPopupMenuData(new MenuData(new String[] { "Paste" }, "Edit"));
setKeyBindingData(new KeyBindingData(KeyStroke.getKeyStroke(KeyEvent.VK_V,
InputEvent.CTRL_DOWN_MASK), KeyBindingPrecedence.ActionMapLevel));
setEnabled(true);
setKeyBindingData(
new KeyBindingData(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK),
KeyBindingPrecedence.ActionMapLevel));
}
@Override
@ -65,7 +65,7 @@ public class PasteAction extends DockingAction {
@Override
public boolean isEnabledForContext(ActionContext context) {
DataTypeTreeNode node = getSelectedDataTypeTreeNode(context);
if (!(node instanceof CategoryNode) || !((CategoryNode) node).isEnabled()) {
if (node == null) {
return false;
}
List<GTreeNode> transferNodeList = getNodesFromClipboard();
@ -96,7 +96,7 @@ public class PasteAction extends DockingAction {
return false;
}
if (invalidCutNodes(destinationNode, transferNodeList)) {
if (hasInvalidCutNodes(destinationNode, transferNodeList)) {
return false; // cut nodes that cannot be pasted here
}
@ -109,13 +109,14 @@ public class PasteAction extends DockingAction {
}
DataFlavor[] flavors = handler.getSupportedDataFlavors(transferNodeList);
return handler.isDropSiteOk(destinationNode, flavors, DnDConstants.ACTION_COPY);
return handler.isValidDataTypeDestination(destinationNode, flavors,
DnDConstants.ACTION_COPY);
}
private boolean invalidCutNodes(DataTypeTreeNode destinationNode, List<GTreeNode> nodeList) {
private boolean hasInvalidCutNodes(DataTypeTreeNode destinationNode, List<GTreeNode> nodeList) {
DataTypeTreeNode node = (DataTypeTreeNode) nodeList.get(0);
if (!node.isCut()) {
return false; // hasn't been cut, no problemo
return false; // hasn't been cut, no problem
}
// can't cut nodes from one archive and paste into another
@ -142,28 +143,37 @@ public class PasteAction extends DockingAction {
@Override
public void actionPerformed(ActionContext context) {
GTree gTree = (GTree) context.getContextObject();
TreePath[] selectionPaths = gTree.getSelectionPaths();
GTreeNode destinationNode = (GTreeNode) selectionPaths[0].getLastPathComponent();
List<GTreeNode> nodeList = getNodesFromClipboard();
if (nodeList.isEmpty()) {
return;
}
GTree gTree = (GTree) context.getContextObject();
TreePath[] selectionPaths = gTree.getSelectionPaths();
CategoryNode destinationNode = getDropTargetNode(selectionPaths);
DataTypeTreeNode dataTypeTreeNode = (DataTypeTreeNode) nodeList.get(0);
if (dataTypeTreeNode.isCut()) { // clear cut nodes on paste operation
clipboard.setContents(null, null);
}
ActionType actionType = getActionType(dataTypeTreeNode);
DataTypeTreeCopyMoveTask task =
new DataTypeTreeCopyMoveTask(destinationNode, nodeList, actionType,
(DataTypeArchiveGTree) gTree,
plugin.getConflictHandler());
DataTypeTreeCopyMoveTask task = new DataTypeTreeCopyMoveTask(destinationNode, nodeList,
actionType, (DataTypeArchiveGTree) gTree, plugin.getConflictHandler());
tool.execute(task, 250);
}
private CategoryNode getDropTargetNode(TreePath[] selectionPaths) {
// clients can drop/paste onto a category or archive
GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent();
if (node instanceof CategoryNode) {
return (CategoryNode) node;
}
return (CategoryNode) node.getParent();
}
private ActionType getActionType(DataTypeTreeNode pasteNode) {
if (pasteNode.isCut()) {
return ActionType.MOVE;

View file

@ -0,0 +1,138 @@
/* ###
* 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;
import javax.swing.tree.TreePath;
import docking.ActionContext;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.widgets.tree.GTree;
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.Archive;
import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
import ghidra.app.plugin.core.datamgr.tree.*;
import ghidra.app.util.datatype.DataTypeSelectionDialog;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.data.*;
import ghidra.util.Msg;
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
/**
* Replace the selected data type with the chosen data type
*/
public class ReplaceDataTypeAction extends DockingAction {
private DataTypeManagerPlugin plugin;
public ReplaceDataTypeAction(DataTypeManagerPlugin plugin) {
super("Replace Data Type", plugin.getName());
this.plugin = plugin;
setPopupMenuData(new MenuData(new String[] { "Replace Data Type..." }, "Edit"));
}
@Override
public boolean isAddToPopup(ActionContext context) {
DataTypeTreeNode node = getSelectedDataTypeTreeNode(context);
if (node instanceof BuiltInArchiveNode) {
return false;
}
return (node != null);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
DataTypeTreeNode node = getSelectedDataTypeTreeNode(context);
if (node == null) {
return false;
}
if (!(node instanceof DataTypeNode)) {
return false;
}
return node.isModifiable();
}
private DataTypeTreeNode getSelectedDataTypeTreeNode(ActionContext context) {
if (!(context instanceof DataTypesActionContext)) {
return null;
}
GTree gTree = (GTree) context.getContextObject();
TreePath[] selectionPaths = gTree.getSelectionPaths();
if (selectionPaths == null || selectionPaths.length == 0) {
return null;
}
if (selectionPaths.length > 1) {
return null;
}
DataTypeTreeNode node = (DataTypeTreeNode) selectionPaths[0].getLastPathComponent();
return node;
}
@Override
public void actionPerformed(ActionContext context) {
PluginTool tool = plugin.getTool();
int noSizeRestriction = -1;
DataTypeSelectionDialog selectionDialog = new DataTypeSelectionDialog(tool,
plugin.getProgram().getDataTypeManager(), noSizeRestriction, AllowedDataTypes.ALL);
tool.showDialog(selectionDialog);
DataType newDt = selectionDialog.getUserChosenDataType();
if (newDt == null) {
return; // cancelled
}
DataTypeTreeNode node = getSelectedDataTypeTreeNode(context);
DataTypeManagerHandler dtmHandler = plugin.getDataTypeManagerHandler();
DataTypeManager dtm = newDt.getDataTypeManager();
Archive sourceArchive = dtmHandler.getArchive(dtm);
Archive destinationArchive = findArchive(node);
DataType oldDt = ((DataTypeNode) node).getDataType();
if (sourceArchive != destinationArchive) {
oldDt = oldDt.clone(oldDt.getDataTypeManager());
}
int txId = dtm.startTransaction("Replace Data Type");
try {
dtm.replaceDataType(oldDt, newDt, true);
}
catch (DataTypeDependencyException e) {
Msg.showError(this, null, "Replace Failed", "Replace failed. Existing type " + newDt +
"; replacment type " + oldDt + ". " + e.getMessage());
}
finally {
dtm.endTransaction(txId, true);
}
}
private Archive findArchive(GTreeNode node) {
while (node != null) {
if (node instanceof ArchiveNode) {
return ((ArchiveNode) node).getArchive();
}
node = node.getParent();
}
return null;
}
}

View file

@ -585,8 +585,8 @@ public class DataTypeManagerHandler {
dataTypeManager.updateSourceArchiveName(existingDataTypeManager.getUniversalID(),
existingDataTypeManager.getName());
int existingTxID = existingDataTypeManager.startTransaction(
"Update Data Type Source Archive Name");
int existingTxID = existingDataTypeManager
.startTransaction("Update Data Type Source Archive Name");
try {
existingDataTypeManager.updateSourceArchiveName(
dataTypeManager.getUniversalID(), dataTypeManager.getName());
@ -686,7 +686,7 @@ public class DataTypeManagerHandler {
Msg.info(this, "Closed archive: '" + archive.getName() + "'");
}
private Archive getArchive(DataTypeManager dtm) {
public Archive getArchive(DataTypeManager dtm) {
for (Archive archive : openArchives) {
DataTypeManager dataTypeManager = archive.getDataTypeManager();
if (dataTypeManager.equals(dtm)) {
@ -814,43 +814,43 @@ public class DataTypeManagerHandler {
private void initializeFavorites() {
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(PointerDataType.dataType, null), true);
builtInDataTypesManager
.setFavorite(builtInDataTypesManager.resolve(PointerDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(CharDataType.dataType, null), true);
builtInDataTypesManager
.setFavorite(builtInDataTypesManager.resolve(CharDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(StringDataType.dataType, null), true);
builtInDataTypesManager
.setFavorite(builtInDataTypesManager.resolve(StringDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(TerminatedStringDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(TerminatedUnicodeDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(FloatDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(DoubleDataType.dataType, null), true);
builtInDataTypesManager
.setFavorite(builtInDataTypesManager.resolve(FloatDataType.dataType, null), true);
builtInDataTypesManager
.setFavorite(builtInDataTypesManager.resolve(DoubleDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(LongDoubleDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(IntegerDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(LongDataType.dataType, null), true);
builtInDataTypesManager
.setFavorite(builtInDataTypesManager.resolve(IntegerDataType.dataType, null), true);
builtInDataTypesManager
.setFavorite(builtInDataTypesManager.resolve(LongDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(UnsignedIntegerDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(UnsignedLongDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(ByteDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(WordDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(DWordDataType.dataType, null), true);
builtInDataTypesManager.setFavorite(
builtInDataTypesManager.resolve(QWordDataType.dataType, null), true);
builtInDataTypesManager
.setFavorite(builtInDataTypesManager.resolve(ByteDataType.dataType, null), true);
builtInDataTypesManager
.setFavorite(builtInDataTypesManager.resolve(WordDataType.dataType, null), true);
builtInDataTypesManager
.setFavorite(builtInDataTypesManager.resolve(DWordDataType.dataType, null), true);
builtInDataTypesManager
.setFavorite(builtInDataTypesManager.resolve(QWordDataType.dataType, null), true);
}
@ -1428,8 +1428,8 @@ public class DataTypeManagerHandler {
new DataTreeDialog(null, "Save As", DataTreeDialog.SAVE, domainFileFilter);
dataTreeSaveDialog.addOkActionListener(listener);
dataTreeSaveDialog.setHelpLocation(
new HelpLocation(HelpTopics.PROGRAM, "Save_As_File"));
dataTreeSaveDialog
.setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Save_As_File"));
}
return dataTreeSaveDialog;
}
@ -1614,8 +1614,8 @@ public class DataTypeManagerHandler {
@Override
public void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE.equals(
file.getContentType())) {
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE
.equals(file.getContentType())) {
return;
}
Iterator<Archive> archiveIter = openArchives.iterator();
@ -1658,8 +1658,8 @@ public class DataTypeManagerHandler {
@Override
public void domainFileRenamed(DomainFile file, String oldName) {
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE.equals(
file.getContentType())) {
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE
.equals(file.getContentType())) {
return;
}
String newName = file.getName();

View file

@ -37,11 +37,11 @@ import ghidra.util.task.Task;
public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
private static DataFlavor localDataTypeTreeFlavor = createLocalTreeNodeFlavor();
public static DataFlavor[] allSupportedFlavors = { DataTypeTransferable.localDataTypeFlavor,
localDataTypeTreeFlavor };
public static DataFlavor[] allSupportedFlavors =
{ DataTypeTransferable.localDataTypeFlavor, localDataTypeTreeFlavor };
public static DataFlavor[] builtinFlavors = { DataTypeTransferable.localBuiltinDataTypeFlavor,
localDataTypeTreeFlavor };
public static DataFlavor[] builtinFlavors =
{ DataTypeTransferable.localBuiltinDataTypeFlavor, localDataTypeTreeFlavor };
public static DataFlavor[] restrictedFlavors = { localDataTypeTreeFlavor };
@ -52,8 +52,9 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
// create a data flavor that is an List of GTreeNodes
private static DataFlavor createLocalTreeNodeFlavor() {
try {
return new GenericDataFlavor(DataFlavor.javaJVMLocalObjectMimeType +
"; class=java.util.List", "Local list of Drag/Drop DataType Tree objects");
return new GenericDataFlavor(
DataFlavor.javaJVMLocalObjectMimeType + "; class=java.util.List",
"Local list of Drag/Drop DataType Tree objects");
}
catch (Exception e) {
Msg.showError(DataTypeDragNDropHandler.class, null, null, null, e);
@ -75,12 +76,12 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
if (list.contains(destinationNode)) { // don't allow drop on dragged nodes.
return;
}
CategoryNode updatedDestinationNode = getDropTargetNode(destinationNode);
ActionType actionType =
dropAction == DnDConstants.ACTION_COPY ? ActionType.COPY : ActionType.MOVE;
Task task =
new DataTypeTreeCopyMoveTask(destinationNode, list, actionType,
(DataTypeArchiveGTree) tree,
plugin.getConflictHandler());
Task task = new DataTypeTreeCopyMoveTask(updatedDestinationNode, list, actionType,
(DataTypeArchiveGTree) tree, plugin.getConflictHandler());
plugin.getTool().execute(task, 250);
}
catch (UnsupportedFlavorException e) {
@ -91,6 +92,16 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
}
}
private CategoryNode getDropTargetNode(GTreeNode node) {
// clients can drop/paste onto a category or archive
if (node instanceof CategoryNode) {
return (CategoryNode) node;
}
return (CategoryNode) node.getParent();
}
@Override
public DataFlavor[] getSupportedDataFlavors(List<GTreeNode> draggedNodes) {
// single, datatype node supports both datatype dragging *and* local tree dragging
@ -150,12 +161,38 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
@Override
public boolean isDropSiteOk(GTreeNode destinationNode, DataFlavor[] flavors, int dropAction) {
// can't drop on the root node
if (!isValidDataTypeDestination(destinationNode, flavors, dropAction)) {
return false;
}
GTreeNode updatedDestinationNode = getDropTargetNode(destinationNode);
if (isDroppingBuiltin(flavors)) {
if (!isValidBuiltinDropSite(updatedDestinationNode)) {
return false;
}
}
return true;
}
/**
* Verifies the given destination node can accept the given drop/copy/paste action and content
* flavors.
* @param destinationNode the node accepting the action
* @param flavors the supported flavors of the action
* @param dropAction the actual action see {@link DnDConstants}
* @return true if valid
*/
public boolean isValidDataTypeDestination(GTreeNode destinationNode, DataFlavor[] flavors,
int dropAction) {
// can't drop/paste on the root node
if (destinationNode == null || destinationNode.getParent() == null) {
return false;
}
// can only drop nodes from other dataTypetrees
// can only drop/paste nodes from other dataType trees
if (!containsFlavor(flavors, localDataTypeTreeFlavor)) {
return false;
}
@ -167,20 +204,6 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
return false;
}
// only a single datatype node can be dropped on a datatype node.
if (destinationNode instanceof DataTypeNode) {
if (!containsFlavor(flavors, DataTypeTransferable.localDataTypeFlavor) &&
!containsFlavor(flavors, DataTypeTransferable.localBuiltinDataTypeFlavor)) {
return false;
}
}
if (isDroppingBuiltin(flavors)) {
if (!isValidBuiltinDropSite(destinationNode)) {
return false;
}
}
return true;
}

View file

@ -204,11 +204,7 @@ public class DataTypeNode extends DataTypeTreeNode {
@Override
public boolean canPaste(List<GTreeNode> pastedNodes) {
if (pastedNodes.size() != 1) {
return false;
}
GTreeNode pastedNode = pastedNodes.get(0);
return pastedNode instanceof DataTypeNode;
return isModifiable();
}
@Override

View file

@ -34,6 +34,7 @@ public abstract class DataTypeTreeNode extends GTreeLazyNode {
/**
* Returns true if this nodes handles paste operations
* @param pastedNodes the nodes to be pasted
* @return true if this nodes handles paste operations
*/
public abstract boolean canPaste(List<GTreeNode> pastedNodes);

View file

@ -22,8 +22,6 @@ import java.util.regex.Pattern;
import docking.widgets.OptionDialog;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.GTreeState;
import ghidra.app.plugin.core.datamgr.DataTypeSyncInfo;
import ghidra.app.plugin.core.datamgr.DataTypeSynchronizer;
import ghidra.app.plugin.core.datamgr.archive.Archive;
import ghidra.app.plugin.core.datamgr.archive.ProgramArchive;
import ghidra.app.plugin.core.datamgr.tree.*;
@ -50,7 +48,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
}
private DataTypeArchiveGTree gTree;
private GTreeNode destinationNode;
private CategoryNode destinationNode;
private List<GTreeNode> droppedNodes;
private Archive sourceArchive;
private Archive destinationArchive;
@ -64,7 +62,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
super("Drag/Drop", true, true, true);
}
public DataTypeTreeCopyMoveTask(GTreeNode destinationNode, List<GTreeNode> droppedNodeList,
public DataTypeTreeCopyMoveTask(CategoryNode destinationNode, List<GTreeNode> droppedNodeList,
ActionType actionType, DataTypeArchiveGTree gTree,
DataTypeConflictHandler conflictHandler) {
super("Drag/Drop", true, true, true);
@ -118,7 +116,6 @@ public class DataTypeTreeCopyMoveTask extends Task {
}
doCopy(monitor);
}
catch (CancelledException e) {
return; // nothing to report
@ -161,13 +158,8 @@ public class DataTypeTreeCopyMoveTask extends Task {
DataTypeManager dtm = destinationArchive.getDataTypeManager();
int txId = dtm.startTransaction("Copy/Move Category/DataType");
try {
if (destinationNode instanceof DataTypeNode) {
dragNodeToDataType();
}
else {
dragNodesToCategory(monitor);
}
}
finally {
dtm.endTransaction(txId, true);
}
@ -189,8 +181,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
}
}
private void associateDataTypes(TaskMonitor monitor)
throws CancelledException {
private void associateDataTypes(TaskMonitor monitor) throws CancelledException {
if (!promptToAssociateTypes(monitor)) {
return;
@ -347,21 +338,18 @@ public class DataTypeTreeCopyMoveTask extends Task {
DataTypeManager nodeDtm = dataType.getDataTypeManager();
boolean sameManager = (dtm == nodeDtm);
DataType newDt = !sameManager ? dataType.clone(nodeDtm) : dataType.copy(nodeDtm);
if (sameManager &&
newDt.getCategoryPath().equals(toCategory.getCategoryPath())) {
if (sameManager && newDt.getCategoryPath().equals(toCategory.getCategoryPath())) {
renameAsCopy(toCategory, newDt);
}
DataType resolvedDt = toCategory.addDataType(newDt, conflictHandler);
if (resolvedDt instanceof Pointer || resolvedDt instanceof Array ||
resolvedDt instanceof BuiltInDataType ||
resolvedDt instanceof MissingBuiltInDataType) {
resolvedDt instanceof BuiltInDataType || resolvedDt instanceof MissingBuiltInDataType) {
return;
}
if (!resolvedDt.getCategoryPath().equals(toCategory.getCategoryPath())) {
errors.add(
"Data type copy failed. Another copy of this data type already exists at " +
errors.add("Data type copy failed. Another copy of this data type already exists at " +
resolvedDt.getPathName());
}
}
@ -417,17 +405,15 @@ public class DataTypeTreeCopyMoveTask extends Task {
}
}
private void moveCategory(Category toCategory, Category category,
TaskMonitor monitor) {
private void moveCategory(Category toCategory, Category category, TaskMonitor monitor) {
if (category.getParent() == toCategory) { // moving to same place
return;
}
try {
CategoryPath path = toCategory.getCategoryPath();
if (path.isAncestorOrSelf(category.getCategoryPath())) {
errors.add(
"Cannot move a parent node onto a child node. Moving " + category + " to " +
toCategory);
errors.add("Cannot move a parent node onto a child node. Moving " + category +
" to " + toCategory);
return;
}
@ -441,30 +427,25 @@ public class DataTypeTreeCopyMoveTask extends Task {
private void moveDataType(Category toCategory, DataType dataType) {
if (dataType.getCategoryPath().equals(toCategory.getCategoryPath())) {
errors.add(
"Move failed. DataType is already in this category. Category " + toCategory +
"; Data type: " + dataType);
errors.add("Move failed. DataType is already in this category. Category " +
toCategory + "; Data type: " + dataType);
return;
}
try {
toCategory.moveDataType(dataType, conflictHandler);
}
catch (DataTypeDependencyException e) {
errors.add(
"Move failed. DataType is already in this category. Category " + toCategory +
"; Data type: " + dataType + ". " + e.getMessage());
errors.add("Move failed. DataType is already in this category. Category " +
toCategory + "; Data type: " + dataType + ". " + e.getMessage());
}
}
private void copyCategory(Category toCategory, Category category,
TaskMonitor monitor) {
private void copyCategory(Category toCategory, Category category, TaskMonitor monitor) {
CategoryPath toPath = toCategory.getCategoryPath();
boolean sameManager =
(toCategory.getDataTypeManager() == category.getDataTypeManager());
boolean sameManager = (toCategory.getDataTypeManager() == category.getDataTypeManager());
if (sameManager && toPath.isAncestorOrSelf(category.getCategoryPath())) {
errors.add("Copy failed. " +
"Cannot copy a parent node onto a child node. Moving " + category + " to " +
toCategory);
errors.add("Copy failed. " + "Cannot copy a parent node onto a child node. Moving " +
category + " to " + toCategory);
return;
}
toCategory.copyCategory(category, conflictHandler, monitor);
@ -481,27 +462,6 @@ public class DataTypeTreeCopyMoveTask extends Task {
"Expected node to be either an ArchiveNode or CategoryNode but was " + node.getClass());
}
private boolean isAssociatedEitherWay(DataType dt1, DataType dt2) {
return isAssociated(dt1, dt2) || isAssociated(dt2, dt1);
}
private boolean isAssociated(DataType sourceDt, DataType destinationDt) {
UniversalID destinationID = destinationDt.getUniversalID();
if (destinationID == null || !destinationID.equals(sourceDt.getUniversalID())) {
return false;
}
if (!haveSameSourceArchive(sourceDt, destinationDt)) {
return false;
}
return isLocal(sourceDt);
}
private boolean haveSameSourceArchive(DataType dt1, DataType dt2) {
SourceArchive s1 = dt1.getSourceArchive();
SourceArchive s2 = dt2.getSourceArchive();
return s1.getSourceArchiveID().equals(s2.getSourceArchiveID());
}
/**
* 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
@ -516,95 +476,11 @@ public class DataTypeTreeCopyMoveTask extends Task {
return sourceId.equals(dtmId);
}
private void dragNodeToDataType() {
DataType destinationDt = ((DataTypeNode) destinationNode).getDataType();
// there must be exactly one and it must be a dataTypeNode, because of isValidDropSite()
GTreeNode node = droppedNodes.get(0);
DataType replacementDt = ((DataTypeNode) node).getDataType();
if (sourceArchive != destinationArchive) {
if (isAssociatedEitherWay(replacementDt, destinationDt)) {
handleAssociatedType(destinationDt, replacementDt);
return;
}
replacementDt = replacementDt.clone(replacementDt.getDataTypeManager());
}
else if (actionType == ActionType.COPY) { // Copy within a single data type manager.
replacementDt = replacementDt.copy(replacementDt.getDataTypeManager());
}
replaceDataType(destinationDt, replacementDt);
}
private void handleAssociatedType(DataType destinationDt, DataType replacementDt) {
if (isLocal(destinationDt)) {
DataTypeSyncInfo syncInfo = new DataTypeSyncInfo(replacementDt,
destinationDt.getDataTypeManager());
if (!syncInfo.canCommit()) {
Msg.showInfo(getClass(), gTree, "Commit Data Type",
"No changes to commit");
}
// destination data-type is local to an archive
else if (confirmCommit()) {
// if the destination dataType is local to its dataTypeManager
// then we are committing.
DataTypeSynchronizer.commit(destinationDt.getDataTypeManager(), replacementDt);
}
}
else { // else we are updating
DataTypeSyncInfo syncInfo = new DataTypeSyncInfo(destinationDt,
replacementDt.getDataTypeManager());
if (!syncInfo.canUpdate()) {
Msg.showInfo(getClass(), gTree, "Update Data Type", "No changes to copy");
}
else if (confirmUpdate()) {
DataTypeSynchronizer.update(destinationDt.getDataTypeManager(), replacementDt);
}
}
}
private boolean confirmCommit() {
return confirm("Commit Data Type?",
"Do you want to commit the changes to this data type back to the source Archive? \n" +
"(Warning: any changes in the source archive will be overwritten.)");
}
private boolean confirmUpdate() {
return confirm("Update Data Type?",
"Do you want to update this data type with the changes in the source Archive?\n" +
"(Warning: any local changes will be overwritten.)");
}
private boolean confirm(String title, String message) {
int choice = OptionDialog.showYesNoDialog(gTree, title, message);
return choice == OptionDialog.YES_OPTION;
}
private int askToAssociateDataTypes() {
return OptionDialog.showYesNoCancelDialog(gTree, "Associate DataTypes?",
"Do you want to associate local datatypes with the target archive?");
}
private void replaceDataType(DataType existingDt, DataType replacementDt) {
int choice =
OptionDialog.showYesNoDialog(gTree, "Replace Data Type?", "Replace " +
existingDt.getPathName() + "\nwith " + replacementDt.getPathName() +
"?");
if (choice == OptionDialog.YES_OPTION) {
try {
DataTypeManager dtMgr = existingDt.getDataTypeManager();
dtMgr.replaceDataType(existingDt, replacementDt, true);
}
catch (DataTypeDependencyException e) {
errors.add("Replace failed. Existing type " + existingDt + "; replacment type " +
replacementDt + ". " + e.getMessage());
}
}
}
// filters out nodes with categories in their path
private void filterRedundantNodes() {

View file

@ -22,20 +22,21 @@ import java.awt.dnd.DnDConstants;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JTextField;
import org.junit.*;
import docking.action.DockingActionIf;
import docking.menu.ActionState;
import docking.widgets.OptionDialog;
import docking.widgets.tree.GTreeNode;
import docking.widgets.tree.support.GTreeDragNDropHandler;
import docking.widgets.tree.support.GTreeNodeTransferable;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.datamgr.actions.ConflictHandlerModesAction;
import ghidra.app.plugin.core.datamgr.tree.*;
import ghidra.app.services.ProgramManager;
import ghidra.app.util.datatype.DataTypeSelectionDialog;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
@ -57,7 +58,9 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
private ProgramDB program;
private DataTypeManagerPlugin plugin;
private DataTypesProvider provider;
private DockingActionIf pasteAction;
private ConflictHandlerModesAction conflictHandlerModesAction;
private ProgramActionContext treeContext;
private DataTypeArchiveGTree tree;
private ArchiveRootNode archiveRootNode;
private ArchiveNode programNode;
@ -89,6 +92,9 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
assertNotNull("Did not successfully wait for the program node to load", programNode);
tool.showComponentProvider(provider, true);
pasteAction = getAction(plugin, "Paste");
treeContext = new DataTypesActionContext(provider, program, tree, null);
}
private ProgramDB buildProgram() throws Exception {
@ -164,7 +170,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
public void testConflictCopyInProgram() throws Exception {
public void testCopyPasteToCategory_RenameConflictHandler() throws Exception {
enableRenameConflictHandler();
@ -194,7 +200,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
public void testConflictCopyReplace() throws Exception {
public void testCopyPasteToCategory_ReplaceExistingConflictHandler() throws Exception {
enableReplaceExistingConflictHandler();
@ -213,7 +219,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
public void testConflictCopyUseExisting() throws Exception {
public void testCopyPasteToCategory_UseExistingConflictHandler() throws Exception {
enableUseExistingConflictHandler();
@ -234,11 +240,11 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
assertEquals(originalDataType, newDataTypeNode.getDataType());
structureNode = (DataTypeNode) category3Node.getChild(structName);
assertTrue(!originalDataType.isEquivalent(structureNode.getDataType()));
assertFalse(originalDataType.isEquivalent(structureNode.getDataType()));
}
@Test
public void testConflictPasteMoveRename() throws Exception {
public void testCutPasteToCategory_RenameConflictHandler() throws Exception {
enableRenameConflictHandler();
@ -256,7 +262,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
public void testConflictDragMoveRename() throws Exception {
public void testDragMoveToCategory_RenameConflictHandler() throws Exception {
enableRenameConflictHandler();
@ -269,7 +275,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
expandNode(miscNode);
// move/drag ArrayStruct to MISC
dragNodeToNode(structureNode, miscNode);
moveDragNodeToNode(structureNode, miscNode);
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName + DataType.CONFLICT_SUFFIX);
assertNotNull(node);
@ -277,7 +283,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
public void testConflictDragCopyRename() throws Exception {
public void testDragCopyToCategory_RenameConflictHandler() throws Exception {
enableRenameConflictHandler();
@ -291,7 +297,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
expandNode(miscNode);
// copy/drag ArrayStruct to MISC
copyNodeToNode(structureNode, miscNode);
copyDragNodeToNode(structureNode, miscNode);
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
category3Node = (CategoryNode) tree.getViewNode(category3Node);
@ -305,7 +311,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
public void testConflictDragCopyReplace() throws Exception {
public void testDragCopyToCategory_ReplaceExistingConflictHandler() throws Exception {
enableReplaceExistingConflictHandler();
@ -314,13 +320,13 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
String structName = "ArrayStruct";
DataTypeNode structureNode = createAndSelectStructure(structName);
CategoryNode category3Node = (CategoryNode) structureNode.getParent();
DataType origDt = structureNode.getDataType();
DataType originalDt = structureNode.getDataType();
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
expandNode(miscNode);
// copy/drag ArrayStruct to MISC
copyNodeToNode(structureNode, miscNode);
copyDragNodeToNode(structureNode, miscNode);
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
category3Node = (CategoryNode) tree.getViewNode(category3Node);
@ -329,11 +335,11 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(node);
assertEquals(category3Node, structureNode.getParent());
assertTrue(origDt.isEquivalent(structureNode.getDataType()));
assertTrue(originalDt.isEquivalent(structureNode.getDataType()));
}
@Test
public void testConflictDragCopyUseExisting() throws Exception {
public void testDragCopyToCategory_UseExistingConflictHandler() throws Exception {
enableUseExistingConflictHandler();
@ -341,23 +347,23 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
// in the program, rename Category1/Category2/Category3/IntStruct to ArrayStruct
String structName = "ArrayStruct";
DataTypeNode structureNode = createAndSelectStructure(structName);
DataType origDt = structureNode.getDataType();
DataType originalDt = structureNode.getDataType();
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
expandNode(miscNode);
// copy/drag ArrayStruct to MISC
copyNodeToNode(structureNode, miscNode);
copyDragNodeToNode(structureNode, miscNode);
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(node);
structureNode = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(structureNode);
assertTrue(!origDt.isEquivalent(structureNode.getDataType()));
assertFalse(originalDt.isEquivalent(structureNode.getDataType()));
}
@Test
public void testConflictPasteMoveReplace() throws Exception {
public void testCutPasteToCategory_ReplaceExistingConflictHandler() throws Exception {
enableReplaceExistingConflictHandler();
@ -368,18 +374,18 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
DataTypeNode node = (DataTypeNode) miscNode.getChild("ArrayStruct");
DataType origDt = node.getDataType();
DataType originalDt = node.getDataType();
// move ArrayStruct to MISC
cutPasteSelectedNodeToNode("MISC");
node = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(node);
assertTrue(!origDt.equals(node.getDataType()));
assertFalse(originalDt.equals(node.getDataType()));
}
@Test
public void testConflictPasteMoveUseExisting() throws Exception {
public void testCutPasteToCategory_UseExistingConflictHandler() throws Exception {
enableUseExistingConflictHandler();
@ -390,18 +396,18 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
DataTypeNode node = (DataTypeNode) miscNode.getChild("ArrayStruct");
DataType origDt = node.getDataType();
DataType originalDt = node.getDataType();
cutPasteSelectedNodeToNode("MISC");
node = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(node);
assertTrue(origDt.equals(node.getDataType()));
assertTrue(originalDt.equals(node.getDataType()));
assertNull(structureNode.getParent());
}
@Test
public void testConflictDragMoveReplace() throws Exception {
public void testDragMoveToCategory_ReplaceExistingConflictHandler() throws Exception {
enableReplaceExistingConflictHandler();
@ -414,21 +420,21 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
expandNode(miscNode);
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
DataType origDt = node.getDataType();
DataType originalDt = node.getDataType();
// move/drag ArrayStruct to MISC
dragNodeToNode(structureNode, miscNode);
moveDragNodeToNode(structureNode, miscNode);
node = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(node);
assertNull(structureNode.getParent());
assertNotNull(node);
assertTrue(!origDt.equals(node.getDataType()));
assertFalse(originalDt.equals(node.getDataType()));
}
@Test
public void testConflictDragMoveUseExisting() throws Exception {
public void testDragMoveToCategory_UseExistingConflictHandler() throws Exception {
enableUseExistingConflictHandler();
@ -440,21 +446,24 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
expandNode(miscNode);
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
DataType origDt = node.getDataType();
DataType originalDt = node.getDataType();
// move/drag ArrayStruct to MISC
dragNodeToNode(structureNode, miscNode);
moveDragNodeToNode(structureNode, miscNode);
node = (DataTypeNode) miscNode.getChild(structName);
assertNotNull(node);
assertNull(structureNode.getParent());
assertNotNull(node);
assertTrue(origDt.equals(node.getDataType()));
assertTrue(originalDt.equals(node.getDataType()));
}
@Test
public void testReplaceDataTypeYes() throws Exception {
public void testDragMoveToDataType_Replace() throws Exception {
enableReplaceExistingConflictHandler();
String structName = "ArrayStruct";
DataTypeNode structureNode = createAndSelectStructure(structName);
Structure structure = (Structure) structureNode.getDataType();
@ -465,9 +474,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
// drag/move ArrayStruct to MISC/ArrayStruct
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
dragNodeToNode(structureNode, miscStructureNode);
pressButtonOnOptionDialog("Yes");
moveDragNodeToNode(structureNode, miscStructureNode);
assertNull(structureNode.getParent());
assertNull(category3Node.getChild(structName));
@ -491,122 +498,38 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
}
@Test
public void testReplaceDataTypeNo() throws Exception {
String structName = "ArrayStruct";
DataTypeNode structureNode = createAndSelectStructure(structName);
Structure structure = (Structure) structureNode.getDataType();
CategoryNode category3Node = (CategoryNode) structureNode.getParent();
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
expandNode(miscNode);
// drag/move ArrayStruct to MISC/ArrayStruct
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
dragNodeToNode(structureNode, miscStructureNode);
pressButtonOnOptionDialog("No");
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
assertNotNull(structureNode.getParent());
category3Node = (CategoryNode) tree.getViewNode(category3Node);
assertNotNull(category3Node.getChild("ArrayStruct"));
miscNode = (CategoryNode) tree.getViewNode(miscNode);
DataTypeNode node = (DataTypeNode) miscNode.getChild("ArrayStruct");
assertTrue(!structure.isEquivalent(node.getDataType()));
}
@Test
public void testReplaceDTSameParentYes() throws Exception {
public void testDragMoveToDataType_SameParent() throws Exception {
// drag/drop a data type onto another data type
// get Option dialog, and choose "Yes"
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
expandNode(miscNode);
DataTypeNode structureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
DataTypeNode unionNode = (DataTypeNode) miscNode.getChild("ArrayUnion");
dragNodeToNode(unionNode, structureNode);
setErrorsExpected(true);
moveDragNodeToNode(unionNode, structureNode);
setErrorsExpected(false);
pressButtonOnOptionDialog("Yes");
assertNotNull(miscNode.getChild("ArrayUnion"));
assertNull(miscNode.getChild("ArrayStruct"));
// can't move a data type into its same category
waitForWindow("Encountered Errors Copying/Moving");
}
@Test
public void testReplaceDTSameParentNo() throws Exception {
// drag/drop a data type onto another data type
// get Option dialog, and choose "Yes"
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
expandNode(miscNode);
public void testDragCopyToDataType() throws Exception {
DataTypeNode structureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
DataTypeNode unionNode = (DataTypeNode) miscNode.getChild("ArrayUnion");
enableRenameConflictHandler();
dragNodeToNode(unionNode, structureNode);
pressButtonOnOptionDialog("No");
assertNotNull(miscNode.getChild("ArrayUnion"));
assertNotNull(miscNode.getChild("ArrayStruct"));
}
@Test
public void testCopyReplaceDataTypeNo() throws Exception {
// drag/copy a data type onto another data type
// get Option dialog, and choose "No"
// cause a conflict
// in the program, rename Category1/Category2/Category3/IntStruct to ArrayStruct
String structName = "ArrayStruct";
DataTypeNode structureNode = createAndSelectStructure(structName);
CategoryNode category3Node = (CategoryNode) structureNode.getParent();
// drag/move ArrayStruct to MISC/ArrayStruct
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
expandNode(miscNode);
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
DataType origDt = miscStructureNode.getDataType();
copyNodeToNode(structureNode, miscStructureNode);
pressButtonOnOptionDialog("No");
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
assertNotNull(structureNode.getParent());
category3Node = (CategoryNode) tree.getViewNode(category3Node);
assertNotNull(category3Node.getChild("ArrayStruct"));
miscNode = (CategoryNode) tree.getViewNode(miscNode);
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
assertEquals(origDt, node.getDataType());
}
@Test
public void testCopyReplaceDataTypeYes() throws Exception {
// drag/copy a data type onto another data type
// get Option dialog, and choose "Yes"
// cause a conflict
// in the program, rename Category1/Category2/Category3/IntStruct to ArrayStruct
String structName = "ArrayStruct";
DataTypeNode structureNode = createAndSelectStructure(structName);
Structure structure = (Structure) structureNode.getDataType();
CategoryNode category3Node = (CategoryNode) structureNode.getParent();
// drag/move ArrayStruct to MISC/ArrayStruct
String miscName = "MISC";
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
expandNode(miscNode);
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
DataType miscStructure = miscStructureNode.getDataType();
copyNodeToNode(structureNode, miscStructureNode);
pressButtonOnOptionDialog("Yes");
copyDragNodeToNode(structureNode, miscStructureNode);
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
assertNotNull(structureNode.getParent());
@ -615,26 +538,251 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
assertNotNull(category3Node.getChild(structName));
miscNode = (CategoryNode) tree.getViewNode(miscNode);
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
assertTrue(structure.isEquivalent(node.getDataType()));
DataTypeNode newNode =
(DataTypeNode) miscNode.getChild(structName + DataType.CONFLICT_SUFFIX);
assertNotNull(newNode);
assertNotNull(structureNode.getParent());
}
undo();
@Test
public void testCopyPasteToCategory() {
miscNode = (CategoryNode) programNode.getChild(miscName);
node = (DataTypeNode) miscNode.getChild(structName);
assertTrue(miscStructure.isEquivalent(node.getDataType()));
String miscName = "MISC";
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
expandNode(miscNode);
redo();
String dtName = "ArrayStruct";
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName);
selectNode(miscStructureNode);
miscNode = (CategoryNode) programNode.getChild(miscName);
node = (DataTypeNode) miscNode.getChild(structName);
assertTrue(structure.isEquivalent(node.getDataType()));
DockingActionIf copyAction = getAction(plugin, "Copy");
assertTrue(copyAction.isEnabledForContext(treeContext));
assertFalse(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(copyAction, tree);
selectNode(miscNode);
assertTrue(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(pasteAction, tree);
GTreeNode newNode = miscNode.getChild("Copy_1_of_" + dtName);
assertNotNull(newNode);
selectNode(miscNode);
assertTrue(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(pasteAction, tree);
newNode = miscNode.getChild("Copy_2_of_" + dtName);
assertNotNull(newNode);
}
@Test
public void testCopyPasteToDataType_SameType() throws Exception {
String miscName = "MISC";
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
expandNode(miscNode);
String dtName = "ArrayStruct";
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName);
selectNode(miscStructureNode);
DockingActionIf copyAction = getAction(plugin, "Copy");
assertTrue(copyAction.isEnabledForContext(treeContext));
assertFalse(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(copyAction, tree);
selectNode(miscStructureNode);
assertTrue(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(pasteAction, tree);
GTreeNode newNode = miscNode.getChild("Copy_1_of_" + dtName);
assertNotNull(newNode);
selectNode(miscStructureNode);
assertTrue(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(pasteAction, tree);
newNode = miscNode.getChild("Copy_2_of_" + dtName);
assertNotNull(newNode);
}
@Test
public void testCopyPasteToDataType_DifferentType() throws Exception {
String miscName = "MISC";
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
expandNode(miscNode);
String dtName = "ArrayStruct";
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName);
selectNode(miscStructureNode);
DockingActionIf copyAction = getAction(plugin, "Copy");
assertTrue(copyAction.isEnabledForContext(treeContext));
assertFalse(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(copyAction, tree);
DataTypeNode miscUnionNode = (DataTypeNode) miscNode.getChild("ArrayUnion");
selectNode(miscUnionNode);
assertTrue(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(pasteAction, tree);
GTreeNode newNode = miscNode.getChild("Copy_1_of_" + dtName);
assertNotNull(newNode);
}
@Test
public void testCopyPasteToDataType_DifferentType_SameName_RenameConflictHanlder()
throws Exception {
enableRenameConflictHandler();
String existingDtName = "ArrayUnion";
DataTypeNode intStructureNode =
(DataTypeNode) getNotepadNode("Category1/Category2/Category3/IntStruct");
rename(intStructureNode, existingDtName);
intStructureNode =
(DataTypeNode) getNotepadNode("Category1/Category2/Category3/" + existingDtName);
selectNode(intStructureNode);
DockingActionIf copyAction = getAction(plugin, "Copy");
assertTrue(copyAction.isEnabledForContext(treeContext));
assertFalse(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(copyAction, tree);
String miscName = "MISC";
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
expandNode(miscNode);
DataTypeNode miscUnionNode = (DataTypeNode) miscNode.getChild(existingDtName);
selectNode(miscUnionNode);
assertTrue(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(pasteAction, tree);
waitForTree();
GTreeNode newNode = miscNode.getChild(existingDtName + DataType.CONFLICT_SUFFIX);
assertNotNull(newNode);
}
@Test
public void testCopyPasteToDataType_FromDifferentCategory() throws Exception {
String miscName = "MISC";
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
expandNode(miscNode);
DataTypeNode intStructureNode =
(DataTypeNode) getNotepadNode("Category1/Category2/Category3/IntStruct");
selectNode(intStructureNode);
DockingActionIf copyAction = getAction(plugin, "Copy");
assertTrue(copyAction.isEnabledForContext(treeContext));
assertFalse(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(copyAction, tree);
String dtName = "ArrayStruct";
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName);
selectNode(miscStructureNode);
assertTrue(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(pasteAction, tree);
GTreeNode newNode = miscNode.getChild("IntStruct");
assertNotNull(newNode);
}
@Test
public void testCopyPasteToDataType_MultipleDataTypes() {
String miscName = "MISC";
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
expandNode(miscNode);
String dtName1 = "ArrayStruct";
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName1);
String dtName2 = "ArrayUnion";
DataTypeNode miscUnionNode = (DataTypeNode) miscNode.getChild(dtName2);
selectNodes(miscStructureNode, miscUnionNode);
DockingActionIf copyAction = getAction(plugin, "Copy");
assertTrue(copyAction.isEnabledForContext(treeContext));
assertFalse(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(copyAction, tree);
DataTypeNode intStructureNode =
(DataTypeNode) getNotepadNode("Category1/Category2/Category3/IntStruct");
selectNode(intStructureNode);
assertTrue(pasteAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(pasteAction, tree);
GTreeNode newNode = miscNode.getChild(dtName1);
assertNotNull(newNode);
newNode = miscNode.getChild(dtName2);
assertNotNull(newNode);
}
@Test
public void testReplaceAction() {
String miscName = "MISC";
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
expandNode(miscNode);
String originalDtName = "ArrayStruct";
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(originalDtName);
selectNode(miscStructureNode);
DockingActionIf replaceAction = getAction(plugin, "Replace Data Type");
assertTrue(replaceAction.isEnabledForContext(treeContext));
DataTypeTestUtils.performAction(replaceAction, tree, false);
String newDtName = "IntStruct";
chooseDataType(newDtName);
DataTypeNode updatedNode = (DataTypeNode) miscNode.getChild(newDtName);
assertNotNull(updatedNode);
}
//==================================================================================================
// Private Refactored Methods
// Private Methods
//==================================================================================================
private void chooseDataType(String dtName) {
DataTypeSelectionDialog chooser = waitForDialogComponent(DataTypeSelectionDialog.class);
JTextField tf = findComponent(chooser, JTextField.class);
triggerText(tf, dtName);
pressButtonByText(chooser, "OK");
waitForTasks();
}
private GTreeNode getNotepadNode(String path) {
GTreeNode last = programNode;
String[] names = path.split("/");
for (String name : names) {
last = last.getChild(name);
}
return last;
}
private void rename(DataTypeNode node, String newName) throws Exception {
DataType dt = node.getDataType();
tx(program, () -> dt.setName(newName));
waitForProgram();
}
/**
* In the program, rename Category1/Category2/Category3/IntStruct to <structureName>
*/
@ -649,9 +797,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
DataTypeNode structureNode = (DataTypeNode) category3Node.getChild("IntStruct");
Structure structure = (Structure) structureNode.getDataType();
int transactionID = program.startTransaction("test");
structure.setName(structureName);
program.endTransaction(transactionID, true);
tx(program, () -> structure.setName(structureName));
waitForProgram();
structureNode = (DataTypeNode) category3Node.getChild(structureName);
@ -659,9 +805,6 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
return structureNode;
}
/**
* Copies the currently selected node to the node by the given name.
*/
private CategoryNode copyPasteSelectedNodeToNode(String toNodeName) throws Exception {
DockingActionIf copyAction = getAction(plugin, "Copy");
assertTrue(copyAction.isEnabled());
@ -671,10 +814,8 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
expandNode(miscNode);
selectNode(miscNode);
executeOnSwingWithoutBlocking(() -> {
DockingActionIf pasteAction = getAction(plugin, "Paste");
DataTypeTestUtils.performAction(pasteAction, tree);
});
runSwing(() -> DataTypeTestUtils.performAction(pasteAction, tree), false);
waitForTasks();
return miscNode;
}
@ -686,40 +827,30 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
expandNode(miscNode);
selectNode(miscNode);
executeOnSwingWithoutBlocking(() -> {
DockingActionIf pasteAction = getAction(plugin, "Paste");
DataTypeTestUtils.performAction(pasteAction, tree);
});
runSwing(() -> DataTypeTestUtils.performAction(pasteAction, tree), false);
waitForTasks();
return miscNode;
}
private void pressButtonOnOptionDialog(String buttonName) throws Exception {
OptionDialog d = waitForDialogComponent(OptionDialog.class);
JButton button = findButtonByText(d, buttonName);
assertNotNull(button);
pressButton(button);
waitForProgram();
}
private void dragNodeToNode(GTreeNode fromNode, final GTreeNode toNode) {
private void moveDragNodeToNode(GTreeNode fromNode, final GTreeNode toNode) {
final GTreeDragNDropHandler dragNDropHandler = tree.getDragNDropHandler();
List<GTreeNode> dropList = new ArrayList<>();
dropList.add(fromNode);
final Transferable transferable = new GTreeNodeTransferable(dragNDropHandler, dropList);
runSwing(
() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_MOVE), false);
waitForSwing();
runSwing(() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_MOVE),
false);
waitForTasks();
}
private void copyNodeToNode(GTreeNode fromNode, final GTreeNode toNode) throws Exception {
private void copyDragNodeToNode(GTreeNode fromNode, final GTreeNode toNode) throws Exception {
final GTreeDragNDropHandler dragNDropHandler = tree.getDragNDropHandler();
List<GTreeNode> dropList = new ArrayList<>();
dropList.add(fromNode);
final Transferable transferable = new GTreeNodeTransferable(dragNDropHandler, dropList);
runSwing(
() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_COPY), false);
Transferable transferable = new GTreeNodeTransferable(dragNDropHandler, dropList);
runSwing(() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_COPY),
false);
waitForTasks();
}
//==================================================================================================
@ -735,6 +866,11 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
waitForTree();
}
private void selectNodes(GTreeNode... nodes) {
tree.setSelectedNodes(nodes);
waitForTree();
}
private void waitForTree() {
waitForTree(tree);
}

View file

@ -113,6 +113,9 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
treeContext = new DataTypesActionContext(provider, program, tree, null);
removeDistractingPlugins();
cutAction = getAction(plugin, "Copy");
pasteAction = getAction(plugin, "Paste");
}
private void removeDistractingPlugins() {
@ -589,8 +592,6 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
selectNode(myStructNode);
DockingActionIf copyAction = getAction(plugin, "Copy");
cutAction = getAction(plugin, "Cut");
pasteAction = getAction(plugin, "Paste");
assertTrue(cutAction.isEnabledForContext(treeContext));
assertTrue(copyAction.isEnabledForContext(treeContext));
@ -616,8 +617,6 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
selectNode(cat2Node);
DockingActionIf copyAction = getAction(plugin, "Copy");
cutAction = getAction(plugin, "Cut");
pasteAction = getAction(plugin, "Paste");
assertTrue(cutAction.isEnabledForContext(treeContext));
assertTrue(copyAction.isEnabledForContext(treeContext));

View file

@ -25,28 +25,31 @@ import ghidra.util.task.TaskMonitor;
* Each data type resides in a given a category.
*/
public interface Category extends Comparable<Category> {
/**
* Get the name of this category.
* @return the name.
*/
public abstract String getName();
/**
* Sets the name of this category.
* @param name the new name for this category
* @throws DuplicateNameException if another category exists in the same parent with the same name;
* @throws DuplicateNameException if another category exists in the same parent with the same
* name
* @throws InvalidNameException if the name is not an acceptable name.
*/
public abstract void setName(String name) throws DuplicateNameException, InvalidNameException;
/**
* Get all categories in this category.
* @return zero-length array if there are no categories
* @return zero-length array if there are no categories.
*/
public abstract Category[] getCategories();
/**
* Get all data types in this category.
* @return zero-length array if there are no data types
* @return zero-length array if there are no data types.
*/
public abstract DataType[] getDataTypes();
@ -56,10 +59,11 @@ public interface Category extends Comparable<Category> {
* occurs. In other words, finds all data types whose name matches the given name once
* any conflict suffixes have been removed from both the given name and the data types
* that are being scanned.
* @param name the name for which to get conflict related data types in this category. Note: the
* name that is passed in will be normalized to its base name, so you may pass in names with .conflict
* appended as a convenience.
* @return a list of data types that have the same base name as the base name of the given name
* @param name the name for which to get conflict related data types in this category. Note:
* the name that is passed in will be normalized to its base name, so you may pass in names
* with .conflict appended as a convenience.
* @return a list of data types that have the same base name as the base name of the given
* name.
*/
public abstract List<DataType> getDataTypesByBaseName(String name);
@ -73,8 +77,8 @@ public interface Category extends Comparable<Category> {
/**
* Get a category with the given name.
* @param name the name of the category
* @return null if there is no category by this name
* @param name the name of the category.
* @return null if there is no category by this name.
*/
public abstract Category getCategory(String name);
@ -86,39 +90,41 @@ public interface Category extends Comparable<Category> {
/**
* Get a data type with the given name.
* @param name the name of the data type
* @return null if there is no data type by this name
* @param name the name of the data type.
* @return null if there is no data type by this name.
*/
public abstract DataType getDataType(String name);
/**
* Create a category with the given name; if category already exists, then
* return that category.
* @param name the category name
* @throws InvalidNameException if name has invalid characters
* Create a category with the given name; if category already exists, then return that
* category.
* @param name the category name.
* @return the category.
* @throws InvalidNameException if name has invalid characters.
*/
public abstract Category createCategory(String name) throws InvalidNameException;
/**
* Remove the named category from this category.
* @param name the name of the category to remove
* @param monitor the task monitor
* @return true if the category was removed
* @param name the name of the category to remove.
* @param monitor the task monitor.
* @return true if the category was removed.
*/
public abstract boolean removeCategory(String name, TaskMonitor monitor);
/**
* Remove the named category from this category, IFF it is empty.
* @param name the name of the category to remove
* @param monitor the task monitor
* @return true if the category was removed
* @param name the name of the category to remove.
* @param monitor the task monitor.
* @return true if the category was removed.
*/
public abstract boolean removeEmptyCategory(String name, TaskMonitor monitor);
/**
* Move the given category to this category; category is removed from
* its original parent category.
* @param category the category to move
* Move the given category to this category; category is removed from its original parent
* category.
* @param category the category to move.
* @param monitor the monitor.
* @throws DuplicateNameException if this category already contains a
* category or data type with the same name as the category param.
*/
@ -126,15 +132,18 @@ public interface Category extends Comparable<Category> {
throws DuplicateNameException;
/**
* Make a new subcategory from the given category.
* @param category the category to copy into this category
* @return category that is added to this category
* Make a new sub-category from the given category.
* @param category the category to copy into this category.
* @param handler the handler to call if there is a data type conflict.
* @param monitor the monitor.
* @return category that is added to this category.
*/
public abstract Category copyCategory(Category category, DataTypeConflictHandler handler,
TaskMonitor monitor);
/**
* Return this category's parent; return null if this is the root category.
* @return the category.
*/
public abstract Category getParent();
@ -146,33 +155,36 @@ public interface Category extends Comparable<Category> {
/**
* Get the fully qualified name for this category.
* @return the name.
*/
public abstract String getCategoryPathName();
/**
* Get the root category.
* @return the category.
*/
public abstract Category getRoot();
/**
* Get the data type manager associated with this category.
* @return the manager.
*/
public abstract DataTypeManager getDataTypeManager();
/**
* Move a data type into this category
* Move a data type into this category.
*
* @param type data type to be moved
* @param handler the handler to call if there is a data type conflict
* @throws DataTypeDependencyException
* @param type data type to be moved.
* @param handler the handler to call if there is a data type conflict.
* @throws DataTypeDependencyException if a disallowed dependency is created during the move.
*/
public abstract void moveDataType(DataType type, DataTypeConflictHandler handler)
throws DataTypeDependencyException;
/**
* Remove a datatype from this category
* Remove a datatype from this category.
*
* @param type data type to be removed
* @param type data type to be removed.
* @param monitor monitor of progress in case operation takes a long time.
* @return true if the data type was found in this category and successfully removed.
*/
@ -180,6 +192,7 @@ public interface Category extends Comparable<Category> {
/**
* Get the ID for this category.
* @return the ID.
*/
public long getID();
}