mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 02:39:44 +02:00
Merge remote-tracking branch
'origin/GP-1627-dragonmacher-dt-copy-paste--SQUASHED' (Closes #3568)
This commit is contained in:
commit
ece6e9ecd5
13 changed files with 687 additions and 501 deletions
|
@ -18,13 +18,6 @@
|
|||
program. Allowing the user to build libraries of data types and to share them between programs,
|
||||
projects, and different users is a long term goal for Ghidra.</P>
|
||||
|
||||
<P>Prior to Ghidra 4.3, sharing data types even between programs in the same project was very
|
||||
difficult. (users would have to drag data types, one at a time, from one program or archive to
|
||||
another to propagate changes to data types). As of Ghidra 4.3, significant progress has been
|
||||
made to make sharing data types easier, but the inherent complexity of this problem requires
|
||||
users to understand a number of basic concepts to take advantage of the new features.<BR>
|
||||
</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
||||
<H2>Topics</H2>
|
||||
|
@ -1008,7 +1001,7 @@
|
|||
<LI><I>Rename</I> the data type that you are dragging to have ".conflict" appended to
|
||||
it to make a unique name.</LI>
|
||||
|
||||
<LI><I>Replace</I> the existing data type with the one you are dragging (or, pasting);
|
||||
<LI><I>Replace</I> the existing data type with the new one;
|
||||
this means any use of the existing data types is <B><I>replaced</I></B> with the new
|
||||
data type; the existing data type is deleted.</LI>
|
||||
|
||||
|
@ -1023,30 +1016,9 @@
|
|||
<BLOCKQUOTE>
|
||||
<P>A data type can be replaced by another data type. This means that every occurrence of
|
||||
the original data type in a program is replaced by the new data type and the original
|
||||
data type is deleted. There are two ways to replace a data type.</P>
|
||||
data type is deleted. To replace a data type, right-click on the type to be replaced
|
||||
and select the <B><I>Replace Data Type...</I></B> action.</P>
|
||||
|
||||
<TABLE style="width: 100%; text-align: left;" border="0" cellpadding="2" cellspacing="2">
|
||||
<TBODY>
|
||||
<TR valign="top">
|
||||
<TD style="vertical-align: top; width: 130px;">1. <B>Drag-N-Drop</B></TD>
|
||||
|
||||
<TD style="vertical-align: top;">Click on the replacement data type and drag it
|
||||
onto the data type to be replaced.</TD>
|
||||
</TR>
|
||||
|
||||
<TR valign="top">
|
||||
<TD style="vertical-align: top; width: 130px;">2. <B>Copy/Paste</B></TD>
|
||||
|
||||
<TD style="vertical-align: top;">Right-click on the replacement data type to be
|
||||
moved and select the <I><B>Cut</B></I> action. Next, right-click on the data type
|
||||
to be replaced and select the <I><B>Paste</B></I> action.</TD>
|
||||
</TR>
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Either way, a confirmation dialog will appear.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="Favorites"></A>Setting Favorite Data Types</H3>
|
||||
|
|
|
@ -524,6 +524,21 @@
|
|||
<H2>Miscellaneous Actions</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
||||
<H3><A name="Copy"></A>Copy</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The <I><B>Copy</B></I> action can be be used to
|
||||
<A href="data_type_manager_description.htm#CopyDataType">copy
|
||||
selected data types</A> and/or
|
||||
<A href="data_type_manager_description.htm#CopyCategory">copy selected categories</A>.
|
||||
The <I><B>Copy</B></I> action only primes the selected nodes to be copied. The
|
||||
<I><B>Paste</B></I> action must be used to complete the copy. Any other
|
||||
<I><B>Cut</B></I> or <I><B>Copy</B></I> action will cancel the previous<I><B>Copy
|
||||
.</B></I><BR>
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="Cut"></A>Cut</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
@ -538,27 +553,31 @@
|
|||
"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="Delete"></A>Delete</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The <I><B>Copy</B></I> action can be be used to
|
||||
<A href="data_type_manager_description.htm#CopyDataType">copy
|
||||
selected data types</A> and/or
|
||||
<A href="data_type_manager_description.htm#CopyCategory">copy selected categories</A>.
|
||||
The <I><B>Copy</B></I> action only primes the selected nodes to be copied. The
|
||||
<I><B>Paste</B></I> action must be used to complete the copy. Any other
|
||||
<I><B>Cut</B></I> or <I><B>Copy</B></I> action will cancel the previous<I><B>Copy
|
||||
.</B></I><BR>
|
||||
</P>
|
||||
<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>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The<I><B>Paste</B></I> action is used to complete a move or copy operation as
|
||||
initiated by either the <I><B>Copy</B></I> or <I><B>Cut</B></I> action. The node that
|
||||
is selected will be the destination for whatever nodes where selected when the
|
||||
<I><B>Copy</B></I> or <I><B>Paste</B></I> was invoked.</P>
|
||||
<P>The <I><B>Paste</B></I> action is used to complete a move or copy operation as
|
||||
initiated by either the <I><B>Copy</B></I> or <I><B>Cut</B></I> action. The category
|
||||
node (or the parent category of a data type node) that is selected will be the
|
||||
destination for whatever nodes where selected when the <I><B>Copy</B></I> or
|
||||
<I><B>Paste</B></I> was invoked.
|
||||
</P>
|
||||
|
||||
<P>Any conflicts encountered while pasting will be resolved according to the current
|
||||
<A HREF="#conflict_mode">conflict resolution mode</A>.
|
||||
</P>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="Rename"></A>Rename</H3>
|
||||
|
@ -571,26 +590,29 @@
|
|||
type</A>.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="Delete"></A>Delete</H3>
|
||||
|
||||
<H3><A name="Replace_Data_Type"></A>Replace Data Type...</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>
|
||||
<P>The <I><B>Replace Data Type...</B></I> action is used to
|
||||
<A href="data_type_manager_description.htm#ReplaceDataType">replace</A> a
|
||||
selected data type and all occurrences in the program.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A name="Collapse_All"></A>Collapse All</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
||||
<H3><A name="Collapse_All"></A>Collapse All</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<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
|
||||
via the local toolbar, then the entire tree is collapsed.</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<H3><A name="Expand_All"></A>Expand All</H3>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
|
|
@ -148,6 +148,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
|||
addLocalAction(new CutAction(plugin));
|
||||
addLocalAction(new CopyAction(plugin));
|
||||
addLocalAction(new PasteAction(plugin));
|
||||
addLocalAction(new ReplaceDataTypeAction(plugin));
|
||||
addLocalAction(new DeleteAction(plugin));
|
||||
addLocalAction(new DeleteArchiveAction(plugin));
|
||||
addLocalAction(new RenameAction(plugin));
|
||||
|
@ -181,8 +182,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
|||
addLocalAction(new FindDataTypesBySizeAction(plugin, "2"));
|
||||
addLocalAction(new FindStructuresByOffsetAction(plugin, "3"));
|
||||
addLocalAction(new FindStructuresBySizeAction(plugin, "4"));
|
||||
includeDataMembersInSearchAction =
|
||||
new IncludeDataTypesInFilterAction(plugin, this, "5");
|
||||
includeDataMembersInSearchAction = new IncludeDataTypesInFilterAction(plugin, this, "5");
|
||||
addLocalAction(includeDataMembersInSearchAction);
|
||||
|
||||
addLocalAction(new ApplyFunctionDataTypesAction(plugin)); // Tree
|
||||
|
|
|
@ -48,9 +48,9 @@ public class PasteAction extends DockingAction {
|
|||
this.clipboard = plugin.getClipboard();
|
||||
this.tool = plugin.getTool();
|
||||
setPopupMenuData(new MenuData(new String[] { "Paste" }, "Edit"));
|
||||
setKeyBindingData(new KeyBindingData(KeyStroke.getKeyStroke(KeyEvent.VK_V,
|
||||
InputEvent.CTRL_DOWN_MASK), KeyBindingPrecedence.ActionMapLevel));
|
||||
setEnabled(true);
|
||||
setKeyBindingData(
|
||||
new KeyBindingData(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK),
|
||||
KeyBindingPrecedence.ActionMapLevel));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,7 +65,7 @@ public class PasteAction extends DockingAction {
|
|||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
DataTypeTreeNode node = getSelectedDataTypeTreeNode(context);
|
||||
if (!(node instanceof CategoryNode) || !((CategoryNode) node).isEnabled()) {
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
List<GTreeNode> transferNodeList = getNodesFromClipboard();
|
||||
|
@ -96,7 +96,7 @@ public class PasteAction extends DockingAction {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (invalidCutNodes(destinationNode, transferNodeList)) {
|
||||
if (hasInvalidCutNodes(destinationNode, transferNodeList)) {
|
||||
return false; // cut nodes that cannot be pasted here
|
||||
}
|
||||
|
||||
|
@ -109,13 +109,14 @@ public class PasteAction extends DockingAction {
|
|||
}
|
||||
|
||||
DataFlavor[] flavors = handler.getSupportedDataFlavors(transferNodeList);
|
||||
return handler.isDropSiteOk(destinationNode, flavors, DnDConstants.ACTION_COPY);
|
||||
return handler.isValidDataTypeDestination(destinationNode, flavors,
|
||||
DnDConstants.ACTION_COPY);
|
||||
}
|
||||
|
||||
private boolean invalidCutNodes(DataTypeTreeNode destinationNode, List<GTreeNode> nodeList) {
|
||||
private boolean hasInvalidCutNodes(DataTypeTreeNode destinationNode, List<GTreeNode> nodeList) {
|
||||
DataTypeTreeNode node = (DataTypeTreeNode) nodeList.get(0);
|
||||
if (!node.isCut()) {
|
||||
return false; // hasn't been cut, no problemo
|
||||
return false; // hasn't been cut, no problem
|
||||
}
|
||||
|
||||
// can't cut nodes from one archive and paste into another
|
||||
|
@ -142,28 +143,37 @@ public class PasteAction extends DockingAction {
|
|||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTree gTree = (GTree) context.getContextObject();
|
||||
|
||||
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
||||
GTreeNode destinationNode = (GTreeNode) selectionPaths[0].getLastPathComponent();
|
||||
|
||||
List<GTreeNode> nodeList = getNodesFromClipboard();
|
||||
if (nodeList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GTree gTree = (GTree) context.getContextObject();
|
||||
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
||||
CategoryNode destinationNode = getDropTargetNode(selectionPaths);
|
||||
DataTypeTreeNode dataTypeTreeNode = (DataTypeTreeNode) nodeList.get(0);
|
||||
if (dataTypeTreeNode.isCut()) { // clear cut nodes on paste operation
|
||||
clipboard.setContents(null, null);
|
||||
}
|
||||
|
||||
ActionType actionType = getActionType(dataTypeTreeNode);
|
||||
DataTypeTreeCopyMoveTask task =
|
||||
new DataTypeTreeCopyMoveTask(destinationNode, nodeList, actionType,
|
||||
(DataTypeArchiveGTree) gTree,
|
||||
plugin.getConflictHandler());
|
||||
DataTypeTreeCopyMoveTask task = new DataTypeTreeCopyMoveTask(destinationNode, nodeList,
|
||||
actionType, (DataTypeArchiveGTree) gTree, plugin.getConflictHandler());
|
||||
tool.execute(task, 250);
|
||||
}
|
||||
|
||||
private CategoryNode getDropTargetNode(TreePath[] selectionPaths) {
|
||||
|
||||
// clients can drop/paste onto a category or archive
|
||||
GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent();
|
||||
if (node instanceof CategoryNode) {
|
||||
return (CategoryNode) node;
|
||||
}
|
||||
|
||||
return (CategoryNode) node.getParent();
|
||||
}
|
||||
|
||||
private ActionType getActionType(DataTypeTreeNode pasteNode) {
|
||||
if (pasteNode.isCut()) {
|
||||
return ActionType.MOVE;
|
||||
|
|
|
@ -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(),
|
||||
existingDataTypeManager.getName());
|
||||
|
||||
int existingTxID = existingDataTypeManager.startTransaction(
|
||||
"Update Data Type Source Archive Name");
|
||||
int existingTxID = existingDataTypeManager
|
||||
.startTransaction("Update Data Type Source Archive Name");
|
||||
try {
|
||||
existingDataTypeManager.updateSourceArchiveName(
|
||||
dataTypeManager.getUniversalID(), dataTypeManager.getName());
|
||||
|
@ -686,7 +686,7 @@ public class DataTypeManagerHandler {
|
|||
Msg.info(this, "Closed archive: '" + archive.getName() + "'");
|
||||
}
|
||||
|
||||
private Archive getArchive(DataTypeManager dtm) {
|
||||
public Archive getArchive(DataTypeManager dtm) {
|
||||
for (Archive archive : openArchives) {
|
||||
DataTypeManager dataTypeManager = archive.getDataTypeManager();
|
||||
if (dataTypeManager.equals(dtm)) {
|
||||
|
@ -814,43 +814,43 @@ public class DataTypeManagerHandler {
|
|||
|
||||
private void initializeFavorites() {
|
||||
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(PointerDataType.dataType, null), true);
|
||||
builtInDataTypesManager
|
||||
.setFavorite(builtInDataTypesManager.resolve(PointerDataType.dataType, null), true);
|
||||
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(CharDataType.dataType, null), true);
|
||||
builtInDataTypesManager
|
||||
.setFavorite(builtInDataTypesManager.resolve(CharDataType.dataType, null), true);
|
||||
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(StringDataType.dataType, null), true);
|
||||
builtInDataTypesManager
|
||||
.setFavorite(builtInDataTypesManager.resolve(StringDataType.dataType, null), true);
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(TerminatedStringDataType.dataType, null), true);
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(TerminatedUnicodeDataType.dataType, null), true);
|
||||
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(FloatDataType.dataType, null), true);
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(DoubleDataType.dataType, null), true);
|
||||
builtInDataTypesManager
|
||||
.setFavorite(builtInDataTypesManager.resolve(FloatDataType.dataType, null), true);
|
||||
builtInDataTypesManager
|
||||
.setFavorite(builtInDataTypesManager.resolve(DoubleDataType.dataType, null), true);
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(LongDoubleDataType.dataType, null), true);
|
||||
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(IntegerDataType.dataType, null), true);
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(LongDataType.dataType, null), true);
|
||||
builtInDataTypesManager
|
||||
.setFavorite(builtInDataTypesManager.resolve(IntegerDataType.dataType, null), true);
|
||||
builtInDataTypesManager
|
||||
.setFavorite(builtInDataTypesManager.resolve(LongDataType.dataType, null), true);
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(UnsignedIntegerDataType.dataType, null), true);
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(UnsignedLongDataType.dataType, null), true);
|
||||
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(ByteDataType.dataType, null), true);
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(WordDataType.dataType, null), true);
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(DWordDataType.dataType, null), true);
|
||||
builtInDataTypesManager.setFavorite(
|
||||
builtInDataTypesManager.resolve(QWordDataType.dataType, null), true);
|
||||
builtInDataTypesManager
|
||||
.setFavorite(builtInDataTypesManager.resolve(ByteDataType.dataType, null), true);
|
||||
builtInDataTypesManager
|
||||
.setFavorite(builtInDataTypesManager.resolve(WordDataType.dataType, null), true);
|
||||
builtInDataTypesManager
|
||||
.setFavorite(builtInDataTypesManager.resolve(DWordDataType.dataType, null), true);
|
||||
builtInDataTypesManager
|
||||
.setFavorite(builtInDataTypesManager.resolve(QWordDataType.dataType, null), true);
|
||||
|
||||
}
|
||||
|
||||
|
@ -900,7 +900,7 @@ public class DataTypeManagerHandler {
|
|||
}
|
||||
saveState.putStrings(RECENT_NAMES, getSaveableArchiveNames(recentMenuList));
|
||||
|
||||
// update the initialArchives list so that future checks on that list do not trigger a
|
||||
// update the initialArchives list so that future checks on that list do not trigger a
|
||||
// state change
|
||||
initiallyOpenedFileArchiveNames = getOpenFileArchiveNames(openArchives);
|
||||
}
|
||||
|
@ -957,11 +957,11 @@ public class DataTypeManagerHandler {
|
|||
|
||||
/**
|
||||
* Determine if we can remember the specified project archive using a simple project path
|
||||
* (e.g., we can't remember specific versions).
|
||||
* (e.g., we can't remember specific versions).
|
||||
* @param pa project archive
|
||||
* @param activeProjectOnly if true pa must be contained within the
|
||||
* @param activeProjectOnly if true pa must be contained within the
|
||||
* active project to be remembered.
|
||||
* @return return project path which can be remembered or null
|
||||
* @return return project path which can be remembered or null
|
||||
*/
|
||||
public String getProjectPathname(ProjectArchive pa, boolean activeProjectOnly) {
|
||||
// Project archives are always opened by a user.
|
||||
|
@ -1227,7 +1227,7 @@ public class DataTypeManagerHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Signals to this manager to save the knowledge of all currently opened archives and to mark
|
||||
* Signals to this manager to save the knowledge of all currently opened archives and to mark
|
||||
* the tool as dirty (changed) if the current open archives are not the same as those that
|
||||
* were initially opened.
|
||||
*/
|
||||
|
@ -1428,8 +1428,8 @@ public class DataTypeManagerHandler {
|
|||
new DataTreeDialog(null, "Save As", DataTreeDialog.SAVE, domainFileFilter);
|
||||
|
||||
dataTreeSaveDialog.addOkActionListener(listener);
|
||||
dataTreeSaveDialog.setHelpLocation(
|
||||
new HelpLocation(HelpTopics.PROGRAM, "Save_As_File"));
|
||||
dataTreeSaveDialog
|
||||
.setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Save_As_File"));
|
||||
}
|
||||
return dataTreeSaveDialog;
|
||||
}
|
||||
|
@ -1614,8 +1614,8 @@ public class DataTypeManagerHandler {
|
|||
|
||||
@Override
|
||||
public void domainFileStatusChanged(DomainFile file, boolean fileIDset) {
|
||||
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE.equals(
|
||||
file.getContentType())) {
|
||||
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE
|
||||
.equals(file.getContentType())) {
|
||||
return;
|
||||
}
|
||||
Iterator<Archive> archiveIter = openArchives.iterator();
|
||||
|
@ -1658,8 +1658,8 @@ public class DataTypeManagerHandler {
|
|||
|
||||
@Override
|
||||
public void domainFileRenamed(DomainFile file, String oldName) {
|
||||
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE.equals(
|
||||
file.getContentType())) {
|
||||
if (!DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE
|
||||
.equals(file.getContentType())) {
|
||||
return;
|
||||
}
|
||||
String newName = file.getName();
|
||||
|
@ -1726,7 +1726,7 @@ public class DataTypeManagerHandler {
|
|||
|
||||
/**
|
||||
* Provides an exception handler for a failed attempt to open an datatype archive file.
|
||||
* This method will display exception information to the user and/or log.
|
||||
* This method will display exception information to the user and/or log.
|
||||
* @param plugin datatype manager plugin
|
||||
* @param archiveFile archive file resource being opened
|
||||
* @param t throwable
|
||||
|
|
|
@ -37,11 +37,11 @@ import ghidra.util.task.Task;
|
|||
public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
|
||||
private static DataFlavor localDataTypeTreeFlavor = createLocalTreeNodeFlavor();
|
||||
|
||||
public static DataFlavor[] allSupportedFlavors = { DataTypeTransferable.localDataTypeFlavor,
|
||||
localDataTypeTreeFlavor };
|
||||
public static DataFlavor[] allSupportedFlavors =
|
||||
{ DataTypeTransferable.localDataTypeFlavor, localDataTypeTreeFlavor };
|
||||
|
||||
public static DataFlavor[] builtinFlavors = { DataTypeTransferable.localBuiltinDataTypeFlavor,
|
||||
localDataTypeTreeFlavor };
|
||||
public static DataFlavor[] builtinFlavors =
|
||||
{ DataTypeTransferable.localBuiltinDataTypeFlavor, localDataTypeTreeFlavor };
|
||||
|
||||
public static DataFlavor[] restrictedFlavors = { localDataTypeTreeFlavor };
|
||||
|
||||
|
@ -52,8 +52,9 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
|
|||
// create a data flavor that is an List of GTreeNodes
|
||||
private static DataFlavor createLocalTreeNodeFlavor() {
|
||||
try {
|
||||
return new GenericDataFlavor(DataFlavor.javaJVMLocalObjectMimeType +
|
||||
"; class=java.util.List", "Local list of Drag/Drop DataType Tree objects");
|
||||
return new GenericDataFlavor(
|
||||
DataFlavor.javaJVMLocalObjectMimeType + "; class=java.util.List",
|
||||
"Local list of Drag/Drop DataType Tree objects");
|
||||
}
|
||||
catch (Exception e) {
|
||||
Msg.showError(DataTypeDragNDropHandler.class, null, null, null, e);
|
||||
|
@ -75,12 +76,12 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
|
|||
if (list.contains(destinationNode)) { // don't allow drop on dragged nodes.
|
||||
return;
|
||||
}
|
||||
|
||||
CategoryNode updatedDestinationNode = getDropTargetNode(destinationNode);
|
||||
ActionType actionType =
|
||||
dropAction == DnDConstants.ACTION_COPY ? ActionType.COPY : ActionType.MOVE;
|
||||
Task task =
|
||||
new DataTypeTreeCopyMoveTask(destinationNode, list, actionType,
|
||||
(DataTypeArchiveGTree) tree,
|
||||
plugin.getConflictHandler());
|
||||
Task task = new DataTypeTreeCopyMoveTask(updatedDestinationNode, list, actionType,
|
||||
(DataTypeArchiveGTree) tree, plugin.getConflictHandler());
|
||||
plugin.getTool().execute(task, 250);
|
||||
}
|
||||
catch (UnsupportedFlavorException e) {
|
||||
|
@ -91,6 +92,16 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private CategoryNode getDropTargetNode(GTreeNode node) {
|
||||
|
||||
// clients can drop/paste onto a category or archive
|
||||
if (node instanceof CategoryNode) {
|
||||
return (CategoryNode) node;
|
||||
}
|
||||
|
||||
return (CategoryNode) node.getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor[] getSupportedDataFlavors(List<GTreeNode> draggedNodes) {
|
||||
// single, datatype node supports both datatype dragging *and* local tree dragging
|
||||
|
@ -150,12 +161,38 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
|
|||
|
||||
@Override
|
||||
public boolean isDropSiteOk(GTreeNode destinationNode, DataFlavor[] flavors, int dropAction) {
|
||||
// can't drop on the root node
|
||||
|
||||
if (!isValidDataTypeDestination(destinationNode, flavors, dropAction)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GTreeNode updatedDestinationNode = getDropTargetNode(destinationNode);
|
||||
|
||||
if (isDroppingBuiltin(flavors)) {
|
||||
if (!isValidBuiltinDropSite(updatedDestinationNode)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the given destination node can accept the given drop/copy/paste action and content
|
||||
* flavors.
|
||||
* @param destinationNode the node accepting the action
|
||||
* @param flavors the supported flavors of the action
|
||||
* @param dropAction the actual action see {@link DnDConstants}
|
||||
* @return true if valid
|
||||
*/
|
||||
public boolean isValidDataTypeDestination(GTreeNode destinationNode, DataFlavor[] flavors,
|
||||
int dropAction) {
|
||||
// can't drop/paste on the root node
|
||||
if (destinationNode == null || destinationNode.getParent() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// can only drop nodes from other dataTypetrees
|
||||
// can only drop/paste nodes from other dataType trees
|
||||
if (!containsFlavor(flavors, localDataTypeTreeFlavor)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -167,20 +204,6 @@ public class DataTypeDragNDropHandler implements GTreeDragNDropHandler {
|
|||
return false;
|
||||
}
|
||||
|
||||
// only a single datatype node can be dropped on a datatype node.
|
||||
if (destinationNode instanceof DataTypeNode) {
|
||||
if (!containsFlavor(flavors, DataTypeTransferable.localDataTypeFlavor) &&
|
||||
!containsFlavor(flavors, DataTypeTransferable.localBuiltinDataTypeFlavor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDroppingBuiltin(flavors)) {
|
||||
if (!isValidBuiltinDropSite(destinationNode)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -204,11 +204,7 @@ public class DataTypeNode extends DataTypeTreeNode {
|
|||
|
||||
@Override
|
||||
public boolean canPaste(List<GTreeNode> pastedNodes) {
|
||||
if (pastedNodes.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
GTreeNode pastedNode = pastedNodes.get(0);
|
||||
return pastedNode instanceof DataTypeNode;
|
||||
return isModifiable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -252,7 +248,7 @@ public class DataTypeNode extends DataTypeTreeNode {
|
|||
|
||||
@Override
|
||||
public String getDisplayText() {
|
||||
// note: we have to check the name each time, as the optional underlying
|
||||
// note: we have to check the name each time, as the optional underlying
|
||||
// source archive may have changed.
|
||||
String currentDisplayText = getCurrentDisplayText();
|
||||
if (!displayText.equals(currentDisplayText)) {
|
||||
|
|
|
@ -34,6 +34,7 @@ public abstract class DataTypeTreeNode extends GTreeLazyNode {
|
|||
|
||||
/**
|
||||
* Returns true if this nodes handles paste operations
|
||||
* @param pastedNodes the nodes to be pasted
|
||||
* @return true if this nodes handles paste operations
|
||||
*/
|
||||
public abstract boolean canPaste(List<GTreeNode> pastedNodes);
|
||||
|
|
|
@ -22,8 +22,6 @@ import java.util.regex.Pattern;
|
|||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.GTreeState;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeSyncInfo;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeSynchronizer;
|
||||
import ghidra.app.plugin.core.datamgr.archive.Archive;
|
||||
import ghidra.app.plugin.core.datamgr.archive.ProgramArchive;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
|
@ -39,7 +37,7 @@ import ghidra.util.task.TaskMonitor;
|
|||
public class DataTypeTreeCopyMoveTask extends Task {
|
||||
|
||||
// If the total number of nodes is small, we won't need to collapse the tree before deleting
|
||||
// the nodes to avoid excess tree events. This number is very arbitrary. This number is
|
||||
// the nodes to avoid excess tree events. This number is very arbitrary. This number is
|
||||
// used to compare the number of dragged nodes, which may include categories whose child
|
||||
// count is not reflected in this number. This could mean that thousands of nodes will be
|
||||
// processed, but the actual drag count could be much less.
|
||||
|
@ -50,7 +48,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
|||
}
|
||||
|
||||
private DataTypeArchiveGTree gTree;
|
||||
private GTreeNode destinationNode;
|
||||
private CategoryNode destinationNode;
|
||||
private List<GTreeNode> droppedNodes;
|
||||
private Archive sourceArchive;
|
||||
private Archive destinationArchive;
|
||||
|
@ -64,7 +62,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
|||
super("Drag/Drop", true, true, true);
|
||||
}
|
||||
|
||||
public DataTypeTreeCopyMoveTask(GTreeNode destinationNode, List<GTreeNode> droppedNodeList,
|
||||
public DataTypeTreeCopyMoveTask(CategoryNode destinationNode, List<GTreeNode> droppedNodeList,
|
||||
ActionType actionType, DataTypeArchiveGTree gTree,
|
||||
DataTypeConflictHandler conflictHandler) {
|
||||
super("Drag/Drop", true, true, true);
|
||||
|
@ -100,10 +98,10 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
|||
}
|
||||
|
||||
//
|
||||
// Note: we collapse the node before performing this work because there is a
|
||||
// Note: we collapse the node before performing this work because there is a
|
||||
// potential for a large number of events to be generated. Further, if the
|
||||
// given archive node has many children (like 10s of thousands), then the
|
||||
// copious events generated herein could lock the UI. By closing the node,
|
||||
// copious events generated herein could lock the UI. By closing the node,
|
||||
// the tree is not invalidating/validating its cache as a result of these
|
||||
// events.
|
||||
//
|
||||
|
@ -118,7 +116,6 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
|||
}
|
||||
|
||||
doCopy(monitor);
|
||||
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return; // nothing to report
|
||||
|
@ -161,12 +158,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
|||
DataTypeManager dtm = destinationArchive.getDataTypeManager();
|
||||
int txId = dtm.startTransaction("Copy/Move Category/DataType");
|
||||
try {
|
||||
if (destinationNode instanceof DataTypeNode) {
|
||||
dragNodeToDataType();
|
||||
}
|
||||
else {
|
||||
dragNodesToCategory(monitor);
|
||||
}
|
||||
dragNodesToCategory(monitor);
|
||||
}
|
||||
finally {
|
||||
dtm.endTransaction(txId, true);
|
||||
|
@ -189,8 +181,7 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
|||
}
|
||||
}
|
||||
|
||||
private void associateDataTypes(TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
private void associateDataTypes(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
if (!promptToAssociateTypes(monitor)) {
|
||||
return;
|
||||
|
@ -347,22 +338,19 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
|||
DataTypeManager nodeDtm = dataType.getDataTypeManager();
|
||||
boolean sameManager = (dtm == nodeDtm);
|
||||
DataType newDt = !sameManager ? dataType.clone(nodeDtm) : dataType.copy(nodeDtm);
|
||||
if (sameManager &&
|
||||
newDt.getCategoryPath().equals(toCategory.getCategoryPath())) {
|
||||
if (sameManager && newDt.getCategoryPath().equals(toCategory.getCategoryPath())) {
|
||||
renameAsCopy(toCategory, newDt);
|
||||
}
|
||||
|
||||
DataType resolvedDt = toCategory.addDataType(newDt, conflictHandler);
|
||||
if (resolvedDt instanceof Pointer || resolvedDt instanceof Array ||
|
||||
resolvedDt instanceof BuiltInDataType ||
|
||||
resolvedDt instanceof MissingBuiltInDataType) {
|
||||
resolvedDt instanceof BuiltInDataType || resolvedDt instanceof MissingBuiltInDataType) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!resolvedDt.getCategoryPath().equals(toCategory.getCategoryPath())) {
|
||||
errors.add(
|
||||
"Data type copy failed. Another copy of this data type already exists at " +
|
||||
resolvedDt.getPathName());
|
||||
errors.add("Data type copy failed. Another copy of this data type already exists at " +
|
||||
resolvedDt.getPathName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,17 +405,15 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
|||
}
|
||||
}
|
||||
|
||||
private void moveCategory(Category toCategory, Category category,
|
||||
TaskMonitor monitor) {
|
||||
private void moveCategory(Category toCategory, Category category, TaskMonitor monitor) {
|
||||
if (category.getParent() == toCategory) { // moving to same place
|
||||
return;
|
||||
}
|
||||
try {
|
||||
CategoryPath path = toCategory.getCategoryPath();
|
||||
if (path.isAncestorOrSelf(category.getCategoryPath())) {
|
||||
errors.add(
|
||||
"Cannot move a parent node onto a child node. Moving " + category + " to " +
|
||||
toCategory);
|
||||
errors.add("Cannot move a parent node onto a child node. Moving " + category +
|
||||
" to " + toCategory);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -441,30 +427,25 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
|||
|
||||
private void moveDataType(Category toCategory, DataType dataType) {
|
||||
if (dataType.getCategoryPath().equals(toCategory.getCategoryPath())) {
|
||||
errors.add(
|
||||
"Move failed. DataType is already in this category. Category " + toCategory +
|
||||
"; Data type: " + dataType);
|
||||
errors.add("Move failed. DataType is already in this category. Category " +
|
||||
toCategory + "; Data type: " + dataType);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
toCategory.moveDataType(dataType, conflictHandler);
|
||||
}
|
||||
catch (DataTypeDependencyException e) {
|
||||
errors.add(
|
||||
"Move failed. DataType is already in this category. Category " + toCategory +
|
||||
"; Data type: " + dataType + ". " + e.getMessage());
|
||||
errors.add("Move failed. DataType is already in this category. Category " +
|
||||
toCategory + "; Data type: " + dataType + ". " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void copyCategory(Category toCategory, Category category,
|
||||
TaskMonitor monitor) {
|
||||
private void copyCategory(Category toCategory, Category category, TaskMonitor monitor) {
|
||||
CategoryPath toPath = toCategory.getCategoryPath();
|
||||
boolean sameManager =
|
||||
(toCategory.getDataTypeManager() == category.getDataTypeManager());
|
||||
boolean sameManager = (toCategory.getDataTypeManager() == category.getDataTypeManager());
|
||||
if (sameManager && toPath.isAncestorOrSelf(category.getCategoryPath())) {
|
||||
errors.add("Copy failed. " +
|
||||
"Cannot copy a parent node onto a child node. Moving " + category + " to " +
|
||||
toCategory);
|
||||
errors.add("Copy failed. " + "Cannot copy a parent node onto a child node. Moving " +
|
||||
category + " to " + toCategory);
|
||||
return;
|
||||
}
|
||||
toCategory.copyCategory(category, conflictHandler, monitor);
|
||||
|
@ -481,32 +462,11 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
|||
"Expected node to be either an ArchiveNode or CategoryNode but was " + node.getClass());
|
||||
}
|
||||
|
||||
private boolean isAssociatedEitherWay(DataType dt1, DataType dt2) {
|
||||
return isAssociated(dt1, dt2) || isAssociated(dt2, dt1);
|
||||
}
|
||||
|
||||
private boolean isAssociated(DataType sourceDt, DataType destinationDt) {
|
||||
UniversalID destinationID = destinationDt.getUniversalID();
|
||||
if (destinationID == null || !destinationID.equals(sourceDt.getUniversalID())) {
|
||||
return false;
|
||||
}
|
||||
if (!haveSameSourceArchive(sourceDt, destinationDt)) {
|
||||
return false;
|
||||
}
|
||||
return isLocal(sourceDt);
|
||||
}
|
||||
|
||||
private boolean haveSameSourceArchive(DataType dt1, DataType dt2) {
|
||||
SourceArchive s1 = dt1.getSourceArchive();
|
||||
SourceArchive s2 = dt2.getSourceArchive();
|
||||
return s1.getSourceArchiveID().equals(s2.getSourceArchiveID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given data type's source archive is the same as it's current data
|
||||
* type manager. This is false if copying a new type from the program to an
|
||||
* external archive.
|
||||
*
|
||||
* type manager. This is false if copying a new type from the program to an
|
||||
* external archive.
|
||||
*
|
||||
* @param dt the type
|
||||
* @return true if the given type already lives in its source archive
|
||||
*/
|
||||
|
@ -516,96 +476,12 @@ public class DataTypeTreeCopyMoveTask extends Task {
|
|||
return sourceId.equals(dtmId);
|
||||
}
|
||||
|
||||
private void dragNodeToDataType() {
|
||||
DataType destinationDt = ((DataTypeNode) destinationNode).getDataType();
|
||||
|
||||
// there must be exactly one and it must be a dataTypeNode, because of isValidDropSite()
|
||||
GTreeNode node = droppedNodes.get(0);
|
||||
DataType replacementDt = ((DataTypeNode) node).getDataType();
|
||||
if (sourceArchive != destinationArchive) {
|
||||
if (isAssociatedEitherWay(replacementDt, destinationDt)) {
|
||||
handleAssociatedType(destinationDt, replacementDt);
|
||||
return;
|
||||
}
|
||||
|
||||
replacementDt = replacementDt.clone(replacementDt.getDataTypeManager());
|
||||
}
|
||||
else if (actionType == ActionType.COPY) { // Copy within a single data type manager.
|
||||
replacementDt = replacementDt.copy(replacementDt.getDataTypeManager());
|
||||
}
|
||||
|
||||
replaceDataType(destinationDt, replacementDt);
|
||||
}
|
||||
|
||||
private void handleAssociatedType(DataType destinationDt, DataType replacementDt) {
|
||||
if (isLocal(destinationDt)) {
|
||||
DataTypeSyncInfo syncInfo = new DataTypeSyncInfo(replacementDt,
|
||||
destinationDt.getDataTypeManager());
|
||||
if (!syncInfo.canCommit()) {
|
||||
Msg.showInfo(getClass(), gTree, "Commit Data Type",
|
||||
"No changes to commit");
|
||||
}
|
||||
// destination data-type is local to an archive
|
||||
else if (confirmCommit()) {
|
||||
// if the destination dataType is local to its dataTypeManager
|
||||
// then we are committing.
|
||||
DataTypeSynchronizer.commit(destinationDt.getDataTypeManager(), replacementDt);
|
||||
}
|
||||
}
|
||||
else { // else we are updating
|
||||
DataTypeSyncInfo syncInfo = new DataTypeSyncInfo(destinationDt,
|
||||
replacementDt.getDataTypeManager());
|
||||
if (!syncInfo.canUpdate()) {
|
||||
Msg.showInfo(getClass(), gTree, "Update Data Type", "No changes to copy");
|
||||
}
|
||||
else if (confirmUpdate()) {
|
||||
DataTypeSynchronizer.update(destinationDt.getDataTypeManager(), replacementDt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean confirmCommit() {
|
||||
return confirm("Commit Data Type?",
|
||||
"Do you want to commit the changes to this data type back to the source Archive? \n" +
|
||||
"(Warning: any changes in the source archive will be overwritten.)");
|
||||
}
|
||||
|
||||
private boolean confirmUpdate() {
|
||||
return confirm("Update Data Type?",
|
||||
"Do you want to update this data type with the changes in the source Archive?\n" +
|
||||
"(Warning: any local changes will be overwritten.)");
|
||||
}
|
||||
|
||||
private boolean confirm(String title, String message) {
|
||||
int choice = OptionDialog.showYesNoDialog(gTree, title, message);
|
||||
return choice == OptionDialog.YES_OPTION;
|
||||
}
|
||||
|
||||
private int askToAssociateDataTypes() {
|
||||
return OptionDialog.showYesNoCancelDialog(gTree, "Associate DataTypes?",
|
||||
"Do you want to associate local datatypes with the target archive?");
|
||||
}
|
||||
|
||||
private void replaceDataType(DataType existingDt, DataType replacementDt) {
|
||||
|
||||
int choice =
|
||||
OptionDialog.showYesNoDialog(gTree, "Replace Data Type?", "Replace " +
|
||||
existingDt.getPathName() + "\nwith " + replacementDt.getPathName() +
|
||||
"?");
|
||||
|
||||
if (choice == OptionDialog.YES_OPTION) {
|
||||
try {
|
||||
DataTypeManager dtMgr = existingDt.getDataTypeManager();
|
||||
dtMgr.replaceDataType(existingDt, replacementDt, true);
|
||||
}
|
||||
catch (DataTypeDependencyException e) {
|
||||
errors.add("Replace failed. Existing type " + existingDt + "; replacment type " +
|
||||
replacementDt + ". " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// filters out nodes with categories in their path
|
||||
// filters out nodes with categories in their path
|
||||
private void filterRedundantNodes() {
|
||||
|
||||
Set<GTreeNode> nodeSet = new HashSet<>(droppedNodes);
|
||||
|
|
|
@ -22,20 +22,21 @@ import java.awt.dnd.DnDConstants;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JTextField;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.menu.ActionState;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import docking.widgets.tree.support.GTreeDragNDropHandler;
|
||||
import docking.widgets.tree.support.GTreeNodeTransferable;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.actions.ConflictHandlerModesAction;
|
||||
import ghidra.app.plugin.core.datamgr.tree.*;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.app.util.datatype.DataTypeSelectionDialog;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
|
@ -57,7 +58,9 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
private ProgramDB program;
|
||||
private DataTypeManagerPlugin plugin;
|
||||
private DataTypesProvider provider;
|
||||
private DockingActionIf pasteAction;
|
||||
private ConflictHandlerModesAction conflictHandlerModesAction;
|
||||
private ProgramActionContext treeContext;
|
||||
private DataTypeArchiveGTree tree;
|
||||
private ArchiveRootNode archiveRootNode;
|
||||
private ArchiveNode programNode;
|
||||
|
@ -89,6 +92,9 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
assertNotNull("Did not successfully wait for the program node to load", programNode);
|
||||
|
||||
tool.showComponentProvider(provider, true);
|
||||
|
||||
pasteAction = getAction(plugin, "Paste");
|
||||
treeContext = new DataTypesActionContext(provider, program, tree, null);
|
||||
}
|
||||
|
||||
private ProgramDB buildProgram() throws Exception {
|
||||
|
@ -164,7 +170,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testConflictCopyInProgram() throws Exception {
|
||||
public void testCopyPasteToCategory_RenameConflictHandler() throws Exception {
|
||||
|
||||
enableRenameConflictHandler();
|
||||
|
||||
|
@ -194,7 +200,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testConflictCopyReplace() throws Exception {
|
||||
public void testCopyPasteToCategory_ReplaceExistingConflictHandler() throws Exception {
|
||||
|
||||
enableReplaceExistingConflictHandler();
|
||||
|
||||
|
@ -213,7 +219,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testConflictCopyUseExisting() throws Exception {
|
||||
public void testCopyPasteToCategory_UseExistingConflictHandler() throws Exception {
|
||||
|
||||
enableUseExistingConflictHandler();
|
||||
|
||||
|
@ -234,11 +240,11 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
assertEquals(originalDataType, newDataTypeNode.getDataType());
|
||||
|
||||
structureNode = (DataTypeNode) category3Node.getChild(structName);
|
||||
assertTrue(!originalDataType.isEquivalent(structureNode.getDataType()));
|
||||
assertFalse(originalDataType.isEquivalent(structureNode.getDataType()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConflictPasteMoveRename() throws Exception {
|
||||
public void testCutPasteToCategory_RenameConflictHandler() throws Exception {
|
||||
|
||||
enableRenameConflictHandler();
|
||||
|
||||
|
@ -256,7 +262,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testConflictDragMoveRename() throws Exception {
|
||||
public void testDragMoveToCategory_RenameConflictHandler() throws Exception {
|
||||
|
||||
enableRenameConflictHandler();
|
||||
|
||||
|
@ -269,7 +275,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
expandNode(miscNode);
|
||||
|
||||
// move/drag ArrayStruct to MISC
|
||||
dragNodeToNode(structureNode, miscNode);
|
||||
moveDragNodeToNode(structureNode, miscNode);
|
||||
|
||||
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName + DataType.CONFLICT_SUFFIX);
|
||||
assertNotNull(node);
|
||||
|
@ -277,7 +283,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testConflictDragCopyRename() throws Exception {
|
||||
public void testDragCopyToCategory_RenameConflictHandler() throws Exception {
|
||||
|
||||
enableRenameConflictHandler();
|
||||
|
||||
|
@ -291,7 +297,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
expandNode(miscNode);
|
||||
|
||||
// copy/drag ArrayStruct to MISC
|
||||
copyNodeToNode(structureNode, miscNode);
|
||||
copyDragNodeToNode(structureNode, miscNode);
|
||||
|
||||
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
|
||||
category3Node = (CategoryNode) tree.getViewNode(category3Node);
|
||||
|
@ -305,7 +311,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testConflictDragCopyReplace() throws Exception {
|
||||
public void testDragCopyToCategory_ReplaceExistingConflictHandler() throws Exception {
|
||||
|
||||
enableReplaceExistingConflictHandler();
|
||||
|
||||
|
@ -314,13 +320,13 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
String structName = "ArrayStruct";
|
||||
DataTypeNode structureNode = createAndSelectStructure(structName);
|
||||
CategoryNode category3Node = (CategoryNode) structureNode.getParent();
|
||||
DataType origDt = structureNode.getDataType();
|
||||
DataType originalDt = structureNode.getDataType();
|
||||
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||
expandNode(miscNode);
|
||||
|
||||
// copy/drag ArrayStruct to MISC
|
||||
copyNodeToNode(structureNode, miscNode);
|
||||
copyDragNodeToNode(structureNode, miscNode);
|
||||
|
||||
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
|
||||
category3Node = (CategoryNode) tree.getViewNode(category3Node);
|
||||
|
@ -329,11 +335,11 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
||||
assertNotNull(node);
|
||||
assertEquals(category3Node, structureNode.getParent());
|
||||
assertTrue(origDt.isEquivalent(structureNode.getDataType()));
|
||||
assertTrue(originalDt.isEquivalent(structureNode.getDataType()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConflictDragCopyUseExisting() throws Exception {
|
||||
public void testDragCopyToCategory_UseExistingConflictHandler() throws Exception {
|
||||
|
||||
enableUseExistingConflictHandler();
|
||||
|
||||
|
@ -341,23 +347,23 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
// in the program, rename Category1/Category2/Category3/IntStruct to ArrayStruct
|
||||
String structName = "ArrayStruct";
|
||||
DataTypeNode structureNode = createAndSelectStructure(structName);
|
||||
DataType origDt = structureNode.getDataType();
|
||||
DataType originalDt = structureNode.getDataType();
|
||||
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||
expandNode(miscNode);
|
||||
|
||||
// copy/drag ArrayStruct to MISC
|
||||
copyNodeToNode(structureNode, miscNode);
|
||||
copyDragNodeToNode(structureNode, miscNode);
|
||||
|
||||
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
||||
assertNotNull(node);
|
||||
structureNode = (DataTypeNode) miscNode.getChild(structName);
|
||||
assertNotNull(structureNode);
|
||||
assertTrue(!origDt.isEquivalent(structureNode.getDataType()));
|
||||
assertFalse(originalDt.isEquivalent(structureNode.getDataType()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConflictPasteMoveReplace() throws Exception {
|
||||
public void testCutPasteToCategory_ReplaceExistingConflictHandler() throws Exception {
|
||||
|
||||
enableReplaceExistingConflictHandler();
|
||||
|
||||
|
@ -368,18 +374,18 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||
DataTypeNode node = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||
DataType origDt = node.getDataType();
|
||||
DataType originalDt = node.getDataType();
|
||||
|
||||
// move ArrayStruct to MISC
|
||||
cutPasteSelectedNodeToNode("MISC");
|
||||
|
||||
node = (DataTypeNode) miscNode.getChild(structName);
|
||||
assertNotNull(node);
|
||||
assertTrue(!origDt.equals(node.getDataType()));
|
||||
assertFalse(originalDt.equals(node.getDataType()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConflictPasteMoveUseExisting() throws Exception {
|
||||
public void testCutPasteToCategory_UseExistingConflictHandler() throws Exception {
|
||||
|
||||
enableUseExistingConflictHandler();
|
||||
|
||||
|
@ -390,18 +396,18 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||
DataTypeNode node = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||
DataType origDt = node.getDataType();
|
||||
DataType originalDt = node.getDataType();
|
||||
|
||||
cutPasteSelectedNodeToNode("MISC");
|
||||
|
||||
node = (DataTypeNode) miscNode.getChild(structName);
|
||||
assertNotNull(node);
|
||||
assertTrue(origDt.equals(node.getDataType()));
|
||||
assertTrue(originalDt.equals(node.getDataType()));
|
||||
assertNull(structureNode.getParent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConflictDragMoveReplace() throws Exception {
|
||||
public void testDragMoveToCategory_ReplaceExistingConflictHandler() throws Exception {
|
||||
|
||||
enableReplaceExistingConflictHandler();
|
||||
|
||||
|
@ -414,21 +420,21 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
expandNode(miscNode);
|
||||
|
||||
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
||||
DataType origDt = node.getDataType();
|
||||
DataType originalDt = node.getDataType();
|
||||
|
||||
// move/drag ArrayStruct to MISC
|
||||
dragNodeToNode(structureNode, miscNode);
|
||||
moveDragNodeToNode(structureNode, miscNode);
|
||||
|
||||
node = (DataTypeNode) miscNode.getChild(structName);
|
||||
assertNotNull(node);
|
||||
|
||||
assertNull(structureNode.getParent());
|
||||
assertNotNull(node);
|
||||
assertTrue(!origDt.equals(node.getDataType()));
|
||||
assertFalse(originalDt.equals(node.getDataType()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConflictDragMoveUseExisting() throws Exception {
|
||||
public void testDragMoveToCategory_UseExistingConflictHandler() throws Exception {
|
||||
|
||||
enableUseExistingConflictHandler();
|
||||
|
||||
|
@ -440,21 +446,24 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||
expandNode(miscNode);
|
||||
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
||||
DataType origDt = node.getDataType();
|
||||
DataType originalDt = node.getDataType();
|
||||
|
||||
// move/drag ArrayStruct to MISC
|
||||
dragNodeToNode(structureNode, miscNode);
|
||||
moveDragNodeToNode(structureNode, miscNode);
|
||||
|
||||
node = (DataTypeNode) miscNode.getChild(structName);
|
||||
assertNotNull(node);
|
||||
|
||||
assertNull(structureNode.getParent());
|
||||
assertNotNull(node);
|
||||
assertTrue(origDt.equals(node.getDataType()));
|
||||
assertTrue(originalDt.equals(node.getDataType()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceDataTypeYes() throws Exception {
|
||||
public void testDragMoveToDataType_Replace() throws Exception {
|
||||
|
||||
enableReplaceExistingConflictHandler();
|
||||
|
||||
String structName = "ArrayStruct";
|
||||
DataTypeNode structureNode = createAndSelectStructure(structName);
|
||||
Structure structure = (Structure) structureNode.getDataType();
|
||||
|
@ -465,9 +474,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
|
||||
// drag/move ArrayStruct to MISC/ArrayStruct
|
||||
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||
dragNodeToNode(structureNode, miscStructureNode);
|
||||
|
||||
pressButtonOnOptionDialog("Yes");
|
||||
moveDragNodeToNode(structureNode, miscStructureNode);
|
||||
|
||||
assertNull(structureNode.getParent());
|
||||
assertNull(category3Node.getChild(structName));
|
||||
|
@ -491,122 +498,38 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceDataTypeNo() throws Exception {
|
||||
String structName = "ArrayStruct";
|
||||
DataTypeNode structureNode = createAndSelectStructure(structName);
|
||||
Structure structure = (Structure) structureNode.getDataType();
|
||||
CategoryNode category3Node = (CategoryNode) structureNode.getParent();
|
||||
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||
expandNode(miscNode);
|
||||
|
||||
// drag/move ArrayStruct to MISC/ArrayStruct
|
||||
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||
dragNodeToNode(structureNode, miscStructureNode);
|
||||
|
||||
pressButtonOnOptionDialog("No");
|
||||
|
||||
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
|
||||
assertNotNull(structureNode.getParent());
|
||||
|
||||
category3Node = (CategoryNode) tree.getViewNode(category3Node);
|
||||
assertNotNull(category3Node.getChild("ArrayStruct"));
|
||||
|
||||
miscNode = (CategoryNode) tree.getViewNode(miscNode);
|
||||
DataTypeNode node = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||
assertTrue(!structure.isEquivalent(node.getDataType()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceDTSameParentYes() throws Exception {
|
||||
public void testDragMoveToDataType_SameParent() throws Exception {
|
||||
// drag/drop a data type onto another data type
|
||||
// get Option dialog, and choose "Yes"
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||
expandNode(miscNode);
|
||||
|
||||
DataTypeNode structureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||
DataTypeNode unionNode = (DataTypeNode) miscNode.getChild("ArrayUnion");
|
||||
|
||||
dragNodeToNode(unionNode, structureNode);
|
||||
setErrorsExpected(true);
|
||||
moveDragNodeToNode(unionNode, structureNode);
|
||||
setErrorsExpected(false);
|
||||
|
||||
pressButtonOnOptionDialog("Yes");
|
||||
|
||||
assertNotNull(miscNode.getChild("ArrayUnion"));
|
||||
assertNull(miscNode.getChild("ArrayStruct"));
|
||||
// can't move a data type into its same category
|
||||
waitForWindow("Encountered Errors Copying/Moving");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceDTSameParentNo() throws Exception {
|
||||
// drag/drop a data type onto another data type
|
||||
// get Option dialog, and choose "Yes"
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||
expandNode(miscNode);
|
||||
public void testDragCopyToDataType() throws Exception {
|
||||
|
||||
DataTypeNode structureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||
DataTypeNode unionNode = (DataTypeNode) miscNode.getChild("ArrayUnion");
|
||||
enableRenameConflictHandler();
|
||||
|
||||
dragNodeToNode(unionNode, structureNode);
|
||||
|
||||
pressButtonOnOptionDialog("No");
|
||||
|
||||
assertNotNull(miscNode.getChild("ArrayUnion"));
|
||||
assertNotNull(miscNode.getChild("ArrayStruct"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyReplaceDataTypeNo() throws Exception {
|
||||
// drag/copy a data type onto another data type
|
||||
// get Option dialog, and choose "No"
|
||||
// cause a conflict
|
||||
// in the program, rename Category1/Category2/Category3/IntStruct to ArrayStruct
|
||||
String structName = "ArrayStruct";
|
||||
DataTypeNode structureNode = createAndSelectStructure(structName);
|
||||
CategoryNode category3Node = (CategoryNode) structureNode.getParent();
|
||||
|
||||
// drag/move ArrayStruct to MISC/ArrayStruct
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild("MISC");
|
||||
expandNode(miscNode);
|
||||
|
||||
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||
DataType origDt = miscStructureNode.getDataType();
|
||||
|
||||
copyNodeToNode(structureNode, miscStructureNode);
|
||||
|
||||
pressButtonOnOptionDialog("No");
|
||||
|
||||
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
|
||||
assertNotNull(structureNode.getParent());
|
||||
|
||||
category3Node = (CategoryNode) tree.getViewNode(category3Node);
|
||||
assertNotNull(category3Node.getChild("ArrayStruct"));
|
||||
|
||||
miscNode = (CategoryNode) tree.getViewNode(miscNode);
|
||||
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
||||
assertEquals(origDt, node.getDataType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyReplaceDataTypeYes() throws Exception {
|
||||
// drag/copy a data type onto another data type
|
||||
// get Option dialog, and choose "Yes"
|
||||
// cause a conflict
|
||||
// in the program, rename Category1/Category2/Category3/IntStruct to ArrayStruct
|
||||
String structName = "ArrayStruct";
|
||||
DataTypeNode structureNode = createAndSelectStructure(structName);
|
||||
Structure structure = (Structure) structureNode.getDataType();
|
||||
CategoryNode category3Node = (CategoryNode) structureNode.getParent();
|
||||
|
||||
// drag/move ArrayStruct to MISC/ArrayStruct
|
||||
String miscName = "MISC";
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
|
||||
expandNode(miscNode);
|
||||
|
||||
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild("ArrayStruct");
|
||||
DataType miscStructure = miscStructureNode.getDataType();
|
||||
|
||||
copyNodeToNode(structureNode, miscStructureNode);
|
||||
|
||||
pressButtonOnOptionDialog("Yes");
|
||||
copyDragNodeToNode(structureNode, miscStructureNode);
|
||||
|
||||
structureNode = (DataTypeNode) tree.getViewNode(structureNode);
|
||||
assertNotNull(structureNode.getParent());
|
||||
|
@ -615,26 +538,251 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
assertNotNull(category3Node.getChild(structName));
|
||||
|
||||
miscNode = (CategoryNode) tree.getViewNode(miscNode);
|
||||
DataTypeNode node = (DataTypeNode) miscNode.getChild(structName);
|
||||
assertTrue(structure.isEquivalent(node.getDataType()));
|
||||
DataTypeNode newNode =
|
||||
(DataTypeNode) miscNode.getChild(structName + DataType.CONFLICT_SUFFIX);
|
||||
assertNotNull(newNode);
|
||||
assertNotNull(structureNode.getParent());
|
||||
}
|
||||
|
||||
undo();
|
||||
@Test
|
||||
public void testCopyPasteToCategory() {
|
||||
|
||||
miscNode = (CategoryNode) programNode.getChild(miscName);
|
||||
node = (DataTypeNode) miscNode.getChild(structName);
|
||||
assertTrue(miscStructure.isEquivalent(node.getDataType()));
|
||||
String miscName = "MISC";
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
|
||||
expandNode(miscNode);
|
||||
|
||||
redo();
|
||||
String dtName = "ArrayStruct";
|
||||
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName);
|
||||
selectNode(miscStructureNode);
|
||||
|
||||
miscNode = (CategoryNode) programNode.getChild(miscName);
|
||||
node = (DataTypeNode) miscNode.getChild(structName);
|
||||
assertTrue(structure.isEquivalent(node.getDataType()));
|
||||
DockingActionIf copyAction = getAction(plugin, "Copy");
|
||||
assertTrue(copyAction.isEnabledForContext(treeContext));
|
||||
assertFalse(pasteAction.isEnabledForContext(treeContext));
|
||||
|
||||
DataTypeTestUtils.performAction(copyAction, tree);
|
||||
|
||||
selectNode(miscNode);
|
||||
assertTrue(pasteAction.isEnabledForContext(treeContext));
|
||||
DataTypeTestUtils.performAction(pasteAction, tree);
|
||||
GTreeNode newNode = miscNode.getChild("Copy_1_of_" + dtName);
|
||||
assertNotNull(newNode);
|
||||
|
||||
selectNode(miscNode);
|
||||
assertTrue(pasteAction.isEnabledForContext(treeContext));
|
||||
DataTypeTestUtils.performAction(pasteAction, tree);
|
||||
newNode = miscNode.getChild("Copy_2_of_" + dtName);
|
||||
assertNotNull(newNode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyPasteToDataType_SameType() throws Exception {
|
||||
|
||||
String miscName = "MISC";
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
|
||||
expandNode(miscNode);
|
||||
|
||||
String dtName = "ArrayStruct";
|
||||
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName);
|
||||
selectNode(miscStructureNode);
|
||||
|
||||
DockingActionIf copyAction = getAction(plugin, "Copy");
|
||||
assertTrue(copyAction.isEnabledForContext(treeContext));
|
||||
assertFalse(pasteAction.isEnabledForContext(treeContext));
|
||||
|
||||
DataTypeTestUtils.performAction(copyAction, tree);
|
||||
|
||||
selectNode(miscStructureNode);
|
||||
assertTrue(pasteAction.isEnabledForContext(treeContext));
|
||||
DataTypeTestUtils.performAction(pasteAction, tree);
|
||||
GTreeNode newNode = miscNode.getChild("Copy_1_of_" + dtName);
|
||||
assertNotNull(newNode);
|
||||
|
||||
selectNode(miscStructureNode);
|
||||
assertTrue(pasteAction.isEnabledForContext(treeContext));
|
||||
|
||||
DataTypeTestUtils.performAction(pasteAction, tree);
|
||||
|
||||
newNode = miscNode.getChild("Copy_2_of_" + dtName);
|
||||
assertNotNull(newNode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyPasteToDataType_DifferentType() throws Exception {
|
||||
|
||||
String miscName = "MISC";
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
|
||||
expandNode(miscNode);
|
||||
|
||||
String dtName = "ArrayStruct";
|
||||
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName);
|
||||
selectNode(miscStructureNode);
|
||||
|
||||
DockingActionIf copyAction = getAction(plugin, "Copy");
|
||||
assertTrue(copyAction.isEnabledForContext(treeContext));
|
||||
assertFalse(pasteAction.isEnabledForContext(treeContext));
|
||||
|
||||
DataTypeTestUtils.performAction(copyAction, tree);
|
||||
|
||||
DataTypeNode miscUnionNode = (DataTypeNode) miscNode.getChild("ArrayUnion");
|
||||
selectNode(miscUnionNode);
|
||||
assertTrue(pasteAction.isEnabledForContext(treeContext));
|
||||
|
||||
DataTypeTestUtils.performAction(pasteAction, tree);
|
||||
|
||||
GTreeNode newNode = miscNode.getChild("Copy_1_of_" + dtName);
|
||||
assertNotNull(newNode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyPasteToDataType_DifferentType_SameName_RenameConflictHanlder()
|
||||
throws Exception {
|
||||
|
||||
enableRenameConflictHandler();
|
||||
|
||||
String existingDtName = "ArrayUnion";
|
||||
|
||||
DataTypeNode intStructureNode =
|
||||
(DataTypeNode) getNotepadNode("Category1/Category2/Category3/IntStruct");
|
||||
rename(intStructureNode, existingDtName);
|
||||
intStructureNode =
|
||||
(DataTypeNode) getNotepadNode("Category1/Category2/Category3/" + existingDtName);
|
||||
selectNode(intStructureNode);
|
||||
|
||||
DockingActionIf copyAction = getAction(plugin, "Copy");
|
||||
assertTrue(copyAction.isEnabledForContext(treeContext));
|
||||
assertFalse(pasteAction.isEnabledForContext(treeContext));
|
||||
|
||||
DataTypeTestUtils.performAction(copyAction, tree);
|
||||
|
||||
String miscName = "MISC";
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
|
||||
expandNode(miscNode);
|
||||
DataTypeNode miscUnionNode = (DataTypeNode) miscNode.getChild(existingDtName);
|
||||
selectNode(miscUnionNode);
|
||||
assertTrue(pasteAction.isEnabledForContext(treeContext));
|
||||
|
||||
DataTypeTestUtils.performAction(pasteAction, tree);
|
||||
waitForTree();
|
||||
|
||||
GTreeNode newNode = miscNode.getChild(existingDtName + DataType.CONFLICT_SUFFIX);
|
||||
assertNotNull(newNode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyPasteToDataType_FromDifferentCategory() throws Exception {
|
||||
|
||||
String miscName = "MISC";
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
|
||||
expandNode(miscNode);
|
||||
|
||||
DataTypeNode intStructureNode =
|
||||
(DataTypeNode) getNotepadNode("Category1/Category2/Category3/IntStruct");
|
||||
selectNode(intStructureNode);
|
||||
|
||||
DockingActionIf copyAction = getAction(plugin, "Copy");
|
||||
assertTrue(copyAction.isEnabledForContext(treeContext));
|
||||
assertFalse(pasteAction.isEnabledForContext(treeContext));
|
||||
|
||||
DataTypeTestUtils.performAction(copyAction, tree);
|
||||
|
||||
String dtName = "ArrayStruct";
|
||||
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName);
|
||||
selectNode(miscStructureNode);
|
||||
assertTrue(pasteAction.isEnabledForContext(treeContext));
|
||||
|
||||
DataTypeTestUtils.performAction(pasteAction, tree);
|
||||
|
||||
GTreeNode newNode = miscNode.getChild("IntStruct");
|
||||
assertNotNull(newNode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyPasteToDataType_MultipleDataTypes() {
|
||||
|
||||
String miscName = "MISC";
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
|
||||
expandNode(miscNode);
|
||||
|
||||
String dtName1 = "ArrayStruct";
|
||||
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(dtName1);
|
||||
String dtName2 = "ArrayUnion";
|
||||
DataTypeNode miscUnionNode = (DataTypeNode) miscNode.getChild(dtName2);
|
||||
selectNodes(miscStructureNode, miscUnionNode);
|
||||
|
||||
DockingActionIf copyAction = getAction(plugin, "Copy");
|
||||
assertTrue(copyAction.isEnabledForContext(treeContext));
|
||||
assertFalse(pasteAction.isEnabledForContext(treeContext));
|
||||
|
||||
DataTypeTestUtils.performAction(copyAction, tree);
|
||||
|
||||
DataTypeNode intStructureNode =
|
||||
(DataTypeNode) getNotepadNode("Category1/Category2/Category3/IntStruct");
|
||||
selectNode(intStructureNode);
|
||||
|
||||
assertTrue(pasteAction.isEnabledForContext(treeContext));
|
||||
DataTypeTestUtils.performAction(pasteAction, tree);
|
||||
|
||||
GTreeNode newNode = miscNode.getChild(dtName1);
|
||||
assertNotNull(newNode);
|
||||
newNode = miscNode.getChild(dtName2);
|
||||
assertNotNull(newNode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplaceAction() {
|
||||
|
||||
String miscName = "MISC";
|
||||
CategoryNode miscNode = (CategoryNode) programNode.getChild(miscName);
|
||||
expandNode(miscNode);
|
||||
|
||||
String originalDtName = "ArrayStruct";
|
||||
DataTypeNode miscStructureNode = (DataTypeNode) miscNode.getChild(originalDtName);
|
||||
selectNode(miscStructureNode);
|
||||
|
||||
DockingActionIf replaceAction = getAction(plugin, "Replace Data Type");
|
||||
assertTrue(replaceAction.isEnabledForContext(treeContext));
|
||||
DataTypeTestUtils.performAction(replaceAction, tree, false);
|
||||
|
||||
String newDtName = "IntStruct";
|
||||
chooseDataType(newDtName);
|
||||
|
||||
DataTypeNode updatedNode = (DataTypeNode) miscNode.getChild(newDtName);
|
||||
assertNotNull(updatedNode);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Refactored Methods
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private void chooseDataType(String dtName) {
|
||||
|
||||
DataTypeSelectionDialog chooser = waitForDialogComponent(DataTypeSelectionDialog.class);
|
||||
|
||||
JTextField tf = findComponent(chooser, JTextField.class);
|
||||
triggerText(tf, dtName);
|
||||
|
||||
pressButtonByText(chooser, "OK");
|
||||
waitForTasks();
|
||||
}
|
||||
|
||||
private GTreeNode getNotepadNode(String path) {
|
||||
|
||||
GTreeNode last = programNode;
|
||||
String[] names = path.split("/");
|
||||
for (String name : names) {
|
||||
last = last.getChild(name);
|
||||
}
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
private void rename(DataTypeNode node, String newName) throws Exception {
|
||||
|
||||
DataType dt = node.getDataType();
|
||||
tx(program, () -> dt.setName(newName));
|
||||
waitForProgram();
|
||||
}
|
||||
|
||||
/**
|
||||
* In the program, rename Category1/Category2/Category3/IntStruct to <structureName>
|
||||
*/
|
||||
|
@ -649,9 +797,7 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
DataTypeNode structureNode = (DataTypeNode) category3Node.getChild("IntStruct");
|
||||
Structure structure = (Structure) structureNode.getDataType();
|
||||
|
||||
int transactionID = program.startTransaction("test");
|
||||
structure.setName(structureName);
|
||||
program.endTransaction(transactionID, true);
|
||||
tx(program, () -> structure.setName(structureName));
|
||||
waitForProgram();
|
||||
|
||||
structureNode = (DataTypeNode) category3Node.getChild(structureName);
|
||||
|
@ -659,9 +805,6 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
return structureNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the currently selected node to the node by the given name.
|
||||
*/
|
||||
private CategoryNode copyPasteSelectedNodeToNode(String toNodeName) throws Exception {
|
||||
DockingActionIf copyAction = getAction(plugin, "Copy");
|
||||
assertTrue(copyAction.isEnabled());
|
||||
|
@ -671,10 +814,8 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
expandNode(miscNode);
|
||||
selectNode(miscNode);
|
||||
|
||||
executeOnSwingWithoutBlocking(() -> {
|
||||
DockingActionIf pasteAction = getAction(plugin, "Paste");
|
||||
DataTypeTestUtils.performAction(pasteAction, tree);
|
||||
});
|
||||
runSwing(() -> DataTypeTestUtils.performAction(pasteAction, tree), false);
|
||||
waitForTasks();
|
||||
return miscNode;
|
||||
}
|
||||
|
||||
|
@ -686,40 +827,30 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
expandNode(miscNode);
|
||||
selectNode(miscNode);
|
||||
|
||||
executeOnSwingWithoutBlocking(() -> {
|
||||
DockingActionIf pasteAction = getAction(plugin, "Paste");
|
||||
DataTypeTestUtils.performAction(pasteAction, tree);
|
||||
});
|
||||
runSwing(() -> DataTypeTestUtils.performAction(pasteAction, tree), false);
|
||||
waitForTasks();
|
||||
return miscNode;
|
||||
}
|
||||
|
||||
private void pressButtonOnOptionDialog(String buttonName) throws Exception {
|
||||
OptionDialog d = waitForDialogComponent(OptionDialog.class);
|
||||
JButton button = findButtonByText(d, buttonName);
|
||||
assertNotNull(button);
|
||||
pressButton(button);
|
||||
waitForProgram();
|
||||
}
|
||||
|
||||
private void dragNodeToNode(GTreeNode fromNode, final GTreeNode toNode) {
|
||||
private void moveDragNodeToNode(GTreeNode fromNode, final GTreeNode toNode) {
|
||||
final GTreeDragNDropHandler dragNDropHandler = tree.getDragNDropHandler();
|
||||
List<GTreeNode> dropList = new ArrayList<>();
|
||||
dropList.add(fromNode);
|
||||
final Transferable transferable = new GTreeNodeTransferable(dragNDropHandler, dropList);
|
||||
|
||||
runSwing(
|
||||
() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_MOVE), false);
|
||||
waitForSwing();
|
||||
runSwing(() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_MOVE),
|
||||
false);
|
||||
waitForTasks();
|
||||
}
|
||||
|
||||
private void copyNodeToNode(GTreeNode fromNode, final GTreeNode toNode) throws Exception {
|
||||
private void copyDragNodeToNode(GTreeNode fromNode, final GTreeNode toNode) throws Exception {
|
||||
final GTreeDragNDropHandler dragNDropHandler = tree.getDragNDropHandler();
|
||||
List<GTreeNode> dropList = new ArrayList<>();
|
||||
dropList.add(fromNode);
|
||||
final Transferable transferable = new GTreeNodeTransferable(dragNDropHandler, dropList);
|
||||
|
||||
runSwing(
|
||||
() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_COPY), false);
|
||||
Transferable transferable = new GTreeNodeTransferable(dragNDropHandler, dropList);
|
||||
runSwing(() -> dragNDropHandler.drop(toNode, transferable, DnDConstants.ACTION_COPY),
|
||||
false);
|
||||
waitForTasks();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
@ -735,6 +866,11 @@ public class DataTypeCopyMoveDragTest extends AbstractGhidraHeadedIntegrationTes
|
|||
waitForTree();
|
||||
}
|
||||
|
||||
private void selectNodes(GTreeNode... nodes) {
|
||||
tree.setSelectedNodes(nodes);
|
||||
waitForTree();
|
||||
}
|
||||
|
||||
private void waitForTree() {
|
||||
waitForTree(tree);
|
||||
}
|
||||
|
|
|
@ -113,6 +113,9 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
treeContext = new DataTypesActionContext(provider, program, tree, null);
|
||||
|
||||
removeDistractingPlugins();
|
||||
|
||||
cutAction = getAction(plugin, "Copy");
|
||||
pasteAction = getAction(plugin, "Paste");
|
||||
}
|
||||
|
||||
private void removeDistractingPlugins() {
|
||||
|
@ -589,8 +592,6 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
selectNode(myStructNode);
|
||||
|
||||
DockingActionIf copyAction = getAction(plugin, "Copy");
|
||||
cutAction = getAction(plugin, "Cut");
|
||||
pasteAction = getAction(plugin, "Paste");
|
||||
|
||||
assertTrue(cutAction.isEnabledForContext(treeContext));
|
||||
assertTrue(copyAction.isEnabledForContext(treeContext));
|
||||
|
@ -616,8 +617,6 @@ public class DataTypeManagerPluginTest extends AbstractGhidraHeadedIntegrationTe
|
|||
selectNode(cat2Node);
|
||||
|
||||
DockingActionIf copyAction = getAction(plugin, "Copy");
|
||||
cutAction = getAction(plugin, "Cut");
|
||||
pasteAction = getAction(plugin, "Paste");
|
||||
|
||||
assertTrue(cutAction.isEnabledForContext(treeContext));
|
||||
assertTrue(copyAction.isEnabledForContext(treeContext));
|
||||
|
|
|
@ -25,28 +25,31 @@ import ghidra.util.task.TaskMonitor;
|
|||
* Each data type resides in a given a category.
|
||||
*/
|
||||
public interface Category extends Comparable<Category> {
|
||||
|
||||
/**
|
||||
* Get the name of this category.
|
||||
* @return the name.
|
||||
*/
|
||||
public abstract String getName();
|
||||
|
||||
/**
|
||||
* Sets the name of this category.
|
||||
* @param name the new name for this category
|
||||
* @throws DuplicateNameException if another category exists in the same parent with the same name;
|
||||
* @throws DuplicateNameException if another category exists in the same parent with the same
|
||||
* name
|
||||
* @throws InvalidNameException if the name is not an acceptable name.
|
||||
*/
|
||||
public abstract void setName(String name) throws DuplicateNameException, InvalidNameException;
|
||||
|
||||
/**
|
||||
* Get all categories in this category.
|
||||
* @return zero-length array if there are no categories
|
||||
* @return zero-length array if there are no categories.
|
||||
*/
|
||||
public abstract Category[] getCategories();
|
||||
|
||||
/**
|
||||
* Get all data types in this category.
|
||||
* @return zero-length array if there are no data types
|
||||
* @return zero-length array if there are no data types.
|
||||
*/
|
||||
public abstract DataType[] getDataTypes();
|
||||
|
||||
|
@ -55,11 +58,12 @@ public interface Category extends Comparable<Category> {
|
|||
* The base name of a name is the first part of the string up to where the first ".conflict"
|
||||
* occurs. In other words, finds all data types whose name matches the given name once
|
||||
* any conflict suffixes have been removed from both the given name and the data types
|
||||
* that are being scanned.
|
||||
* @param name the name for which to get conflict related data types in this category. Note: the
|
||||
* name that is passed in will be normalized to its base name, so you may pass in names with .conflict
|
||||
* appended as a convenience.
|
||||
* @return a list of data types that have the same base name as the base name of the given name
|
||||
* that are being scanned.
|
||||
* @param name the name for which to get conflict related data types in this category. Note:
|
||||
* the name that is passed in will be normalized to its base name, so you may pass in names
|
||||
* with .conflict appended as a convenience.
|
||||
* @return a list of data types that have the same base name as the base name of the given
|
||||
* name.
|
||||
*/
|
||||
public abstract List<DataType> getDataTypesByBaseName(String name);
|
||||
|
||||
|
@ -73,8 +77,8 @@ public interface Category extends Comparable<Category> {
|
|||
|
||||
/**
|
||||
* Get a category with the given name.
|
||||
* @param name the name of the category
|
||||
* @return null if there is no category by this name
|
||||
* @param name the name of the category.
|
||||
* @return null if there is no category by this name.
|
||||
*/
|
||||
public abstract Category getCategory(String name);
|
||||
|
||||
|
@ -86,39 +90,41 @@ public interface Category extends Comparable<Category> {
|
|||
|
||||
/**
|
||||
* Get a data type with the given name.
|
||||
* @param name the name of the data type
|
||||
* @return null if there is no data type by this name
|
||||
* @param name the name of the data type.
|
||||
* @return null if there is no data type by this name.
|
||||
*/
|
||||
public abstract DataType getDataType(String name);
|
||||
|
||||
/**
|
||||
* Create a category with the given name; if category already exists, then
|
||||
* return that category.
|
||||
* @param name the category name
|
||||
* @throws InvalidNameException if name has invalid characters
|
||||
* Create a category with the given name; if category already exists, then return that
|
||||
* category.
|
||||
* @param name the category name.
|
||||
* @return the category.
|
||||
* @throws InvalidNameException if name has invalid characters.
|
||||
*/
|
||||
public abstract Category createCategory(String name) throws InvalidNameException;
|
||||
|
||||
/**
|
||||
* Remove the named category from this category.
|
||||
* @param name the name of the category to remove
|
||||
* @param monitor the task monitor
|
||||
* @return true if the category was removed
|
||||
* @param name the name of the category to remove.
|
||||
* @param monitor the task monitor.
|
||||
* @return true if the category was removed.
|
||||
*/
|
||||
public abstract boolean removeCategory(String name, TaskMonitor monitor);
|
||||
|
||||
/**
|
||||
* Remove the named category from this category, IFF it is empty.
|
||||
* @param name the name of the category to remove
|
||||
* @param monitor the task monitor
|
||||
* @return true if the category was removed
|
||||
* @param name the name of the category to remove.
|
||||
* @param monitor the task monitor.
|
||||
* @return true if the category was removed.
|
||||
*/
|
||||
public abstract boolean removeEmptyCategory(String name, TaskMonitor monitor);
|
||||
|
||||
/**
|
||||
* Move the given category to this category; category is removed from
|
||||
* its original parent category.
|
||||
* @param category the category to move
|
||||
* Move the given category to this category; category is removed from its original parent
|
||||
* category.
|
||||
* @param category the category to move.
|
||||
* @param monitor the monitor.
|
||||
* @throws DuplicateNameException if this category already contains a
|
||||
* category or data type with the same name as the category param.
|
||||
*/
|
||||
|
@ -126,15 +132,18 @@ public interface Category extends Comparable<Category> {
|
|||
throws DuplicateNameException;
|
||||
|
||||
/**
|
||||
* Make a new subcategory from the given category.
|
||||
* @param category the category to copy into this category
|
||||
* @return category that is added to this category
|
||||
* Make a new sub-category from the given category.
|
||||
* @param category the category to copy into this category.
|
||||
* @param handler the handler to call if there is a data type conflict.
|
||||
* @param monitor the monitor.
|
||||
* @return category that is added to this category.
|
||||
*/
|
||||
public abstract Category copyCategory(Category category, DataTypeConflictHandler handler,
|
||||
TaskMonitor monitor);
|
||||
|
||||
/**
|
||||
* Return this category's parent; return null if this is the root category.
|
||||
* @return the category.
|
||||
*/
|
||||
public abstract Category getParent();
|
||||
|
||||
|
@ -146,33 +155,36 @@ public interface Category extends Comparable<Category> {
|
|||
|
||||
/**
|
||||
* Get the fully qualified name for this category.
|
||||
* @return the name.
|
||||
*/
|
||||
public abstract String getCategoryPathName();
|
||||
|
||||
/**
|
||||
* Get the root category.
|
||||
* @return the category.
|
||||
*/
|
||||
public abstract Category getRoot();
|
||||
|
||||
/**
|
||||
* Get the data type manager associated with this category.
|
||||
* @return the manager.
|
||||
*/
|
||||
public abstract DataTypeManager getDataTypeManager();
|
||||
|
||||
/**
|
||||
* Move a data type into this category
|
||||
* Move a data type into this category.
|
||||
*
|
||||
* @param type data type to be moved
|
||||
* @param handler the handler to call if there is a data type conflict
|
||||
* @throws DataTypeDependencyException
|
||||
* @param type data type to be moved.
|
||||
* @param handler the handler to call if there is a data type conflict.
|
||||
* @throws DataTypeDependencyException if a disallowed dependency is created during the move.
|
||||
*/
|
||||
public abstract void moveDataType(DataType type, DataTypeConflictHandler handler)
|
||||
throws DataTypeDependencyException;
|
||||
|
||||
/**
|
||||
* Remove a datatype from this category
|
||||
* Remove a datatype from this category.
|
||||
*
|
||||
* @param type data type to be removed
|
||||
* @param type data type to be removed.
|
||||
* @param monitor monitor of progress in case operation takes a long time.
|
||||
* @return true if the data type was found in this category and successfully removed.
|
||||
*/
|
||||
|
@ -180,6 +192,7 @@ public interface Category extends Comparable<Category> {
|
|||
|
||||
/**
|
||||
* Get the ID for this category.
|
||||
* @return the ID.
|
||||
*/
|
||||
public long getID();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue