diff --git a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_ApplyMarkupDialog.png b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_ApplyMarkupDialog.png index 72d418cb18..01496cb332 100644 Binary files a/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_ApplyMarkupDialog.png and b/Ghidra/Features/VersionTracking/src/main/help/help/topics/VersionTrackingPlugin/images/VTOptions_ApplyMarkupDialog.png differ diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/DataTypeMarkupType.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/DataTypeMarkupType.java index d7248e4c6a..b649c81698 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/DataTypeMarkupType.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/api/markuptype/DataTypeMarkupType.java @@ -25,6 +25,7 @@ import ghidra.feature.vt.api.stringable.DataTypeStringable; import ghidra.feature.vt.api.util.Stringable; import ghidra.feature.vt.api.util.VersionTrackingApplyException; import ghidra.feature.vt.gui.util.VTMatchApplyChoices; +import ghidra.feature.vt.gui.util.VTMatchApplyChoices.DataTypeConflictChoices; import ghidra.feature.vt.gui.util.VTMatchApplyChoices.ReplaceDataChoices; import ghidra.feature.vt.gui.util.VTOptionDefines; import ghidra.framework.options.Options; @@ -206,7 +207,8 @@ public class DataTypeMarkupType extends VTMarkupType { } private boolean setDataType(Program program, Address startAddress, DataType dataType, - int dataLength, VTMatchApplyChoices.ReplaceDataChoices replaceChoice) + int dataLength, VTMatchApplyChoices.ReplaceDataChoices replaceChoice, + DataTypeConflictHandler conflictHandler) throws CodeUnitInsertionException, VersionTrackingApplyException { Listing listing = program.getListing(); @@ -260,15 +262,26 @@ public class DataTypeMarkupType extends VTMarkupType { } if (replaceFirstOnly && hasOtherDefinedData) { - // Just return since we are only replacing first data and this has some defined + // Just return since we are only replacing first data and this has some defined // data that would be overwritten following the first data in the destination. return false; } + /* + Note: the resolve may add a .conflict type. That will not be removed if an exception is + thrown. Also if an unapply is executed, .conflict types will not be removed. For now, + we leave this up to the user to fix, should they care. Trying to remove .conflict types + during an unapply may have unintended side-effects if the user added new uses of that + conflict type. + */ + + ProgramBasedDataTypeManager dtm = program.getDataTypeManager(); + DataType resolvedDataType = dtm.resolve(dataType, conflictHandler); + listing.clearCodeUnits(startAddress, endAddress, false); try { - listing.createData(startAddress, dataType, dataLength); + listing.createData(startAddress, resolvedDataType, dataLength); } catch (CodeUnitInsertionException e) { tryToRestoreOriginalData(listing, startAddress, originalDataType, originalDataLength); @@ -334,9 +347,17 @@ public class DataTypeMarkupType extends VTMarkupType { sourceDataLength = sourceData.getLength(); } + DataTypeConflictChoices conflictChoice = markupOptions.getEnum( + VTOptionDefines.DATA_TYPE_CONFLICT_HANDLER, + VTOptionDefines.DEFAULT_OPTION_FOR_DATA_TYPE_CONFLICT_HANDLER); + DataTypeConflictHandler conflictHandler = switch (conflictChoice) { + case USE_EXISTING -> DataTypeConflictHandler.KEEP_HANDLER; + case RENAME_AND_ADD -> DataTypeConflictHandler.DEFAULT_HANDLER; + }; + try { return setDataType(destinationProgram, destinationAddress, sourceDataType, - sourceDataLength, replaceChoice); + sourceDataLength, replaceChoice, conflictHandler); } catch (CodeUnitInsertionException e) { throw new VersionTrackingApplyException(getApplyFailedMessage(sourceAddress, @@ -404,7 +425,8 @@ public class DataTypeMarkupType extends VTMarkupType { try { setDataType(destinationProgram, destinationAddress, originalDataType, - originalDataLength, VTMatchApplyChoices.ReplaceDataChoices.REPLACE_ALL_DATA); + originalDataLength, VTMatchApplyChoices.ReplaceDataChoices.REPLACE_ALL_DATA, + DataTypeConflictHandler.DEFAULT_HANDLER); } catch (CodeUnitInsertionException e) { throw new VersionTrackingApplyException("Couldn't unapply data type markup @ " + diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/ApplyMarkupPropertyEditor.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/ApplyMarkupPropertyEditor.java index e48653a8e6..f2a058f677 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/ApplyMarkupPropertyEditor.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/ApplyMarkupPropertyEditor.java @@ -51,6 +51,9 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { // help tooltips private static final String DATA_MATCH_DATA_TYPE_TOOLTIP = "The apply action for the data type on a data match when performing bulk apply operations"; + private static final String DATA_TYPE_CONFLICT_HANDLER_TOOLTIP = + "How to resolve a conflict when the data type being applied already exists " + + "but differs in the destination program"; private static final String LABELS_TOOLTIP = "The apply action for labels when performing bulk apply operations"; private static final String FUNCTION_NAME_TOOLTIP = @@ -116,6 +119,7 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { private JComponent editorComponent; private JLabel dataMatchDataTypeLabel; + private JLabel dataTypeConflictHandlerLabel; private JLabel functionNameLabel; private JLabel functionSignatureLabel; private JLabel useFunctionNamespaceLabel; @@ -137,6 +141,7 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { private JLabel postCommentsLabel; private JComboBox> dataMatchDataTypeComboBox; + private JComboBox> dataTypeConflictHandlerComboBox; private JComboBox> functionNameComboBox; private JComboBox> functionSignatureComboBox; private JCheckBox useFunctionNamespaceCheckBox; @@ -418,6 +423,8 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { panel.add(dataMatchDataTypeLabel); panel.add(dataMatchDataTypeComboBox); + panel.add(dataTypeConflictHandlerLabel); + panel.add(dataTypeConflictHandlerComboBox); panel.add(labelsLabel); panel.add(labelsComboBox); panel.add(functionNameLabel); @@ -439,6 +446,10 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { dataMatchDataTypeLabel = new GDLabel("Data Match Data Type", SwingConstants.RIGHT); dataMatchDataTypeLabel.setToolTipText(DATA_MATCH_DATA_TYPE_TOOLTIP); + dataTypeConflictHandlerLabel = + new GDLabel("Data Type Conflict Handler", SwingConstants.RIGHT); + dataTypeConflictHandlerLabel.setToolTipText(DATA_TYPE_CONFLICT_HANDLER_TOOLTIP); + labelsLabel = new GDLabel("Labels", SwingConstants.RIGHT); labelsLabel.setToolTipText(LABELS_TOOLTIP); @@ -460,6 +471,10 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { DEFAULT_OPTION_FOR_DATA_MATCH_DATA_TYPE); dataMatchDataTypeComboBox.setToolTipText(DATA_MATCH_DATA_TYPE_TOOLTIP); + dataTypeConflictHandlerComboBox = createComboBox(VTOptionDefines.DATA_TYPE_CONFLICT_HANDLER, + DEFAULT_OPTION_FOR_DATA_TYPE_CONFLICT_HANDLER); + dataTypeConflictHandlerComboBox.setToolTipText(DATA_TYPE_CONFLICT_HANDLER_TOOLTIP); + labelsComboBox = createComboBox(VTOptionDefines.LABELS, DEFAULT_OPTION_FOR_LABELS); labelsComboBox.setToolTipText(LABELS_TOOLTIP); @@ -556,6 +571,10 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { (ReplaceDataChoices) dataMatchDataTypeComboBox.getSelectedItem(); options.setEnum(DATA_MATCH_DATA_TYPE, dataMatchDataTypeChoice); + DataTypeConflictChoices dataTypeConflictHandlerChoice = + (DataTypeConflictChoices) dataTypeConflictHandlerComboBox.getSelectedItem(); + options.setEnum(DATA_TYPE_CONFLICT_HANDLER, dataTypeConflictHandlerChoice); + LabelChoices labelsChoice = (LabelChoices) labelsComboBox.getSelectedItem(); options.setEnum(LABELS, labelsChoice); @@ -666,6 +685,12 @@ public class ApplyMarkupPropertyEditor implements OptionsEditor { dataMatchDataTypeComboBox.setSelectedItem(dataMatchDataTypeChoice); } + DataTypeConflictChoices dataTypeConflictHandlerChoice = options + .getEnum(DATA_TYPE_CONFLICT_HANDLER, DEFAULT_OPTION_FOR_DATA_TYPE_CONFLICT_HANDLER); + if (dataTypeConflictHandlerChoice != dataTypeConflictHandlerComboBox.getSelectedItem()) { + dataTypeConflictHandlerComboBox.setSelectedItem(dataTypeConflictHandlerChoice); + } + LabelChoices labelsChoice = options.getEnum(LABELS, DEFAULT_OPTION_FOR_LABELS); if (labelsChoice != labelsComboBox.getSelectedItem()) { labelsComboBox.setSelectedItem(labelsChoice); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java index a2e2a95ab7..a4e0dd6717 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/provider/matchtable/VTMatchTableProvider.java @@ -686,6 +686,11 @@ public class VTMatchTableProvider extends ComponentProviderAdapter null, "The default apply action for the data type on a data match when performing bulk apply operations"); + vtOptions.registerOption(DATA_TYPE_CONFLICT_HANDLER, + DEFAULT_OPTION_FOR_DATA_TYPE_CONFLICT_HANDLER, null, + "How to resolve a conflict when the data type being applied already exists but " + + "differs in the destination program"); + vtOptions.registerOption(LABELS, DEFAULT_OPTION_FOR_LABELS, null, "The default apply action for labels when performing bulk apply operations"); diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/VTMatchApplyChoices.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/VTMatchApplyChoices.java index 9888902210..cdbb05156c 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/VTMatchApplyChoices.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/VTMatchApplyChoices.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -239,4 +239,20 @@ public class VTMatchApplyChoices { } } + public static enum DataTypeConflictChoices { + USE_EXISTING("Use Existing Data Type"), + RENAME_AND_ADD("Rename New Data Type"); + + private String optionDisplayString; + + private DataTypeConflictChoices(String optionDisplayString) { + this.optionDisplayString = optionDisplayString; + } + + @Override + public String toString() { + return optionDisplayString; + } + } + } diff --git a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/VTOptionDefines.java b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/VTOptionDefines.java index 7cf9c9238a..41e293751d 100644 --- a/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/VTOptionDefines.java +++ b/Ghidra/Features/VersionTracking/src/main/java/ghidra/feature/vt/gui/util/VTOptionDefines.java @@ -36,6 +36,8 @@ public class VTOptionDefines { public static boolean DEFAULT_OPTION_FOR_PARAMETER_NAMES_REPLACE_IF_SAME_PRIORITY = false; public static ReplaceDataChoices DEFAULT_OPTION_FOR_DATA_MATCH_DATA_TYPE = ReplaceDataChoices.REPLACE_UNDEFINED_DATA_ONLY; + public static DataTypeConflictChoices DEFAULT_OPTION_FOR_DATA_TYPE_CONFLICT_HANDLER = + DataTypeConflictChoices.USE_EXISTING; public static FunctionNameChoices DEFAULT_OPTION_FOR_FUNCTION_NAME = FunctionNameChoices.ADD_AS_PRIMARY; public static FunctionSignatureChoices DEFAULT_OPTION_FOR_FUNCTION_SIGNATURE = @@ -84,6 +86,8 @@ public class VTOptionDefines { public static final String POST_COMMENT = APPLY_MARKUP_OPTIONS_NAME + ".Post Comment"; public static final String DATA_MATCH_DATA_TYPE = APPLY_MARKUP_OPTIONS_NAME + ".Data Match Data Type"; + public static final String DATA_TYPE_CONFLICT_HANDLER = APPLY_MARKUP_OPTIONS_NAME + + ".Data Type Conflict Handler"; public static final String FUNCTION_SIGNATURE = APPLY_MARKUP_OPTIONS_NAME + ".Function Signature"; public static final String CALLING_CONVENTION = APPLY_MARKUP_OPTIONS_NAME + diff --git a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/markupitem/DataTypeMarkupItemTest.java b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/markupitem/DataTypeMarkupItemTest.java index 921199ff38..eb8327870d 100644 --- a/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/markupitem/DataTypeMarkupItemTest.java +++ b/Ghidra/Features/VersionTracking/src/test.slow/java/ghidra/feature/vt/api/markupitem/DataTypeMarkupItemTest.java @@ -25,9 +25,11 @@ import org.junit.Test; import ghidra.feature.vt.api.main.*; import ghidra.feature.vt.api.markuptype.DataTypeMarkupType; +import ghidra.feature.vt.gui.util.VTMatchApplyChoices.DataTypeConflictChoices; import ghidra.feature.vt.gui.util.VTMatchApplyChoices.ReplaceDataChoices; import ghidra.feature.vt.gui.util.VTOptionDefines; import ghidra.framework.options.ToolOptions; +import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.model.address.Address; import ghidra.program.model.data.*; import ghidra.program.model.lang.*; @@ -85,6 +87,57 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest { doTestFindAndApplyMarkupItem(validator); } + @Test + public void testReplace_ConflictInDtm_ChooseExistingType() throws Exception { + + Address srcAddr = addr("0x010074e6", sourceProgram); + StructureDataType coolStruct1 = createCoolStruct1(); + Data sourceData = setDataType(sourceProgram, srcAddr, coolStruct1, coolStruct1.getLength()); + + // Add a type to the destination that has the same data type path, but is not equivalent + StructureDataType coolStruct2 = createCoolStruct2(); + addToDestinationDtm(coolStruct2); + + // make room for the type to be applied + Address destAddr = addr("0x010074e6", destinationProgram); + clear(destinationProgram, destAddr, coolStruct2.getLength()); + + StringDataType destDt = new StringDataType(); + Data destData = setDataType(destinationProgram, destAddr, destDt, 4); + + DataTypeValidator validator = new DataTypeValidator(sourceData, destData, + ReplaceDataChoices.REPLACE_FIRST_DATA_ONLY); + validator.setConflictChoice(DataTypeConflictChoices.USE_EXISTING); + validator.setKeepExistingType(true); + doTestFindAndApplyMarkupItem(validator); + + assertConflictTypeInDestinationDtm(coolStruct1, false); + } + + @Test + public void testReplace_ConflictInDtm_ChooseRenameAndAdd() throws Exception { + + Address srcAddr = addr("0x010074e6", sourceProgram); + StructureDataType coolStruct1 = createCoolStruct1(); + Data sourceData = setDataType(sourceProgram, srcAddr, coolStruct1, coolStruct1.getLength()); + + // Add a type to the destination that has the same data type path, but is not equivalent + StructureDataType coolStruct2 = createCoolStruct2(); + addToDestinationDtm(coolStruct2); + + Address destAddr = addr("0x010074e6", destinationProgram); + StringDataType destDt = new StringDataType(); + Data destData = setDataType(destinationProgram, destAddr, destDt, 4); + setDataType(destinationProgram, destAddr.add(4), destDt, 6); + + DataTypeValidator validator = new DataTypeValidator(sourceData, destData, + ReplaceDataChoices.REPLACE_FIRST_DATA_ONLY); + validator.setConflictChoice(DataTypeConflictChoices.RENAME_AND_ADD); + doTestFindAndApplyMarkupItem(validator); + + assertConflictTypeInDestinationDtm(coolStruct1, true); + } + @Test public void testReplaceLargerWithSmaller() throws Exception { @@ -305,6 +358,38 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest { doTestFindAndApplyMarkupItem_NoEffect(validator); } + @Test + public void testRejectedApplyDoesNotMutateDestinationDataTypeManager() throws Exception { + + Address sourceAddress = addr("0x010074e6", sourceProgram); // LoadCursorW + StructureDataType sourceDataType = new StructureDataType("RejectedApplyStruct", 0); + sourceDataType.add(new DWordDataType()); + Data sourceData = + setDataType(sourceProgram, sourceAddress, sourceDataType, sourceDataType.getLength()); + + Address destinationAddress = addr("0x010074e6", destinationProgram); // LoadCursorW + StringDataType destinationDataType = new StringDataType(); + Data destinationData = + setDataType(destinationProgram, destinationAddress, destinationDataType, 4); // Get "Load". + setDataType(destinationProgram, destinationAddress.add(4), destinationDataType, 6); // Get "Cursor". + + DataTypeManager destinationDTM = destinationProgram.getDataTypeManager(); + assertNull("Test setup invalid - destination should not already have this data type", + destinationDTM.getDataType(sourceDataType.getCategoryPath(), + sourceDataType.getName())); + + DataTypeValidator validator = new DataTypeValidator(sourceData, destinationData, + ReplaceDataChoices.REPLACE_UNDEFINED_DATA_ONLY); + validator.setConflictChoice(DataTypeConflictChoices.RENAME_AND_ADD); + doTestFindAndApplyMarkupItem_NoEffect(validator); + + assertNull( + "Rejected apply must not add the source data type to the destination data type " + + "manager", + destinationDTM.getDataType(sourceDataType.getCategoryPath(), + sourceDataType.getName())); + } + @Test public void testReplaceUndefinedOnlyWithLargerWhenBlockedByInstruction() throws Exception { @@ -413,6 +498,48 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest { // Private Methods //================================================================================================== + private void addToDestinationDtm(StructureDataType struct) { + ProgramDataTypeManager destDtm = destinationProgram.getDataTypeManager(); + tx(destDtm, () -> { + destDtm.resolve(struct, null); + }); + + DataTypePath dtp = struct.getDataTypePath(); + DataType resolvedDtm = destDtm.getDataType(dtp); + assertNotNull(resolvedDtm); + } + + private StructureDataType createCoolStruct1() { + StructureDataType struct = new StructureDataType("CoolStructure", 0); + struct.add(new DWordDataType()); + return struct; + } + + // 'CoolStructure' that is slightly different than that made in createCoolStruct1() + private StructureDataType createCoolStruct2() { + StructureDataType struct = new StructureDataType("CoolStructure", 0); + struct.add(new DWordDataType()); + struct.add(new DWordDataType()); + return struct; + } + + private void assertConflictTypeInDestinationDtm(DataType dt, boolean expectConflict) { + + DataTypePath dtp = dt.getDataTypePath(); + String name = dt.getName() + ".conflict"; + CategoryPath cp = dtp.getCategoryPath(); + DataTypePath conflictPath = new DataTypePath(cp, name); + + ProgramDataTypeManager destDtm = destinationProgram.getDataTypeManager(); + DataType conflictType = destDtm.getDataType(conflictPath); + if (expectConflict) { + assertNotNull(conflictType); + } + else { + assertNull(conflictType); + } + } + private Structure createGadgetStruct() { Structure gadgetStruct = new StructureDataType("Gadget", 0); @@ -428,14 +555,13 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest { private Data setDataType(Program program, Address address, DataType dataType, int length) { - int txID = program.startTransaction("Change Data Type"); - boolean commit = false; - try { + return tx(program, () -> { Listing listing = program.getListing(); Data sourceData = listing.getDataAt(address); if (sourceData == null) { return null; } + listing.clearCodeUnits(address, sourceData.getMaxAddress(), false); Data data; if (length > 0) { @@ -444,23 +570,20 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest { else { data = listing.createData(address, dataType); } - commit = true; return data; - } - catch (Exception e) { - // Commit is false by default so nothing else to do. - return null; - } - finally { - program.endTransaction(txID, commit); - } + }); + } + + private void clear(Program p, Address a, int length) { + tx(p, () -> { + Listing listing = p.getListing(); + listing.clearCodeUnits(a, a.add(length), false); + }); } private Instruction createInstruction(Program program, Address atAddress) { - int txID = program.startTransaction("Create Instruction"); - boolean commit = false; - try { + return tx(program, () -> { Listing listing = program.getListing(); Memory memory = program.getMemory(); MemBuffer buf = new DumbMemBufferImpl(memory, atAddress); @@ -469,16 +592,8 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest { InstructionPrototype proto = program.getLanguage().parse(buf, context, false); Instruction createdInstruction = listing.createInstruction(atAddress, proto, buf, context, 0); - commit = true; return createdInstruction; - } - catch (Exception e) { - // Commit is false by default so nothing else to do. - return null; - } - finally { - program.endTransaction(txID, commit); - } + }); } //================================================================================================== @@ -494,6 +609,8 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest { private int sourceLength; private int originalDestinationLength; private ReplaceDataChoices dataTypeChoice; + private DataTypeConflictChoices conflictChoice; + private boolean keepExistingType; DataTypeValidator(Data sourceData, Data destinationData, ReplaceDataChoices dataTypeChoice) { @@ -510,6 +627,14 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest { this.originalDestinationLength = destinationData.getLength(); } + void setConflictChoice(DataTypeConflictChoices conflictChoice) { + this.conflictChoice = conflictChoice; + } + + void setKeepExistingType(boolean keep) { + this.keepExistingType = keep; + } + @Override protected Address getDestinationApplyAddress() { return getDestinationMatchAddress(); @@ -549,10 +674,19 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest { Data currentDestinationData = listing.getDataAt(getDestinationApplyAddress()); DataType currentDestinationDataType = currentDestinationData.getDataType(); int currentDestinationLength = currentDestinationData.getLength(); - assertTrue("Data type was not applied", - sourceDataType.isEquivalent(currentDestinationDataType)); - assertTrue("Data type was not set to the source data type's size", - sourceLength == currentDestinationLength); + + if (keepExistingType) { + // guilty knowledge: keeping the existing type is used when the types have the same + // data type path, but are not equivalent + assertEquals(sourceDataType.getDataTypePath(), + currentDestinationDataType.getDataTypePath()); + } + else { + assertTrue("Data type was not applied", + sourceDataType.isEquivalent(currentDestinationDataType)); + assertTrue("Data type was not set to the source data type's size", + sourceLength == currentDestinationLength); + } } @Override @@ -571,6 +705,9 @@ public class DataTypeMarkupItemTest extends AbstractVTMarkupItemTest { public ToolOptions getOptions() { ToolOptions vtOptions = super.getOptions(); vtOptions.setEnum(VTOptionDefines.DATA_MATCH_DATA_TYPE, dataTypeChoice); + if (conflictChoice != null) { + vtOptions.setEnum(VTOptionDefines.DATA_TYPE_CONFLICT_HANDLER, conflictChoice); + } return vtOptions; }