mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
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:
parent
073c726885
commit
2cefdb7688
14 changed files with 687 additions and 502 deletions
|
@ -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/help/empty.htm||GHIDRA||||END|
|
||||||
src/main/resources/images/2leftarrow.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|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/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/Array.png||GHIDRA||||END|
|
||||||
src/main/resources/images/B.gif||GHIDRA||||END|
|
src/main/resources/images/B.gif||GHIDRA||||END|
|
||||||
src/main/resources/images/BookShelf.png||GHIDRA||||END|
|
src/main/resources/images/BookShelf.png||GHIDRA||||END|
|
||||||
|
|
|
@ -18,13 +18,6 @@
|
||||||
program. Allowing the user to build libraries of data types and to share them between programs,
|
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>
|
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>
|
<BLOCKQUOTE>
|
||||||
|
|
||||||
<H2>Topics</H2>
|
<H2>Topics</H2>
|
||||||
|
@ -1008,7 +1001,7 @@
|
||||||
<LI><I>Rename</I> the data type that you are dragging to have ".conflict" appended to
|
<LI><I>Rename</I> the data type that you are dragging to have ".conflict" appended to
|
||||||
it to make a unique name.</LI>
|
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
|
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>
|
data type; the existing data type is deleted.</LI>
|
||||||
|
|
||||||
|
@ -1023,30 +1016,9 @@
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>A data type can be replaced by another data type. This means that every occurrence of
|
<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
|
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>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H3><A name="Favorites"></A>Setting Favorite Data Types</H3>
|
<H3><A name="Favorites"></A>Setting Favorite Data Types</H3>
|
||||||
|
|
|
@ -524,19 +524,6 @@
|
||||||
<H2>Miscellaneous Actions</H2>
|
<H2>Miscellaneous Actions</H2>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<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>
|
<H3><A name="Copy"></A>Copy</H3>
|
||||||
|
|
||||||
|
@ -552,13 +539,45 @@
|
||||||
</P>
|
</P>
|
||||||
</BLOCKQUOTE>
|
</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="Delete"></A>Delete</H3>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>The <I><B>Delete</B></I> action is used to
|
||||||
|
<A href="data_type_manager_description.htm#DeleteDataType">delete data
|
||||||
|
types</A> and/or
|
||||||
|
<A href="data_type_manager_description.htm#DeleteCategory">delete categories</A>. A confirmation dialog
|
||||||
|
will appear before actually deleting the selected data types and categories.</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
|
||||||
<H3><A name="Paste"></A>Paste</H3>
|
<H3><A name="Paste"></A>Paste</H3>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>The<I><B>Paste</B></I> action is used to complete a move or copy operation as
|
<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
|
initiated by either the <I><B>Copy</B></I> or <I><B>Cut</B></I> action. The category
|
||||||
is selected will be the destination for whatever nodes where selected when the
|
node (or the parent category of a data type node) that is selected will be the
|
||||||
<I><B>Copy</B></I> or <I><B>Paste</B></I> was invoked.</P>
|
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>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H3><A name="Rename"></A>Rename</H3>
|
<H3><A name="Rename"></A>Rename</H3>
|
||||||
|
@ -571,26 +590,29 @@
|
||||||
type</A>.</P>
|
type</A>.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H3><A name="Delete"></A>Delete</H3>
|
|
||||||
|
<H3><A name="Replace_Data_Type"></A>Replace Data Type...</H3>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>The <I><B>Delete</B></I> action is used to
|
<P>The <I><B>Replace Data Type...</B></I> action is used to
|
||||||
<A href="data_type_manager_description.htm#DeleteDataType">delete data
|
<A href="data_type_manager_description.htm#ReplaceDataType">replace</A> a
|
||||||
types</A> and/or
|
selected data type and all occurrences in the program.</P>
|
||||||
<A href="data_type_manager_description.htm#DeleteCategory">delete categories</A>. A confirmation dialog
|
|
||||||
will appear before actually deleting the selected data types and categories.</P>
|
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H3><A name="Collapse_All"></A>Collapse All</H3>
|
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H3><A name="Collapse_All"></A>Collapse All</H3>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>To collapse all open nodes in a sub-tree, right-click on the root node of the
|
<P>To collapse all open nodes in a sub-tree, right-click on the root node of the
|
||||||
sub-tree and select the <I><B>Collapse All</B></I> action. If this action is invoked
|
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>
|
via the local toolbar, then the entire tree is collapsed.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
</BLOCKQUOTE>
|
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
|
||||||
<H3><A name="Expand_All"></A>Expand All</H3>
|
<H3><A name="Expand_All"></A>Expand All</H3>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
addLocalAction(new CutAction(plugin));
|
addLocalAction(new CutAction(plugin));
|
||||||
addLocalAction(new CopyAction(plugin));
|
addLocalAction(new CopyAction(plugin));
|
||||||
addLocalAction(new PasteAction(plugin));
|
addLocalAction(new PasteAction(plugin));
|
||||||
|
addLocalAction(new ReplaceDataTypeAction(plugin));
|
||||||
addLocalAction(new DeleteAction(plugin));
|
addLocalAction(new DeleteAction(plugin));
|
||||||
addLocalAction(new DeleteArchiveAction(plugin));
|
addLocalAction(new DeleteArchiveAction(plugin));
|
||||||
addLocalAction(new RenameAction(plugin));
|
addLocalAction(new RenameAction(plugin));
|
||||||
|
@ -181,8 +182,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||||
addLocalAction(new FindDataTypesBySizeAction(plugin, "2"));
|
addLocalAction(new FindDataTypesBySizeAction(plugin, "2"));
|
||||||
addLocalAction(new FindStructuresByOffsetAction(plugin, "3"));
|
addLocalAction(new FindStructuresByOffsetAction(plugin, "3"));
|
||||||
addLocalAction(new FindStructuresBySizeAction(plugin, "4"));
|
addLocalAction(new FindStructuresBySizeAction(plugin, "4"));
|
||||||
includeDataMembersInSearchAction =
|
includeDataMembersInSearchAction = new IncludeDataTypesInFilterAction(plugin, this, "5");
|
||||||
new IncludeDataTypesInFilterAction(plugin, this, "5");
|
|
||||||
addLocalAction(includeDataMembersInSearchAction);
|
addLocalAction(includeDataMembersInSearchAction);
|
||||||
|
|
||||||
addLocalAction(new ApplyFunctionDataTypesAction(plugin)); // Tree
|
addLocalAction(new ApplyFunctionDataTypesAction(plugin)); // Tree
|
||||||
|
|
|
@ -48,9 +48,9 @@ public class PasteAction extends DockingAction {
|
||||||
this.clipboard = plugin.getClipboard();
|
this.clipboard = plugin.getClipboard();
|
||||||
this.tool = plugin.getTool();
|
this.tool = plugin.getTool();
|
||||||
setPopupMenuData(new MenuData(new String[] { "Paste" }, "Edit"));
|
setPopupMenuData(new MenuData(new String[] { "Paste" }, "Edit"));
|
||||||
setKeyBindingData(new KeyBindingData(KeyStroke.getKeyStroke(KeyEvent.VK_V,
|
setKeyBindingData(
|
||||||
InputEvent.CTRL_DOWN_MASK), KeyBindingPrecedence.ActionMapLevel));
|
new KeyBindingData(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK),
|
||||||
setEnabled(true);
|
KeyBindingPrecedence.ActionMapLevel));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -65,7 +65,7 @@ public class PasteAction extends DockingAction {
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledForContext(ActionContext context) {
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
DataTypeTreeNode node = getSelectedDataTypeTreeNode(context);
|
DataTypeTreeNode node = getSelectedDataTypeTreeNode(context);
|
||||||
if (!(node instanceof CategoryNode) || !((CategoryNode) node).isEnabled()) {
|
if (node == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
List<GTreeNode> transferNodeList = getNodesFromClipboard();
|
List<GTreeNode> transferNodeList = getNodesFromClipboard();
|
||||||
|
@ -96,7 +96,7 @@ public class PasteAction extends DockingAction {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (invalidCutNodes(destinationNode, transferNodeList)) {
|
if (hasInvalidCutNodes(destinationNode, transferNodeList)) {
|
||||||
return false; // cut nodes that cannot be pasted here
|
return false; // cut nodes that cannot be pasted here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,13 +109,14 @@ public class PasteAction extends DockingAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFlavor[] flavors = handler.getSupportedDataFlavors(transferNodeList);
|
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);
|
DataTypeTreeNode node = (DataTypeTreeNode) nodeList.get(0);
|
||||||
if (!node.isCut()) {
|
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
|
// can't cut nodes from one archive and paste into another
|
||||||
|
@ -142,28 +143,37 @@ public class PasteAction extends DockingAction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
GTree gTree = (GTree) context.getContextObject();
|
|
||||||
|
|
||||||
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
|
||||||
GTreeNode destinationNode = (GTreeNode) selectionPaths[0].getLastPathComponent();
|
|
||||||
|
|
||||||
List<GTreeNode> nodeList = getNodesFromClipboard();
|
List<GTreeNode> nodeList = getNodesFromClipboard();
|
||||||
if (nodeList.isEmpty()) {
|
if (nodeList.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GTree gTree = (GTree) context.getContextObject();
|
||||||
|
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
||||||
|
CategoryNode destinationNode = getDropTargetNode(selectionPaths);
|
||||||
DataTypeTreeNode dataTypeTreeNode = (DataTypeTreeNode) nodeList.get(0);
|
DataTypeTreeNode dataTypeTreeNode = (DataTypeTreeNode) nodeList.get(0);
|
||||||
if (dataTypeTreeNode.isCut()) { // clear cut nodes on paste operation
|
if (dataTypeTreeNode.isCut()) { // clear cut nodes on paste operation
|
||||||
clipboard.setContents(null, null);
|
clipboard.setContents(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionType actionType = getActionType(dataTypeTreeNode);
|
ActionType actionType = getActionType(dataTypeTreeNode);
|
||||||
DataTypeTreeCopyMoveTask task =
|
DataTypeTreeCopyMoveTask task = new DataTypeTreeCopyMoveTask(destinationNode, nodeList,
|
||||||
new DataTypeTreeCopyMoveTask(destinationNode, nodeList, actionType,
|
actionType, (DataTypeArchiveGTree) gTree, plugin.getConflictHandler());
|
||||||
(DataTypeArchiveGTree) gTree,
|
|
||||||
plugin.getConflictHandler());
|
|
||||||
tool.execute(task, 250);
|
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) {
|
private ActionType getActionType(DataTypeTreeNode pasteNode) {
|
||||||
if (pasteNode.isCut()) {
|
if (pasteNode.isCut()) {
|
||||||
return ActionType.MOVE;
|
return ActionType.MOVE;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -585,8 +585,8 @@ public class DataTypeManagerHandler {
|
||||||
dataTypeManager.updateSourceArchiveName(existingDataTypeManager.getUniversalID(),
|
dataTypeManager.updateSourceArchiveName(existingDataTypeManager.getUniversalID(),
|
||||||
existingDataTypeManager.getName());
|
existingDataTypeManager.getName());
|
||||||
|
|
||||||
int existingTxID = existingDataTypeManager.startTransaction(
|
int existingTxID = existingDataTypeManager
|
||||||
"Update Data Type Source Archive Name");
|
.startTransaction("Update Data Type Source Archive Name");
|
||||||
try {
|
try {
|
||||||
existingDataTypeManager.updateSourceArchiveName(
|
existingDataTypeManager.updateSourceArchiveName(
|
||||||
dataTypeManager.getUniversalID(), dataTypeManager.getName());
|
dataTypeManager.getUniversalID(), dataTypeManager.getName());
|
||||||
|
@ -686,7 +686,7 @@ public class DataTypeManagerHandler {
|
||||||
Msg.info(this, "Closed archive: '" + archive.getName() + "'");
|
Msg.info(this, "Closed archive: '" + archive.getName() + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Archive getArchive(DataTypeManager dtm) {
|
public Archive getArchive(DataTypeManager dtm) {
|
||||||
for (Archive archive : openArchives) {
|
for (Archive archive : openArchives) {
|
||||||
DataTypeManager dataTypeManager = archive.getDataTypeManager();
|
DataTypeManager dataTypeManager = archive.getDataTypeManager();
|
||||||
if (dataTypeManager.equals(dtm)) {
|
if (dataTypeManager.equals(dtm)) {
|
||||||
|
@ -814,43 +814,43 @@ public class DataTypeManagerHandler {
|
||||||
|
|
||||||
private void initializeFavorites() {
|
private void initializeFavorites() {
|
||||||
|
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager
|
||||||
builtInDataTypesManager.resolve(PointerDataType.dataType, null), true);
|
.setFavorite(builtInDataTypesManager.resolve(PointerDataType.dataType, null), true);
|
||||||
|
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager
|
||||||
builtInDataTypesManager.resolve(CharDataType.dataType, null), true);
|
.setFavorite(builtInDataTypesManager.resolve(CharDataType.dataType, null), true);
|
||||||
|
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager
|
||||||
builtInDataTypesManager.resolve(StringDataType.dataType, null), true);
|
.setFavorite(builtInDataTypesManager.resolve(StringDataType.dataType, null), true);
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager.setFavorite(
|
||||||
builtInDataTypesManager.resolve(TerminatedStringDataType.dataType, null), true);
|
builtInDataTypesManager.resolve(TerminatedStringDataType.dataType, null), true);
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager.setFavorite(
|
||||||
builtInDataTypesManager.resolve(TerminatedUnicodeDataType.dataType, null), true);
|
builtInDataTypesManager.resolve(TerminatedUnicodeDataType.dataType, null), true);
|
||||||
|
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager
|
||||||
builtInDataTypesManager.resolve(FloatDataType.dataType, null), true);
|
.setFavorite(builtInDataTypesManager.resolve(FloatDataType.dataType, null), true);
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager
|
||||||
builtInDataTypesManager.resolve(DoubleDataType.dataType, null), true);
|
.setFavorite(builtInDataTypesManager.resolve(DoubleDataType.dataType, null), true);
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager.setFavorite(
|
||||||
builtInDataTypesManager.resolve(LongDoubleDataType.dataType, null), true);
|
builtInDataTypesManager.resolve(LongDoubleDataType.dataType, null), true);
|
||||||
|
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager
|
||||||
builtInDataTypesManager.resolve(IntegerDataType.dataType, null), true);
|
.setFavorite(builtInDataTypesManager.resolve(IntegerDataType.dataType, null), true);
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager
|
||||||
builtInDataTypesManager.resolve(LongDataType.dataType, null), true);
|
.setFavorite(builtInDataTypesManager.resolve(LongDataType.dataType, null), true);
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager.setFavorite(
|
||||||
builtInDataTypesManager.resolve(UnsignedIntegerDataType.dataType, null), true);
|
builtInDataTypesManager.resolve(UnsignedIntegerDataType.dataType, null), true);
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager.setFavorite(
|
||||||
builtInDataTypesManager.resolve(UnsignedLongDataType.dataType, null), true);
|
builtInDataTypesManager.resolve(UnsignedLongDataType.dataType, null), true);
|
||||||
|
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager
|
||||||
builtInDataTypesManager.resolve(ByteDataType.dataType, null), true);
|
.setFavorite(builtInDataTypesManager.resolve(ByteDataType.dataType, null), true);
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager
|
||||||
builtInDataTypesManager.resolve(WordDataType.dataType, null), true);
|
.setFavorite(builtInDataTypesManager.resolve(WordDataType.dataType, null), true);
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager
|
||||||
builtInDataTypesManager.resolve(DWordDataType.dataType, null), true);
|
.setFavorite(builtInDataTypesManager.resolve(DWordDataType.dataType, null), true);
|
||||||
builtInDataTypesManager.setFavorite(
|
builtInDataTypesManager
|
||||||
builtInDataTypesManager.resolve(QWordDataType.dataType, null), true);
|
.setFavorite(builtInDataTypesManager.resolve(QWordDataType.dataType, null), true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1428,8 +1428,8 @@ public class DataTypeManagerHandler {
|
||||||
new DataTreeDialog(null, "Save As", DataTreeDialog.SAVE, domainFileFilter);
|
new DataTreeDialog(null, "Save As", DataTreeDialog.SAVE, domainFileFilter);
|
||||||
|
|
||||||
dataTreeSaveDialog.addOkActionListener(listener);
|
dataTreeSaveDialog.addOkActionListener(listener);
|
||||||
dataTreeSaveDialog.setHelpLocation(
|
dataTreeSaveDialog
|
||||||
new HelpLocation(HelpTopics.PROGRAM, "Save_As_File"));
|
.setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Save_As_File"));
|
||||||
}
|
}
|
||||||
return dataTreeSaveDialog;
|
return dataTreeSaveDialog;
|
||||||
}
|
}
|
||||||
|
@ -1614,8 +1614,8 @@ public class DataTypeManagerHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
|
public void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
|
||||||
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE.equals(
|
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE
|
||||||
file.getContentType())) {
|
.equals(file.getContentType())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Iterator<Archive> archiveIter = openArchives.iterator();
|
Iterator<Archive> archiveIter = openArchives.iterator();
|
||||||
|
@ -1658,8 +1658,8 @@ public class DataTypeManagerHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void domainFileRenamed(DomainFile file, String oldName) {
|
public void domainFileRenamed(DomainFile file, String oldName) {
|
||||||
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE.equals(
|
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE
|
||||||
file.getContentType())) {
|
.equals(file.getContentType())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String newName = file.getName();
|
String newName = file.getName();
|
||||||
|
|
|
@ -37,11 +37,11 @@ import ghidra.util.task.Task;
|
||||||
public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
|
public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
|
||||||
private static DataFlavor localDataTypeTreeFlavor = createLocalTreeNodeFlavor();
|
private static DataFlavor localDataTypeTreeFlavor = createLocalTreeNodeFlavor();
|
||||||
|
|
||||||
public static DataFlavor[] allSupportedFlavors = { DataTypeTransferable.localDataTypeFlavor,
|
public static DataFlavor[] allSupportedFlavors =
|
||||||
localDataTypeTreeFlavor };
|
{ DataTypeTransferable.localDataTypeFlavor, localDataTypeTreeFlavor };
|
||||||
|
|
||||||
public static DataFlavor[] builtinFlavors = { DataTypeTransferable.localBuiltinDataTypeFlavor,
|
public static DataFlavor[] builtinFlavors =
|
||||||
localDataTypeTreeFlavor };
|
{ DataTypeTransferable.localBuiltinDataTypeFlavor, localDataTypeTreeFlavor };
|
||||||
|
|
||||||
public static DataFlavor[] restrictedFlavors = { 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
|
// create a data flavor that is an List of GTreeNodes
|
||||||
private static DataFlavor createLocalTreeNodeFlavor() {
|
private static DataFlavor createLocalTreeNodeFlavor() {
|
||||||
try {
|
try {
|
||||||
return new GenericDataFlavor(DataFlavor.javaJVMLocalObjectMimeType +
|
return new GenericDataFlavor(
|
||||||
"; class=java.util.List", "Local list of Drag/Drop DataType Tree objects");
|
DataFlavor.javaJVMLocalObjectMimeType + "; class=java.util.List",
|
||||||
|
"Local list of Drag/Drop DataType Tree objects");
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
Msg.showError(DataTypeDragNDropHandler.class, null, null, null, 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.
|
if (list.contains(destinationNode)) { // don't allow drop on dragged nodes.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CategoryNode updatedDestinationNode = getDropTargetNode(destinationNode);
|
||||||
ActionType actionType =
|
ActionType actionType =
|
||||||
dropAction == DnDConstants.ACTION_COPY ? ActionType.COPY : ActionType.MOVE;
|
dropAction == DnDConstants.ACTION_COPY ? ActionType.COPY : ActionType.MOVE;
|
||||||
Task task =
|
Task task = new DataTypeTreeCopyMoveTask(updatedDestinationNode, list, actionType,
|
||||||
new DataTypeTreeCopyMoveTask(destinationNode, list, actionType,
|
(DataTypeArchiveGTree) tree, plugin.getConflictHandler());
|
||||||
(DataTypeArchiveGTree) tree,
|
|
||||||
plugin.getConflictHandler());
|
|
||||||
plugin.getTool().execute(task, 250);
|
plugin.getTool().execute(task, 250);
|
||||||
}
|
}
|
||||||
catch (UnsupportedFlavorException e) {
|
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
|
@Override
|
||||||
public DataFlavor[] getSupportedDataFlavors(List<GTreeNode> draggedNodes) {
|
public DataFlavor[] getSupportedDataFlavors(List<GTreeNode> draggedNodes) {
|
||||||
// single, datatype node supports both datatype dragging *and* local tree dragging
|
// single, datatype node supports both datatype dragging *and* local tree dragging
|
||||||
|
@ -150,12 +161,38 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDropSiteOk(GTreeNode destinationNode, DataFlavor[] flavors, int dropAction) {
|
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) {
|
if (destinationNode == null || destinationNode.getParent() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// can only drop nodes from other dataTypetrees
|
// can only drop/paste nodes from other dataType trees
|
||||||
if (!containsFlavor(flavors, localDataTypeTreeFlavor)) {
|
if (!containsFlavor(flavors, localDataTypeTreeFlavor)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -167,20 +204,6 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,11 +204,7 @@ public class DataTypeNode extends DataTypeTreeNode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPaste(List<GTreeNode> pastedNodes) {
|
public boolean canPaste(List<GTreeNode> pastedNodes) {
|
||||||
if (pastedNodes.size() != 1) {
|
return isModifiable();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
GTreeNode pastedNode = pastedNodes.get(0);
|
|
||||||
return pastedNode instanceof DataTypeNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -34,6 +34,7 @@ public abstract class DataTypeTreeNode extends GTreeLazyNode {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this nodes handles paste operations
|
* Returns true if this nodes handles paste operations
|
||||||
|
* @param pastedNodes the nodes to be pasted
|
||||||
* @return true if this nodes handles paste operations
|
* @return true if this nodes handles paste operations
|
||||||
*/
|
*/
|
||||||
public abstract boolean canPaste(List<GTreeNode> pastedNodes);
|
public abstract boolean canPaste(List<GTreeNode> pastedNodes);
|
||||||
|
|
|
@ -22,8 +22,6 @@ import java.util.regex.Pattern;
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import docking.widgets.tree.GTreeState;
|
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.Archive;
|
||||||
import ghidra.app.plugin.core.datamgr.archive.ProgramArchive;
|
import ghidra.app.plugin.core.datamgr.archive.ProgramArchive;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||||
|
@ -50,7 +48,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataTypeArchiveGTree gTree;
|
private DataTypeArchiveGTree gTree;
|
||||||
private GTreeNode destinationNode;
|
private CategoryNode destinationNode;
|
||||||
private List<GTreeNode> droppedNodes;
|
private List<GTreeNode> droppedNodes;
|
||||||
private Archive sourceArchive;
|
private Archive sourceArchive;
|
||||||
private Archive destinationArchive;
|
private Archive destinationArchive;
|
||||||
|
@ -64,7 +62,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
super("Drag/Drop", true, true, true);
|
super("Drag/Drop", true, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataTypeTreeCopyMoveTask(GTreeNode destinationNode, List<GTreeNode> droppedNodeList,
|
public DataTypeTreeCopyMoveTask(CategoryNode destinationNode, List<GTreeNode> droppedNodeList,
|
||||||
ActionType actionType, DataTypeArchiveGTree gTree,
|
ActionType actionType, DataTypeArchiveGTree gTree,
|
||||||
DataTypeConflictHandler conflictHandler) {
|
DataTypeConflictHandler conflictHandler) {
|
||||||
super("Drag/Drop", true, true, true);
|
super("Drag/Drop", true, true, true);
|
||||||
|
@ -118,7 +116,6 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
}
|
}
|
||||||
|
|
||||||
doCopy(monitor);
|
doCopy(monitor);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
return; // nothing to report
|
return; // nothing to report
|
||||||
|
@ -161,12 +158,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 {
|
||||||
if (destinationNode instanceof DataTypeNode) {
|
dragNodesToCategory(monitor);
|
||||||
dragNodeToDataType();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
dragNodesToCategory(monitor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
dtm.endTransaction(txId, true);
|
dtm.endTransaction(txId, true);
|
||||||
|
@ -189,8 +181,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void associateDataTypes(TaskMonitor monitor)
|
private void associateDataTypes(TaskMonitor monitor) throws CancelledException {
|
||||||
throws CancelledException {
|
|
||||||
|
|
||||||
if (!promptToAssociateTypes(monitor)) {
|
if (!promptToAssociateTypes(monitor)) {
|
||||||
return;
|
return;
|
||||||
|
@ -347,22 +338,19 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
DataTypeManager nodeDtm = dataType.getDataTypeManager();
|
DataTypeManager nodeDtm = dataType.getDataTypeManager();
|
||||||
boolean sameManager = (dtm == nodeDtm);
|
boolean sameManager = (dtm == nodeDtm);
|
||||||
DataType newDt = !sameManager ? dataType.clone(nodeDtm) : dataType.copy(nodeDtm);
|
DataType newDt = !sameManager ? dataType.clone(nodeDtm) : dataType.copy(nodeDtm);
|
||||||
if (sameManager &&
|
if (sameManager && newDt.getCategoryPath().equals(toCategory.getCategoryPath())) {
|
||||||
newDt.getCategoryPath().equals(toCategory.getCategoryPath())) {
|
|
||||||
renameAsCopy(toCategory, newDt);
|
renameAsCopy(toCategory, newDt);
|
||||||
}
|
}
|
||||||
|
|
||||||
DataType resolvedDt = toCategory.addDataType(newDt, conflictHandler);
|
DataType resolvedDt = toCategory.addDataType(newDt, conflictHandler);
|
||||||
if (resolvedDt instanceof Pointer || resolvedDt instanceof Array ||
|
if (resolvedDt instanceof Pointer || resolvedDt instanceof Array ||
|
||||||
resolvedDt instanceof BuiltInDataType ||
|
resolvedDt instanceof BuiltInDataType || resolvedDt instanceof MissingBuiltInDataType) {
|
||||||
resolvedDt instanceof MissingBuiltInDataType) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resolvedDt.getCategoryPath().equals(toCategory.getCategoryPath())) {
|
if (!resolvedDt.getCategoryPath().equals(toCategory.getCategoryPath())) {
|
||||||
errors.add(
|
errors.add("Data type copy failed. Another copy of this data type already exists at " +
|
||||||
"Data type copy failed. Another copy of this data type already exists at " +
|
resolvedDt.getPathName());
|
||||||
resolvedDt.getPathName());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,17 +405,15 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void moveCategory(Category toCategory, Category category,
|
private void moveCategory(Category toCategory, Category category, TaskMonitor monitor) {
|
||||||
TaskMonitor monitor) {
|
|
||||||
if (category.getParent() == toCategory) { // moving to same place
|
if (category.getParent() == toCategory) { // moving to same place
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
CategoryPath path = toCategory.getCategoryPath();
|
CategoryPath path = toCategory.getCategoryPath();
|
||||||
if (path.isAncestorOrSelf(category.getCategoryPath())) {
|
if (path.isAncestorOrSelf(category.getCategoryPath())) {
|
||||||
errors.add(
|
errors.add("Cannot move a parent node onto a child node. Moving " + category +
|
||||||
"Cannot move a parent node onto a child node. Moving " + category + " to " +
|
" to " + toCategory);
|
||||||
toCategory);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,30 +427,25 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
|
|
||||||
private void moveDataType(Category toCategory, DataType dataType) {
|
private void moveDataType(Category toCategory, DataType dataType) {
|
||||||
if (dataType.getCategoryPath().equals(toCategory.getCategoryPath())) {
|
if (dataType.getCategoryPath().equals(toCategory.getCategoryPath())) {
|
||||||
errors.add(
|
errors.add("Move failed. DataType is already in this category. Category " +
|
||||||
"Move failed. DataType is already in this category. Category " + toCategory +
|
toCategory + "; Data type: " + dataType);
|
||||||
"; Data type: " + dataType);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
toCategory.moveDataType(dataType, conflictHandler);
|
toCategory.moveDataType(dataType, conflictHandler);
|
||||||
}
|
}
|
||||||
catch (DataTypeDependencyException e) {
|
catch (DataTypeDependencyException e) {
|
||||||
errors.add(
|
errors.add("Move failed. DataType is already in this category. Category " +
|
||||||
"Move failed. DataType is already in this category. Category " + toCategory +
|
toCategory + "; Data type: " + dataType + ". " + e.getMessage());
|
||||||
"; Data type: " + dataType + ". " + e.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copyCategory(Category toCategory, Category category,
|
private void copyCategory(Category toCategory, Category category, TaskMonitor monitor) {
|
||||||
TaskMonitor monitor) {
|
|
||||||
CategoryPath toPath = toCategory.getCategoryPath();
|
CategoryPath toPath = toCategory.getCategoryPath();
|
||||||
boolean sameManager =
|
boolean sameManager = (toCategory.getDataTypeManager() == category.getDataTypeManager());
|
||||||
(toCategory.getDataTypeManager() == category.getDataTypeManager());
|
|
||||||
if (sameManager && toPath.isAncestorOrSelf(category.getCategoryPath())) {
|
if (sameManager && toPath.isAncestorOrSelf(category.getCategoryPath())) {
|
||||||
errors.add("Copy failed. " +
|
errors.add("Copy failed. " + "Cannot copy a parent node onto a child node. Moving " +
|
||||||
"Cannot copy a parent node onto a child node. Moving " + category + " to " +
|
category + " to " + toCategory);
|
||||||
toCategory);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
toCategory.copyCategory(category, conflictHandler, monitor);
|
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());
|
"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
|
* 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
|
||||||
|
@ -516,95 +476,11 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
||||||
return sourceId.equals(dtmId);
|
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() {
|
private int askToAssociateDataTypes() {
|
||||||
return OptionDialog.showYesNoCancelDialog(gTree, "Associate DataTypes?",
|
return OptionDialog.showYesNoCancelDialog(gTree, "Associate DataTypes?",
|
||||||
"Do you want to associate local datatypes with the target archive?");
|
"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
|
// filters out nodes with categories in their path
|
||||||
private void filterRedundantNodes() {
|
private void filterRedundantNodes() {
|
||||||
|
|
||||||
|
|
|
@ -22,20 +22,21 @@ import java.awt.dnd.DnDConstants;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.JButton;
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
|
||||||
import docking.action.DockingActionIf;
|
import docking.action.DockingActionIf;
|
||||||
import docking.menu.ActionState;
|
import docking.menu.ActionState;
|
||||||
import docking.widgets.OptionDialog;
|
|
||||||
import docking.widgets.tree.GTreeNode;
|
import docking.widgets.tree.GTreeNode;
|
||||||
import docking.widgets.tree.support.GTreeDragNDropHandler;
|
import docking.widgets.tree.support.GTreeDragNDropHandler;
|
||||||
import docking.widgets.tree.support.GTreeNodeTransferable;
|
import docking.widgets.tree.support.GTreeNodeTransferable;
|
||||||
|
import ghidra.app.context.ProgramActionContext;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.app.plugin.core.datamgr.actions.ConflictHandlerModesAction;
|
import ghidra.app.plugin.core.datamgr.actions.ConflictHandlerModesAction;
|
||||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||||
import ghidra.app.services.ProgramManager;
|
import ghidra.app.services.ProgramManager;
|
||||||
|
import ghidra.app.util.datatype.DataTypeSelectionDialog;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
|
@ -57,7 +58,9 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
private ProgramDB program;
|
private ProgramDB program;
|
||||||
private DataTypeManagerPlugin plugin;
|
private DataTypeManagerPlugin plugin;
|
||||||
private DataTypesProvider provider;
|
private DataTypesProvider provider;
|
||||||
|
private DockingActionIf pasteAction;
|
||||||
private ConflictHandlerModesAction conflictHandlerModesAction;
|
private ConflictHandlerModesAction conflictHandlerModesAction;
|
||||||
|
private ProgramActionContext treeContext;
|
||||||
private DataTypeArchiveGTree tree;
|
private DataTypeArchiveGTree tree;
|
||||||
private ArchiveRootNode archiveRootNode;
|
private ArchiveRootNode archiveRootNode;
|
||||||
private ArchiveNode programNode;
|
private ArchiveNode programNode;
|
||||||
|
@ -89,6 +92,9 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
||||||
|
|
||||||
tool.showComponentProvider(provider, true);
|
tool.showComponentProvider(provider, true);
|
||||||
|
|
||||||
|
pasteAction = getAction(plugin, "Paste");
|
||||||
|
treeContext = new DataTypesActionContext(provider, program, tree, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProgramDB buildProgram() throws Exception {
|
private ProgramDB buildProgram() throws Exception {
|
||||||
|
@ -164,7 +170,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConflictCopyInProgram() throws Exception {
|
public void testCopyPasteToCategory_RenameConflictHandler() throws Exception {
|
||||||
|
|
||||||
enableRenameConflictHandler();
|
enableRenameConflictHandler();
|
||||||
|
|
||||||
|
@ -194,7 +200,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConflictCopyReplace() throws Exception {
|
public void testCopyPasteToCategory_ReplaceExistingConflictHandler() throws Exception {
|
||||||
|
|
||||||
enableReplaceExistingConflictHandler();
|
enableReplaceExistingConflictHandler();
|
||||||
|
|
||||||
|
@ -213,7 +219,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConflictCopyUseExisting() throws Exception {
|
public void testCopyPasteToCategory_UseExistingConflictHandler() throws Exception {
|
||||||
|
|
||||||
enableUseExistingConflictHandler();
|
enableUseExistingConflictHandler();
|
||||||
|
|
||||||
|
@ -234,11 +240,11 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
assertEquals(originalDataType, newDataTypeNode.getDataType());
|
assertEquals(originalDataType, newDataTypeNode.getDataType());
|
||||||
|
|
||||||
structureNode = (DataTypeNode) category3Node.getChild(structName);
|
structureNode = (DataTypeNode) category3Node.getChild(structName);
|
||||||
assertTrue(!originalDataType.isEquivalent(structureNode.getDataType()));
|
assertFalse(originalDataType.isEquivalent(structureNode.getDataType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConflictPasteMoveRename() throws Exception {
|
public void testCutPasteToCategory_RenameConflictHandler() throws Exception {
|
||||||
|
|
||||||
enableRenameConflictHandler();
|
enableRenameConflictHandler();
|
||||||
|
|
||||||
|
@ -256,7 +262,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConflictDragMoveRename() throws Exception {
|
public void testDragMoveToCategory_RenameConflictHandler() throws Exception {
|
||||||
|
|
||||||
enableRenameConflictHandler();
|
enableRenameConflictHandler();
|
||||||
|
|
||||||
|
@ -269,7 +275,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
expandNode(miscNode);
|
expandNode(miscNode);
|
||||||
|
|
||||||
// move/drag ArrayStruct to MISC
|
// move/drag ArrayStruct to MISC
|
||||||
dragNodeToNode(structureNode, miscNode);
|
moveDragNodeToNode(structureNode, miscNode);
|
||||||
|
|
||||||
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName + DataType.CONFLICT_SUFFIX);
|
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName + DataType.CONFLICT_SUFFIX);
|
||||||
assertNotNull(node);
|
assertNotNull(node);
|
||||||
|
@ -277,7 +283,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConflictDragCopyRename() throws Exception {
|
public void testDragCopyToCategory_RenameConflictHandler() throws Exception {
|
||||||
|
|
||||||
enableRenameConflictHandler();
|
enableRenameConflictHandler();
|
||||||
|
|
||||||
|
@ -291,7 +297,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
expandNode(miscNode);
|
expandNode(miscNode);
|
||||||
|
|
||||||
// copy/drag ArrayStruct to MISC
|
// copy/drag ArrayStruct to MISC
|
||||||
copyNodeToNode(structureNode, miscNode);
|
copyDragNodeToNode(structureNode, miscNode);
|
||||||
|
|
||||||
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
|
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
|
||||||
category3Node = (CategoryNode) tree.getViewNode(category3Node);
|
category3Node = (CategoryNode) tree.getViewNode(category3Node);
|
||||||
|
@ -305,7 +311,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConflictDragCopyReplace() throws Exception {
|
public void testDragCopyToCategory_ReplaceExistingConflictHandler() throws Exception {
|
||||||
|
|
||||||
enableReplaceExistingConflictHandler();
|
enableReplaceExistingConflictHandler();
|
||||||
|
|
||||||
|
@ -314,13 +320,13 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
String structName = "ArrayStruct";
|
String structName = "ArrayStruct";
|
||||||
DataTypeNode structureNode = createAndSelectStructure(structName);
|
DataTypeNode structureNode = createAndSelectStructure(structName);
|
||||||
CategoryNode category3Node = (CategoryNode) structureNode.getParent();
|
CategoryNode category3Node = (CategoryNode) structureNode.getParent();
|
||||||
DataType origDt = structureNode.getDataType();
|
DataType originalDt = structureNode.getDataType();
|
||||||
|
|
||||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||||
expandNode(miscNode);
|
expandNode(miscNode);
|
||||||
|
|
||||||
// copy/drag ArrayStruct to MISC
|
// copy/drag ArrayStruct to MISC
|
||||||
copyNodeToNode(structureNode, miscNode);
|
copyDragNodeToNode(structureNode, miscNode);
|
||||||
|
|
||||||
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
|
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
|
||||||
category3Node = (CategoryNode) tree.getViewNode(category3Node);
|
category3Node = (CategoryNode) tree.getViewNode(category3Node);
|
||||||
|
@ -329,11 +335,11 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
||||||
assertNotNull(node);
|
assertNotNull(node);
|
||||||
assertEquals(category3Node, structureNode.getParent());
|
assertEquals(category3Node, structureNode.getParent());
|
||||||
assertTrue(origDt.isEquivalent(structureNode.getDataType()));
|
assertTrue(originalDt.isEquivalent(structureNode.getDataType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConflictDragCopyUseExisting() throws Exception {
|
public void testDragCopyToCategory_UseExistingConflictHandler() throws Exception {
|
||||||
|
|
||||||
enableUseExistingConflictHandler();
|
enableUseExistingConflictHandler();
|
||||||
|
|
||||||
|
@ -341,23 +347,23 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
// in the program, rename Category1/Category2/Category3/IntStruct to ArrayStruct
|
// in the program, rename Category1/Category2/Category3/IntStruct to ArrayStruct
|
||||||
String structName = "ArrayStruct";
|
String structName = "ArrayStruct";
|
||||||
DataTypeNode structureNode = createAndSelectStructure(structName);
|
DataTypeNode structureNode = createAndSelectStructure(structName);
|
||||||
DataType origDt = structureNode.getDataType();
|
DataType originalDt = structureNode.getDataType();
|
||||||
|
|
||||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||||
expandNode(miscNode);
|
expandNode(miscNode);
|
||||||
|
|
||||||
// copy/drag ArrayStruct to MISC
|
// copy/drag ArrayStruct to MISC
|
||||||
copyNodeToNode(structureNode, miscNode);
|
copyDragNodeToNode(structureNode, miscNode);
|
||||||
|
|
||||||
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
||||||
assertNotNull(node);
|
assertNotNull(node);
|
||||||
structureNode = (DataTypeNode) miscNode.getChild(structName);
|
structureNode = (DataTypeNode) miscNode.getChild(structName);
|
||||||
assertNotNull(structureNode);
|
assertNotNull(structureNode);
|
||||||
assertTrue(!origDt.isEquivalent(structureNode.getDataType()));
|
assertFalse(originalDt.isEquivalent(structureNode.getDataType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConflictPasteMoveReplace() throws Exception {
|
public void testCutPasteToCategory_ReplaceExistingConflictHandler() throws Exception {
|
||||||
|
|
||||||
enableReplaceExistingConflictHandler();
|
enableReplaceExistingConflictHandler();
|
||||||
|
|
||||||
|
@ -368,18 +374,18 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
|
|
||||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||||
DataTypeNode node = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
DataTypeNode node = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||||
DataType origDt = node.getDataType();
|
DataType originalDt = node.getDataType();
|
||||||
|
|
||||||
// move ArrayStruct to MISC
|
// move ArrayStruct to MISC
|
||||||
cutPasteSelectedNodeToNode("MISC");
|
cutPasteSelectedNodeToNode("MISC");
|
||||||
|
|
||||||
node = (DataTypeNode) miscNode.getChild(structName);
|
node = (DataTypeNode) miscNode.getChild(structName);
|
||||||
assertNotNull(node);
|
assertNotNull(node);
|
||||||
assertTrue(!origDt.equals(node.getDataType()));
|
assertFalse(originalDt.equals(node.getDataType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConflictPasteMoveUseExisting() throws Exception {
|
public void testCutPasteToCategory_UseExistingConflictHandler() throws Exception {
|
||||||
|
|
||||||
enableUseExistingConflictHandler();
|
enableUseExistingConflictHandler();
|
||||||
|
|
||||||
|
@ -390,18 +396,18 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
|
|
||||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||||
DataTypeNode node = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
DataTypeNode node = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||||
DataType origDt = node.getDataType();
|
DataType originalDt = node.getDataType();
|
||||||
|
|
||||||
cutPasteSelectedNodeToNode("MISC");
|
cutPasteSelectedNodeToNode("MISC");
|
||||||
|
|
||||||
node = (DataTypeNode) miscNode.getChild(structName);
|
node = (DataTypeNode) miscNode.getChild(structName);
|
||||||
assertNotNull(node);
|
assertNotNull(node);
|
||||||
assertTrue(origDt.equals(node.getDataType()));
|
assertTrue(originalDt.equals(node.getDataType()));
|
||||||
assertNull(structureNode.getParent());
|
assertNull(structureNode.getParent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConflictDragMoveReplace() throws Exception {
|
public void testDragMoveToCategory_ReplaceExistingConflictHandler() throws Exception {
|
||||||
|
|
||||||
enableReplaceExistingConflictHandler();
|
enableReplaceExistingConflictHandler();
|
||||||
|
|
||||||
|
@ -414,21 +420,21 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
expandNode(miscNode);
|
expandNode(miscNode);
|
||||||
|
|
||||||
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
||||||
DataType origDt = node.getDataType();
|
DataType originalDt = node.getDataType();
|
||||||
|
|
||||||
// move/drag ArrayStruct to MISC
|
// move/drag ArrayStruct to MISC
|
||||||
dragNodeToNode(structureNode, miscNode);
|
moveDragNodeToNode(structureNode, miscNode);
|
||||||
|
|
||||||
node = (DataTypeNode) miscNode.getChild(structName);
|
node = (DataTypeNode) miscNode.getChild(structName);
|
||||||
assertNotNull(node);
|
assertNotNull(node);
|
||||||
|
|
||||||
assertNull(structureNode.getParent());
|
assertNull(structureNode.getParent());
|
||||||
assertNotNull(node);
|
assertNotNull(node);
|
||||||
assertTrue(!origDt.equals(node.getDataType()));
|
assertFalse(originalDt.equals(node.getDataType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConflictDragMoveUseExisting() throws Exception {
|
public void testDragMoveToCategory_UseExistingConflictHandler() throws Exception {
|
||||||
|
|
||||||
enableUseExistingConflictHandler();
|
enableUseExistingConflictHandler();
|
||||||
|
|
||||||
|
@ -440,21 +446,24 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||||
expandNode(miscNode);
|
expandNode(miscNode);
|
||||||
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
||||||
DataType origDt = node.getDataType();
|
DataType originalDt = node.getDataType();
|
||||||
|
|
||||||
// move/drag ArrayStruct to MISC
|
// move/drag ArrayStruct to MISC
|
||||||
dragNodeToNode(structureNode, miscNode);
|
moveDragNodeToNode(structureNode, miscNode);
|
||||||
|
|
||||||
node = (DataTypeNode) miscNode.getChild(structName);
|
node = (DataTypeNode) miscNode.getChild(structName);
|
||||||
assertNotNull(node);
|
assertNotNull(node);
|
||||||
|
|
||||||
assertNull(structureNode.getParent());
|
assertNull(structureNode.getParent());
|
||||||
assertNotNull(node);
|
assertNotNull(node);
|
||||||
assertTrue(origDt.equals(node.getDataType()));
|
assertTrue(originalDt.equals(node.getDataType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReplaceDataTypeYes() throws Exception {
|
public void testDragMoveToDataType_Replace() throws Exception {
|
||||||
|
|
||||||
|
enableReplaceExistingConflictHandler();
|
||||||
|
|
||||||
String structName = "ArrayStruct";
|
String structName = "ArrayStruct";
|
||||||
DataTypeNode structureNode = createAndSelectStructure(structName);
|
DataTypeNode structureNode = createAndSelectStructure(structName);
|
||||||
Structure structure = (Structure) structureNode.getDataType();
|
Structure structure = (Structure) structureNode.getDataType();
|
||||||
|
@ -465,9 +474,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
|
|
||||||
// drag/move ArrayStruct to MISC/ArrayStruct
|
// drag/move ArrayStruct to MISC/ArrayStruct
|
||||||
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||||
dragNodeToNode(structureNode, miscStructureNode);
|
moveDragNodeToNode(structureNode, miscStructureNode);
|
||||||
|
|
||||||
pressButtonOnOptionDialog("Yes");
|
|
||||||
|
|
||||||
assertNull(structureNode.getParent());
|
assertNull(structureNode.getParent());
|
||||||
assertNull(category3Node.getChild(structName));
|
assertNull(category3Node.getChild(structName));
|
||||||
|
@ -491,122 +498,38 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReplaceDataTypeNo() throws Exception {
|
public void testDragMoveToDataType_SameParent() 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 {
|
|
||||||
// drag/drop a data type onto another data type
|
// drag/drop a data type onto another data type
|
||||||
// get Option dialog, and choose "Yes"
|
|
||||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||||
expandNode(miscNode);
|
expandNode(miscNode);
|
||||||
|
|
||||||
DataTypeNode structureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
DataTypeNode structureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||||
DataTypeNode unionNode = (DataTypeNode) miscNode.getChild("ArrayUnion");
|
DataTypeNode unionNode = (DataTypeNode) miscNode.getChild("ArrayUnion");
|
||||||
|
|
||||||
dragNodeToNode(unionNode, structureNode);
|
setErrorsExpected(true);
|
||||||
|
moveDragNodeToNode(unionNode, structureNode);
|
||||||
|
setErrorsExpected(false);
|
||||||
|
|
||||||
pressButtonOnOptionDialog("Yes");
|
// can't move a data type into its same category
|
||||||
|
waitForWindow("Encountered Errors Copying/Moving");
|
||||||
assertNotNull(miscNode.getChild("ArrayUnion"));
|
|
||||||
assertNull(miscNode.getChild("ArrayStruct"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReplaceDTSameParentNo() throws Exception {
|
public void testDragCopyToDataType() 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");
|
enableRenameConflictHandler();
|
||||||
DataTypeNode unionNode = (DataTypeNode) miscNode.getChild("ArrayUnion");
|
|
||||||
|
|
||||||
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";
|
String structName = "ArrayStruct";
|
||||||
DataTypeNode structureNode = createAndSelectStructure(structName);
|
DataTypeNode structureNode = createAndSelectStructure(structName);
|
||||||
CategoryNode category3Node = (CategoryNode) structureNode.getParent();
|
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
|
// drag/move ArrayStruct to MISC/ArrayStruct
|
||||||
String miscName = "MISC";
|
String miscName = "MISC";
|
||||||
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
|
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
|
||||||
expandNode(miscNode);
|
expandNode(miscNode);
|
||||||
|
|
||||||
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||||
DataType miscStructure = miscStructureNode.getDataType();
|
copyDragNodeToNode(structureNode, miscStructureNode);
|
||||||
|
|
||||||
copyNodeToNode(structureNode, miscStructureNode);
|
|
||||||
|
|
||||||
pressButtonOnOptionDialog("Yes");
|
|
||||||
|
|
||||||
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
|
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
|
||||||
assertNotNull(structureNode.getParent());
|
assertNotNull(structureNode.getParent());
|
||||||
|
@ -615,26 +538,251 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
assertNotNull(category3Node.getChild(structName));
|
assertNotNull(category3Node.getChild(structName));
|
||||||
|
|
||||||
miscNode = (CategoryNode) tree.getViewNode(miscNode);
|
miscNode = (CategoryNode) tree.getViewNode(miscNode);
|
||||||
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
DataTypeNode newNode =
|
||||||
assertTrue(structure.isEquivalent(node.getDataType()));
|
(DataTypeNode) miscNode.getChild(structName + DataType.CONFLICT_SUFFIX);
|
||||||
|
assertNotNull(newNode);
|
||||||
|
assertNotNull(structureNode.getParent());
|
||||||
|
}
|
||||||
|
|
||||||
undo();
|
@Test
|
||||||
|
public void testCopyPasteToCategory() {
|
||||||
|
|
||||||
miscNode = (CategoryNode) programNode.getChild(miscName);
|
String miscName = "MISC";
|
||||||
node = (DataTypeNode) miscNode.getChild(structName);
|
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
|
||||||
assertTrue(miscStructure.isEquivalent(node.getDataType()));
|
expandNode(miscNode);
|
||||||
|
|
||||||
redo();
|
String dtName = "ArrayStruct";
|
||||||
|
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName);
|
||||||
|
selectNode(miscStructureNode);
|
||||||
|
|
||||||
miscNode = (CategoryNode) programNode.getChild(miscName);
|
DockingActionIf copyAction = getAction(plugin, "Copy");
|
||||||
node = (DataTypeNode) miscNode.getChild(structName);
|
assertTrue(copyAction.isEnabledForContext(treeContext));
|
||||||
assertTrue(structure.isEquivalent(node.getDataType()));
|
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>
|
* 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");
|
DataTypeNode structureNode = (DataTypeNode) category3Node.getChild("IntStruct");
|
||||||
Structure structure = (Structure) structureNode.getDataType();
|
Structure structure = (Structure) structureNode.getDataType();
|
||||||
|
|
||||||
int transactionID = program.startTransaction("test");
|
tx(program, () -> structure.setName(structureName));
|
||||||
structure.setName(structureName);
|
|
||||||
program.endTransaction(transactionID, true);
|
|
||||||
waitForProgram();
|
waitForProgram();
|
||||||
|
|
||||||
structureNode = (DataTypeNode) category3Node.getChild(structureName);
|
structureNode = (DataTypeNode) category3Node.getChild(structureName);
|
||||||
|
@ -659,9 +805,6 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
return structureNode;
|
return structureNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies the currently selected node to the node by the given name.
|
|
||||||
*/
|
|
||||||
private CategoryNode copyPasteSelectedNodeToNode(String toNodeName) throws Exception {
|
private CategoryNode copyPasteSelectedNodeToNode(String toNodeName) throws Exception {
|
||||||
DockingActionIf copyAction = getAction(plugin, "Copy");
|
DockingActionIf copyAction = getAction(plugin, "Copy");
|
||||||
assertTrue(copyAction.isEnabled());
|
assertTrue(copyAction.isEnabled());
|
||||||
|
@ -671,10 +814,8 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
expandNode(miscNode);
|
expandNode(miscNode);
|
||||||
selectNode(miscNode);
|
selectNode(miscNode);
|
||||||
|
|
||||||
executeOnSwingWithoutBlocking(() -> {
|
runSwing(() -> DataTypeTestUtils.performAction(pasteAction, tree), false);
|
||||||
DockingActionIf pasteAction = getAction(plugin, "Paste");
|
waitForTasks();
|
||||||
DataTypeTestUtils.performAction(pasteAction, tree);
|
|
||||||
});
|
|
||||||
return miscNode;
|
return miscNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,40 +827,30 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
expandNode(miscNode);
|
expandNode(miscNode);
|
||||||
selectNode(miscNode);
|
selectNode(miscNode);
|
||||||
|
|
||||||
executeOnSwingWithoutBlocking(() -> {
|
runSwing(() -> DataTypeTestUtils.performAction(pasteAction, tree), false);
|
||||||
DockingActionIf pasteAction = getAction(plugin, "Paste");
|
waitForTasks();
|
||||||
DataTypeTestUtils.performAction(pasteAction, tree);
|
|
||||||
});
|
|
||||||
return miscNode;
|
return miscNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pressButtonOnOptionDialog(String buttonName) throws Exception {
|
private void moveDragNodeToNode(GTreeNode fromNode, final GTreeNode toNode) {
|
||||||
OptionDialog d = waitForDialogComponent(OptionDialog.class);
|
|
||||||
JButton button = findButtonByText(d, buttonName);
|
|
||||||
assertNotNull(button);
|
|
||||||
pressButton(button);
|
|
||||||
waitForProgram();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dragNodeToNode(GTreeNode fromNode, final GTreeNode toNode) {
|
|
||||||
final GTreeDragNDropHandler dragNDropHandler = tree.getDragNDropHandler();
|
final GTreeDragNDropHandler dragNDropHandler = tree.getDragNDropHandler();
|
||||||
List<GTreeNode> dropList = new ArrayList<>();
|
List<GTreeNode> dropList = new ArrayList<>();
|
||||||
dropList.add(fromNode);
|
dropList.add(fromNode);
|
||||||
final Transferable transferable = new GTreeNodeTransferable(dragNDropHandler, dropList);
|
final Transferable transferable = new GTreeNodeTransferable(dragNDropHandler, dropList);
|
||||||
|
|
||||||
runSwing(
|
runSwing(() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_MOVE),
|
||||||
() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_MOVE), false);
|
false);
|
||||||
waitForSwing();
|
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();
|
final GTreeDragNDropHandler dragNDropHandler = tree.getDragNDropHandler();
|
||||||
List<GTreeNode> dropList = new ArrayList<>();
|
List<GTreeNode> dropList = new ArrayList<>();
|
||||||
dropList.add(fromNode);
|
dropList.add(fromNode);
|
||||||
final Transferable transferable = new GTreeNodeTransferable(dragNDropHandler, dropList);
|
Transferable transferable = new GTreeNodeTransferable(dragNDropHandler, dropList);
|
||||||
|
runSwing(() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_COPY),
|
||||||
runSwing(
|
false);
|
||||||
() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_COPY), false);
|
waitForTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
@ -735,6 +866,11 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
||||||
waitForTree();
|
waitForTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void selectNodes(GTreeNode... nodes) {
|
||||||
|
tree.setSelectedNodes(nodes);
|
||||||
|
waitForTree();
|
||||||
|
}
|
||||||
|
|
||||||
private void waitForTree() {
|
private void waitForTree() {
|
||||||
waitForTree(tree);
|
waitForTree(tree);
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,9 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
treeContext = new DataTypesActionContext(provider, program, tree, null);
|
treeContext = new DataTypesActionContext(provider, program, tree, null);
|
||||||
|
|
||||||
removeDistractingPlugins();
|
removeDistractingPlugins();
|
||||||
|
|
||||||
|
cutAction = getAction(plugin, "Copy");
|
||||||
|
pasteAction = getAction(plugin, "Paste");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeDistractingPlugins() {
|
private void removeDistractingPlugins() {
|
||||||
|
@ -589,8 +592,6 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
selectNode(myStructNode);
|
selectNode(myStructNode);
|
||||||
|
|
||||||
DockingActionIf copyAction = getAction(plugin, "Copy");
|
DockingActionIf copyAction = getAction(plugin, "Copy");
|
||||||
cutAction = getAction(plugin, "Cut");
|
|
||||||
pasteAction = getAction(plugin, "Paste");
|
|
||||||
|
|
||||||
assertTrue(cutAction.isEnabledForContext(treeContext));
|
assertTrue(cutAction.isEnabledForContext(treeContext));
|
||||||
assertTrue(copyAction.isEnabledForContext(treeContext));
|
assertTrue(copyAction.isEnabledForContext(treeContext));
|
||||||
|
@ -616,8 +617,6 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
||||||
selectNode(cat2Node);
|
selectNode(cat2Node);
|
||||||
|
|
||||||
DockingActionIf copyAction = getAction(plugin, "Copy");
|
DockingActionIf copyAction = getAction(plugin, "Copy");
|
||||||
cutAction = getAction(plugin, "Cut");
|
|
||||||
pasteAction = getAction(plugin, "Paste");
|
|
||||||
|
|
||||||
assertTrue(cutAction.isEnabledForContext(treeContext));
|
assertTrue(cutAction.isEnabledForContext(treeContext));
|
||||||
assertTrue(copyAction.isEnabledForContext(treeContext));
|
assertTrue(copyAction.isEnabledForContext(treeContext));
|
||||||
|
|
|
@ -25,28 +25,31 @@ import ghidra.util.task.TaskMonitor;
|
||||||
* Each data type resides in a given a category.
|
* Each data type resides in a given a category.
|
||||||
*/
|
*/
|
||||||
public interface Category extends Comparable<Category> {
|
public interface Category extends Comparable<Category> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of this category.
|
* Get the name of this category.
|
||||||
|
* @return the name.
|
||||||
*/
|
*/
|
||||||
public abstract String getName();
|
public abstract String getName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the name of this category.
|
* Sets the name of this category.
|
||||||
* @param name the new name for 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.
|
* @throws InvalidNameException if the name is not an acceptable name.
|
||||||
*/
|
*/
|
||||||
public abstract void setName(String name) throws DuplicateNameException, InvalidNameException;
|
public abstract void setName(String name) throws DuplicateNameException, InvalidNameException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all categories in this category.
|
* 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();
|
public abstract Category[] getCategories();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all data types in this category.
|
* 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();
|
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
|
* 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
|
* any conflict suffixes have been removed from both the given name and the data types
|
||||||
* that are being scanned.
|
* that are being scanned.
|
||||||
* @param name the name for which to get conflict related data types in this category. Note: the
|
* @param name the name for which to get conflict related data types in this category. Note:
|
||||||
* name that is passed in will be normalized to its base name, so you may pass in names with .conflict
|
* the name that is passed in will be normalized to its base name, so you may pass in names
|
||||||
* appended as a convenience.
|
* 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
|
* @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);
|
public abstract List<DataType> getDataTypesByBaseName(String name);
|
||||||
|
|
||||||
|
@ -73,8 +77,8 @@ public interface Category extends Comparable<Category> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a category with the given name.
|
* Get a category with the given name.
|
||||||
* @param name the name of the category
|
* @param name the name of the category.
|
||||||
* @return null if there is no category by this name
|
* @return null if there is no category by this name.
|
||||||
*/
|
*/
|
||||||
public abstract Category getCategory(String 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.
|
* Get a data type with the given name.
|
||||||
* @param name the name of the data type
|
* @param name the name of the data type.
|
||||||
* @return null if there is no data type by this name
|
* @return null if there is no data type by this name.
|
||||||
*/
|
*/
|
||||||
public abstract DataType getDataType(String name);
|
public abstract DataType getDataType(String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a category with the given name; if category already exists, then
|
* Create a category with the given name; if category already exists, then return that
|
||||||
* return that category.
|
* category.
|
||||||
* @param name the category name
|
* @param name the category name.
|
||||||
* @throws InvalidNameException if name has invalid characters
|
* @return the category.
|
||||||
|
* @throws InvalidNameException if name has invalid characters.
|
||||||
*/
|
*/
|
||||||
public abstract Category createCategory(String name) throws InvalidNameException;
|
public abstract Category createCategory(String name) throws InvalidNameException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the named category from this category.
|
* Remove the named category from this category.
|
||||||
* @param name the name of the category to remove
|
* @param name the name of the category to remove.
|
||||||
* @param monitor the task monitor
|
* @param monitor the task monitor.
|
||||||
* @return true if the category was removed
|
* @return true if the category was removed.
|
||||||
*/
|
*/
|
||||||
public abstract boolean removeCategory(String name, TaskMonitor monitor);
|
public abstract boolean removeCategory(String name, TaskMonitor monitor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the named category from this category, IFF it is empty.
|
* Remove the named category from this category, IFF it is empty.
|
||||||
* @param name the name of the category to remove
|
* @param name the name of the category to remove.
|
||||||
* @param monitor the task monitor
|
* @param monitor the task monitor.
|
||||||
* @return true if the category was removed
|
* @return true if the category was removed.
|
||||||
*/
|
*/
|
||||||
public abstract boolean removeEmptyCategory(String name, TaskMonitor monitor);
|
public abstract boolean removeEmptyCategory(String name, TaskMonitor monitor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move the given category to this category; category is removed from
|
* Move the given category to this category; category is removed from its original parent
|
||||||
* its original parent category.
|
* category.
|
||||||
* @param category the category to move
|
* @param category the category to move.
|
||||||
|
* @param monitor the monitor.
|
||||||
* @throws DuplicateNameException if this category already contains a
|
* @throws DuplicateNameException if this category already contains a
|
||||||
* category or data type with the same name as the category param.
|
* category or data type with the same name as the category param.
|
||||||
*/
|
*/
|
||||||
|
@ -126,15 +132,18 @@ public interface Category extends Comparable<Category> {
|
||||||
throws DuplicateNameException;
|
throws DuplicateNameException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a new subcategory from the given category.
|
* Make a new sub-category from the given category.
|
||||||
* @param category the category to copy into this category
|
* @param category the category to copy into this category.
|
||||||
* @return category that is added to 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,
|
public abstract Category copyCategory(Category category, DataTypeConflictHandler handler,
|
||||||
TaskMonitor monitor);
|
TaskMonitor monitor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return this category's parent; return null if this is the root category.
|
* Return this category's parent; return null if this is the root category.
|
||||||
|
* @return the category.
|
||||||
*/
|
*/
|
||||||
public abstract Category getParent();
|
public abstract Category getParent();
|
||||||
|
|
||||||
|
@ -146,33 +155,36 @@ public interface Category extends Comparable<Category> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the fully qualified name for this category.
|
* Get the fully qualified name for this category.
|
||||||
|
* @return the name.
|
||||||
*/
|
*/
|
||||||
public abstract String getCategoryPathName();
|
public abstract String getCategoryPathName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the root category.
|
* Get the root category.
|
||||||
|
* @return the category.
|
||||||
*/
|
*/
|
||||||
public abstract Category getRoot();
|
public abstract Category getRoot();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the data type manager associated with this category.
|
* Get the data type manager associated with this category.
|
||||||
|
* @return the manager.
|
||||||
*/
|
*/
|
||||||
public abstract DataTypeManager getDataTypeManager();
|
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 type data type to be moved.
|
||||||
* @param handler the handler to call if there is a data type conflict
|
* @param handler the handler to call if there is a data type conflict.
|
||||||
* @throws DataTypeDependencyException
|
* @throws DataTypeDependencyException if a disallowed dependency is created during the move.
|
||||||
*/
|
*/
|
||||||
public abstract void moveDataType(DataType type, DataTypeConflictHandler handler)
|
public abstract void moveDataType(DataType type, DataTypeConflictHandler handler)
|
||||||
throws DataTypeDependencyException;
|
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.
|
* @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.
|
* @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.
|
* Get the ID for this category.
|
||||||
|
* @return the ID.
|
||||||
*/
|
*/
|
||||||
public long getID();
|
public long getID();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue