diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/EnumEditor.htm b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/EnumEditor.htm
index 361f6f127d..75944bace4 100644
--- a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/EnumEditor.htm
+++ b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/EnumEditor.htm
@@ -25,17 +25,24 @@
- The Name column in the table is the name of the enum entry.
- The Value column in the table is the value of the enum entry.
+ - The Comment column in the table is the comment for the enum entry.
+
+
- The Name field below the table shows the name of the enum.
Edit this field to change the name.
- The Description field shows a short description for the enum;
edit this field to update the description.
- - The Category field shows where the enum resides which corresponds to the folder you were selecting when creating the enum;
- the field is not editable, however, you can move it using the Data Type Manager after you have created it if you want to move it.
+
- The Category field shows where the enum resides which corresponds to the folder
+ you were selecting when creating the enum;
+ the field is not editable, however, you can move it using the Data Type Manager after
+ you have created it if you want to move it.
- The Size field shows the number of bytes required when you apply
- the enum. To edit this field, use the dropdown menu to choose a new size. Note: If you are applying an enum to a data definition and do not see expected results in the decompiler it is probably because the size is incorrect.
+ the enum. To edit this field, use the drop-down menu to choose a new size. Note: If you
+ are applying an enum to a data definition and do not see expected results in the
+ decompiler it is probably because the size is incorrect.
When you make any change to the enum, the
diff --git a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png
index 3dfef311d2..4eceff2d80 100644
Binary files a/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png and b/Ghidra/Features/Base/src/main/help/help/topics/DataTypeEditors/images/EnumEditor.png differ
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java
index f2bbdd6e9b..30a42453f5 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/merge/datatypes/DataTypePanel.java
@@ -246,6 +246,30 @@ class DataTypePanel extends JPanel {
insertLength(comp);
}
+ private class EnumEntry implements Comparable {
+
+ private final String name;
+ private final long value;
+ private final String comment;
+
+ EnumEntry(String name, long value, String comment) {
+ this.name = name;
+ this.value = value;
+ this.comment = comment;
+
+ }
+
+ @Override
+ public int compareTo(EnumEntry o) {
+ int c = Long.compare(value, o.value);
+ if (c == 0) {
+ c = name.compareTo(o.name);
+ }
+ return c;
+ }
+
+ }
+
private void formatEnumText(Enum enuum) {
formatSourceArchive(enuum);
formatPath(enuum);
@@ -254,28 +278,41 @@ class DataTypePanel extends JPanel {
StringBuffer sb = new StringBuffer();
- String[] names = enuum.getNames();
- int maxLength = 0;
- for (String name : names) {
- if (name.length() > maxLength) {
- maxLength = name.length();
- }
- }
- long[] values = enuum.getValues();
- Arrays.sort(values);
+ int maxNameLength = 0;
+ int maxValueLength = 0;
- for (int i = 0; i < values.length; i++) {
- String name = enuum.getName(values[i]);
- name = pad(name, maxLength);
- sb.append(" " + name + " = 0x" + Long.toHexString(values[i]) + " ");
- if (i < values.length - 1) {
- sb.append("\n");
- }
+ String[] names = enuum.getNames();
+ EnumEntry[] entries = new EnumEntry[names.length];
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ EnumEntry entry = new EnumEntry(name, enuum.getValue(name), enuum.getComment(name));
+ entries[i] = entry;
+ maxNameLength = Math.max(maxNameLength, name.length());
+ String valStr = Long.toHexString(entry.value);
+ maxValueLength = Math.max(maxValueLength, valStr.length());
}
- sb.append("\n }\n");
+ Arrays.sort(entries);
+
+ for (EnumEntry entry : entries) {
+ renderEnumEntry(entry, maxNameLength, maxValueLength);
+ }
+ sb.append("}\n");
insertString(sb.toString(), contentAttrSet);
}
+ private void renderEnumEntry(EnumEntry entry, int maxNameLength, int maxValueLength) {
+ String name = entry.name;
+ name = pad(name, maxNameLength);
+ String valStr = Long.toHexString(entry.value);
+ valStr = pad(valStr, maxValueLength);
+ insertString(" " + name, fieldNameAttrSet);
+ insertString(" = 0x" + valStr, contentAttrSet);
+ if (entry.comment != null) {
+ insertString(" " + entry.comment, commentAttrSet);
+ }
+ insertString("\n", contentAttrSet);
+ }
+
private void formatTypeDefText(TypeDef td) {
formatSourceArchive(td);
formatPath(td);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEntry.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEntry.java
index cff8937317..955a41cc63 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEntry.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumEntry.java
@@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
- * REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +15,8 @@
*/
package ghidra.app.plugin.core.datamgr.editor;
+import generic.json.Json;
+
public class EnumEntry {
private String name;
private long value;
@@ -50,4 +51,9 @@ public class EnumEntry {
public void setComment(String newComment) {
this.comment = newComment;
}
+
+ @Override
+ public String toString() {
+ return Json.toString(this);
+ }
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumTableModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumTableModel.java
index ce352784a3..3fc52763c6 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumTableModel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/datamgr/editor/EnumTableModel.java
@@ -266,8 +266,8 @@ class EnumTableModel extends AbstractSortedTableModel {
}
private long findNextValue(int afterRow) {
- if (enumEntryList.size() == 0) {
- return new Long(0);
+ if (enumEntryList.isEmpty()) {
+ return 0;
}
if (afterRow < 0 || afterRow >= enumEntryList.size()) {
afterRow = 0;
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ArchiveConverterPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ArchiveConverterPlugin.java
deleted file mode 100644
index 312acc404b..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ArchiveConverterPlugin.java
+++ /dev/null
@@ -1,698 +0,0 @@
-/* ###
- * 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.
- */
-/*
- * Created on Aug 3, 2005
- */
-package ghidra.app.plugin.prototype.dataArchiveUtilities;
-
-import java.awt.Component;
-import java.io.*;
-import java.util.*;
-
-import docking.ActionContext;
-import docking.action.DockingAction;
-import docking.action.MenuData;
-import docking.tool.ToolConstants;
-import docking.widgets.filechooser.GhidraFileChooser;
-import ghidra.MiscellaneousPluginPackage;
-import ghidra.app.context.ListingActionContext;
-import ghidra.app.plugin.PluginCategoryNames;
-import ghidra.app.plugin.ProgramPlugin;
-import ghidra.app.util.xml.DataTypesXmlMgr;
-import ghidra.framework.plugintool.PluginInfo;
-import ghidra.framework.plugintool.PluginTool;
-import ghidra.framework.plugintool.util.*;
-import ghidra.framework.preferences.Preferences;
-import ghidra.program.model.data.*;
-import ghidra.program.model.data.Enum;
-import ghidra.program.model.listing.Program;
-import ghidra.util.Msg;
-import ghidra.util.filechooser.ExtensionFileFilter;
-import ghidra.util.task.*;
-
-//@formatter:off
-@PluginInfo(
- status = PluginStatus.STABLE,
- packageName = MiscellaneousPluginPackage.NAME,
- category = PluginCategoryNames.CODE_VIEWER,
- shortDescription = "Convert data archives",
- description = "This plugin allows the user to convert GSL-generated archives back and forth to Ghidra data archives."
-)
-//@formatter:on
-public class ArchiveConverterPlugin extends ProgramPlugin {
-
- private static final String IMPORT_EXPORT_GROUP = "Import/Export";
- final static String CONVERT_DATA = "Convert GSL- to data-archive";
- final static String GSL_ARCHIVE_DIR = "GSL Archive Directory";
- final static String GDT_ARCHIVE_DIR = "GDT Archive Directory";
- final static String ALIGNMENT_TAG = "added for alignment";
-
- final static String WRITE_GSL = "Write data-archive in GSL Format";
-
- private GhidraFileChooser fileChooser;
- private TypedefDataType dataUI;
- private Hashtable dataTypes = new Hashtable<>();
-
- /**
- * @param id
- * @param plugintool
- * @param consumeLocationChange
- * @param consumeSelectionChange
- */
- public ArchiveConverterPlugin(PluginTool plugintool) {
- super(plugintool, false, false);
- createActions();
- }
-
- public Program getProgram() {
- return currentProgram;
- }
-
- @Override
- public void dispose() {
- super.dispose();
- }
-
- /**
- *
- */
- private void createActions() {
- DockingAction parseAction = new DockingAction(CONVERT_DATA, getName()) {
- @Override
- public void actionPerformed(ActionContext context) {
- File gslArchive =
- chooseFile(tool.getToolFrame(), "Select GSL archive", GSL_ARCHIVE_DIR, "gsl");
- new TaskLauncher(new GSLParserTask(tool, gslArchive), tool.getToolFrame());
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- return context.getContextObject() instanceof ListingActionContext;
- }
- };
- String[] menuPath = { ToolConstants.MENU_FILE, "Parse GSL Archive..." };
- parseAction.setMenuBarData(new MenuData(menuPath, IMPORT_EXPORT_GROUP));
- parseAction.setEnabled(true);
- tool.addAction(parseAction);
-
- DockingAction writeGslAction = new DockingAction(WRITE_GSL, getName()) {
- @Override
- public void actionPerformed(ActionContext context) {
- File gslInfile = chooseFile(tool.getToolFrame(), "Select input file",
- GDT_ARCHIVE_DIR, FileDataTypeManager.SUFFIX);
- File gslOutfile =
- chooseFile(tool.getToolFrame(), "Select output file", GSL_ARCHIVE_DIR, "gsl");
- new TaskLauncher(new GSLWriterTask(tool, gslInfile, gslOutfile),
- tool.getToolFrame());
- }
-
- @Override
- public boolean isEnabledForContext(ActionContext context) {
- return context.getContextObject() instanceof ListingActionContext;
- }
- };
- String[] menuPath2 = { ToolConstants.MENU_FILE, "Write GSL Archive..." };
- writeGslAction.setMenuBarData(new MenuData(menuPath2, IMPORT_EXPORT_GROUP));
- writeGslAction.setEnabled(true);
- tool.addAction(writeGslAction);
- }
-
- private class GSLParserTask extends Task {
- PluginTool myTool;
- File gslArchive;
-
- public GSLParserTask(PluginTool tool, File gslArchive) {
- super("GSL Archive Parser", true, true, false);
- this.gslArchive = gslArchive;
- this.myTool = tool;
- }
-
- @Override
- public void run(TaskMonitor monitor) {
- int lineCount = 0;
- try {
- if (gslArchive != null) {
- String gslName = gslArchive.getName();
- String gslNameWithPath = gslArchive.getAbsolutePath();
- monitor.setMessage("Parsing " + gslName);
- FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(
- new File(gslNameWithPath + FileDataTypeManager.SUFFIX));
- int id = dtMgr.startTransaction("process archive");
- try {
- addPrimitives(dtMgr);
-
- BufferedReader reader = new BufferedReader(new FileReader(gslArchive));
- while (reader.readLine() != null) {
- lineCount++;
- }
- reader.close();
- monitor.initialize(lineCount);
-
- reader = new BufferedReader(new FileReader(gslArchive));
- String line;
- lineCount = 0;
- DataType dt = null;
- while (((line = reader.readLine()) != null) && !monitor.isCancelled()) {
- try {
- dt = parseLine(dtMgr, line);
- if (dt != null) {
- addDataType(dtMgr, dataTypes, dt);
- }
- if (lineCount % 100 == 0) {
- monitor.setProgress(lineCount);
- }
- lineCount++;
- }
- catch (Exception e) {
- Msg.error(this,
- "Error in " + name + " at line " + lineCount + " of " +
- gslArchive.getName() +
- "...possibly an attempt to redefine a Ghidra primitive",
- e);
- }
- }
- reader.close();
-
- }
- finally {
- dtMgr.endTransaction(id, true);
- }
-
- monitor.setMessage("Checking for parser errors");
- searchForErrors(dtMgr);
-
- monitor.setMessage("Writing XML file");
- try {
- DataTypesXmlMgr.writeAsXMLForDebug(dtMgr, gslNameWithPath);
- }
- catch (Exception e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
-
- try {
- dtMgr.save();
- }
- catch (Exception e) {
- Msg.showError(this, myTool.getToolFrame(), "GSL Archive Parser",
- gslNameWithPath + ".gdt already exists - not overwritten", e);
- }
- dtMgr.close();
- }
- }
- catch (Exception e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
- return;
- }
- }
-
- private class GSLWriterTask extends Task {
- File gslInfile;
- File gslOutfile;
-
- public GSLWriterTask(PluginTool tool, File gslInfile, File gslOutfile) {
- super("GSL Archive Writer", true, false, false);
- this.gslInfile = gslInfile;
- this.gslOutfile = gslOutfile;
- }
-
- @Override
- public void run(TaskMonitor monitor) {
- if (gslInfile != null) {
- DataTypeManager dtMgr = null;
- try {
- dtMgr = FileDataTypeManager.openFileArchive(gslInfile, false);
- }
- catch (Exception e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
- if (dtMgr != null) {
- try {
- monitor.setMessage("Writing " + gslOutfile.getName());
- writeAsGsl(gslOutfile, dtMgr, monitor);
- }
- finally {
- dtMgr.close();
- }
- }
- }
- return;
- }
- }
-
- private DataType parseLine(FileDataTypeManager dtMgr, String line) {
- DataType dt = null;
- int index;
- // "$$" doesn't get parsed correctly, so replace it with "$ $"
- while ((index = line.indexOf("$$")) >= 0) {
- line = line.substring(0, index + 1) + "noname" + line.substring(index + 1);
- }
- while ((index = line.indexOf("//")) >= 0) {
- line = line.substring(0, index) + line.substring(index + 1);
- }
-
- StringTokenizer tokenizer = new StringTokenizer(line, "$");
- String fieldId = tokenizer.nextToken();
- String complexName = tokenizer.nextToken();
-
- ComplexName cName = new ComplexName(complexName);
- String myName = cName.getName();
-
- if ((fieldId.compareToIgnoreCase("STRUCT") == 0) ||
- (fieldId.compareToIgnoreCase("UNION") == 0)) {
- tokenizer.nextToken(); // size
- tokenizer.nextToken(); // alignment
- if (fieldId.compareToIgnoreCase("STRUCT") == 0) {
- dt = new StructureDataType(cName.getCategoryPath(), myName, 0);
- }
- else {
- dt = new UnionDataType(cName.getCategoryPath(), myName);
- }
- addDataType(dtMgr, dataTypes, dt);
-
- int lastOffset = dt.getLength();
- DataType member;
- while (tokenizer.hasMoreElements()) {
- String fieldName = tokenizer.nextToken();
- String fieldType = tokenizer.nextToken();
- String fieldOffset = tokenizer.nextToken();
- String fieldSize = tokenizer.nextToken();
- String fieldAlign = tokenizer.nextToken();
-
- int align = valueOf(fieldAlign);
- int fSize = valueOf(fieldSize);
- int fOff = valueOf(fieldOffset);
- ComplexName fType = new ComplexName(fieldType);
- member = fType.getDataType(dtMgr, dataTypes);
-
- try {
- // If we don't know what this is, make something up
- if ((member == null) || (member.getLength() < 0)) {
- member = genUIData(dtMgr, fType, fOff - dt.getLength());
- fSize = member.getLength();
- }
- // Zero length fields are OK if they're the last field (again we fake the size)
- if (member.getLength() == 0) {
- if (!tokenizer.hasMoreElements()) {
- fType.count = 1;
- member = fType.getDataType(dtMgr, dataTypes);
- fSize = member.getLength();
- }
- }
-
- if ((dt instanceof Structure) && ((fOff < dt.getLength()) ||
- ((fOff >= dt.getLength()) && (fSize < member.getLength())))) {
- if (fOff >= dt.getLength()) {
- lastOffset = dt.getLength();
- ((StructureDataType) dt).add(member, member.getLength(),
- "_bit_fields_" + dt.getLength(), "");
- }
- DataTypeComponent dtc = ((StructureDataType) dt).getComponentAt(lastOffset);
- String comment = dtc.getComment();
- comment += " " + fieldName + "(" + fieldSize + ")";
- dtc.setComment(comment);
- if (fSize < member.getLength()) {
- Msg.debug(this, "Dropping bitfield=[" + fieldName + "] type=[" +
- member.getName() + "] in " + myName);
- }
- else {
- Msg.debug(this, "Dropping element=[" + fieldName + "] type=[" +
- member.getName() + "] in " + myName);
- }
- }
- else {
- // HACKALERT: Big ol' hack here... (GSL appears to be lying to us)
- if (align > 4) {
- align = 4;
- }
- int mod = (align == 0) ? 0 : dt.getLength() % align;
- DataType modDt = null;
- if (mod != 0) {
- modDt = new ArrayDataType(new ByteDataType(), align - mod, 1);
- }
- if (member.getLength() > 0) {
- lastOffset = dt.getLength();
- if (modDt != null) {
- ((CompositeDataTypeImpl) dt).add(modDt, modDt.getLength(),
- "_fill_" + dt.getLength(), ALIGNMENT_TAG);
- }
- ((CompositeDataTypeImpl) dt).add(member, member.getLength(), fieldName,
- "");
- }
- else {
- Msg.debug(this, "Dropping mid-structure zero length element=[" +
- fieldName + "] type=[" + member.getName() + "] in " + myName);
- }
- }
- }
- catch (Exception e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
- }
- }
- else if (fieldId.compareToIgnoreCase("FUNCTION") == 0) {
- dt = new FunctionDefinitionDataType(cName.getCategoryPath(), cName.getName());
- String retVal = tokenizer.nextToken();
- ComplexName rvName = new ComplexName(retVal);
- DataType rvDt = rvName.getDataType(dtMgr, dataTypes);
- if (rvDt == null) {
- rvDt = genUIData(dtMgr, rvName, 4);
- }
- ((FunctionDefinitionDataType) dt).setReturnType(rvDt);
- ArrayList parameters = new ArrayList<>();
- index = 0;
- while (tokenizer.hasMoreElements()) {
- String fieldName = tokenizer.nextToken();
- String fieldType = tokenizer.nextToken();
- ComplexName fType = new ComplexName(fieldType);
- DataType parameter = fType.getDataType(dtMgr, dataTypes);
- if (parameter == null) {
- parameter = genUIData(dtMgr, fType, 4);
- }
- parameters.add(new ParameterDefinitionImpl(fieldName, parameter, ""));
- index++;
- }
- if (index > 0) {
- ParameterDefinition[] parms = new ParameterDefinition[parameters.size()];
- parameters.toArray(parms);
- ((FunctionDefinitionDataType) dt).setArguments(parms);
- }
- }
- else if (fieldId.compareToIgnoreCase("TYPEDEF") == 0) {
- String newName = tokenizer.nextToken();
- try {
- DataType baseType = cName.getDataType(dtMgr, dataTypes);
- if (baseType == null) {
- baseType = genUIData(dtMgr, cName, 4);
- }
- ComplexName cNewName = new ComplexName(newName);
- dt = new TypedefDataType(cNewName.getCategoryPath(), newName, baseType);
- }
- catch (Exception e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
- }
- else if (fieldId.compareToIgnoreCase("ENUM") == 0) {
- ArrayList fNames = new ArrayList<>();
- ArrayList fValues = new ArrayList<>();
- ArrayList fComments = new ArrayList<>();
- while (tokenizer.hasMoreElements()) {
- fNames.add(tokenizer.nextToken());
- fValues.add(tokenizer.nextToken());
- fComments.add(tokenizer.nextToken());
- }
- dt = new EnumDataType(cName.getCategoryPath(), cName.getName(), fNames.size(), null);
- for (int i = 0; i < fNames.size(); i++) {
- ((EnumDataType) dt).add(fNames.get(i), new Long(fValues.get(i)).longValue(), fComments.get(i));
- }
- }
- else if (fieldId.compareToIgnoreCase("SYMBOL") == 0) {
-
- }
- else {
- Msg.warn(this, "What is this? " + fieldId);
- }
- return dt;
- }
-
- private void searchForErrors(FileDataTypeManager dtMgr) {
- try {
- Iterator dts = dtMgr.getAllDataTypes();
- while (dts.hasNext()) {
- DataType dti = dts.next();
- if (dti.getDisplayName().indexOf("%") >= 0) {
- Msg.warn(this, "Misprocessed data type: " + dti.getDisplayName());
- }
- if (dti instanceof TypeDef) {
- DataType base = ((TypeDef) dti).getBaseDataType();
- if (base instanceof Pointer) {
- base = ((Pointer) base).getDataType();
- if (base.isEquivalent(dataUI)) {
- Msg.warn(this, "Data type (" + dti.getDisplayName() + ") not found.");
- }
- }
- }
- }
- }
- catch (Exception e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
- }
-
- public DataType genUIData(FileDataTypeManager dtMgr, ComplexName cName, int len) {
- TypedefDataType dt = new TypedefDataType(cName.getCategoryPath(), cName.getName(),
- new ArrayDataType(new ByteDataType(), len, 1));
- addDataType(dtMgr, dataTypes, dt);
- return dt;
- }
-
- int valueOf(String intString) {
- return (new Integer(intString)).intValue() / 8;
- }
-
- private void addPrimitives(FileDataTypeManager dtMgr) {
- addDataType(dtMgr, dataTypes, new TypedefDataType("char", new ByteDataType()));
- addDataType(dtMgr, dataTypes, new TypedefDataType("signed char", new ByteDataType()));
- addDataType(dtMgr, dataTypes, new TypedefDataType("unsigned char", new ByteDataType()));
-
- addDataType(dtMgr, dataTypes, new TypedefDataType("short", new WordDataType()));
- addDataType(dtMgr, dataTypes, new TypedefDataType("short int", new WordDataType()));
- addDataType(dtMgr, dataTypes, new TypedefDataType("short signed int", new WordDataType()));
- addDataType(dtMgr, dataTypes,
- new TypedefDataType("short unsigned int", new WordDataType()));
-
- addDataType(dtMgr, dataTypes, new TypedefDataType("int", new DWordDataType()));
- addDataType(dtMgr, dataTypes, new TypedefDataType("long", new DWordDataType()));
- addDataType(dtMgr, dataTypes, new TypedefDataType("long int", new DWordDataType()));
- addDataType(dtMgr, dataTypes, new TypedefDataType("signed int", new DWordDataType()));
- addDataType(dtMgr, dataTypes, new TypedefDataType("unsigned int", new DWordDataType()));
- addDataType(dtMgr, dataTypes, new TypedefDataType("long signed int", new DWordDataType()));
- addDataType(dtMgr, dataTypes,
- new TypedefDataType("long unsigned int", new DWordDataType()));
-
- addDataType(dtMgr, dataTypes, new TypedefDataType("long long int", new QWordDataType()));
- addDataType(dtMgr, dataTypes,
- new TypedefDataType("long long signed int", new QWordDataType()));
- addDataType(dtMgr, dataTypes,
- new TypedefDataType("long long unsigned int", new QWordDataType()));
-
- addDataType(dtMgr, dataTypes, new TypedefDataType("void", new VoidDataType()));
- dataUI = new TypedefDataType("U/I", new DWordDataType());
- addDataType(dtMgr, dataTypes, dataUI);
- }
-
- private void addDataType(FileDataTypeManager dtMgr, Hashtable myDataTypes,
- DataType dt) {
- DataType type = dtMgr.addDataType(dt, DataTypeConflictHandler.REPLACE_HANDLER);
- myDataTypes.put(type.getCategoryPath() + type.getName(), type);
- }
-
- private File chooseFile(Component parent, String title, String propertyName,
- final String fileType) {
- if (fileChooser == null) {
- fileChooser = new GhidraFileChooser(parent);
- }
- fileChooser.setFileFilter(new ExtensionFileFilter(fileType,
- fileType.toUpperCase() + " files (." + fileType + ")"));
- fileChooser.setTitle(title);
-
- // start the browsing in the user's preferred directory
- //
- File directory =
- new File(Preferences.getProperty(propertyName, System.getProperty("user.home"), true));
- fileChooser.setCurrentDirectory(directory);
- fileChooser.setSelectedFile(directory);
-
- File file = fileChooser.getSelectedFile();
- if (file != null) {
- // record where we last exported a file from to the user's preferences
- Preferences.setProperty(propertyName, file.getAbsolutePath());
- }
-
- return file;
- }
-
- final static String NO_NAMESPACE = "/";
- final int SIZE_X = 8;
- final int ALIGNMENT = 32;
-
- private void writeAsGsl(File file, DataTypeManager dtMgr, TaskMonitor monitor) {
- String out = "";
- int count = 0;
- try {
- FileOutputStream stream = new FileOutputStream(file);
- Iterator it = dtMgr.getAllDataTypes();
- while (it.hasNext()) {
- DataType dt = it.next();
- out = "";
- if (dt instanceof Composite) {
- out = writeComposite(stream, dt);
- }
- else if (dt instanceof TypeDef) {
- out = writeTypeDef(stream, (TypeDef) dt);
- }
- else if (dt instanceof FunctionDefinition) {
- out = writeFunctionDefinition(stream, (FunctionDefinition) dt);
- }
- else if (dt instanceof Enum) {
- out = writeEnum(stream, (Enum) dt);
- }
- else {
- Msg.debug(this, "Something went wrong while printing GSL output...");
- }
- if (out != "") {
- stream.write(out.getBytes());
- }
- if (count % 100 == 0) {
- monitor.setProgress(count);
- }
- count++;
- }
- stream.close();
- }
- catch (Exception e) {
- Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
- }
- }
-
- private String writeComposite(FileOutputStream stream, DataType dt) {
- String out;
- DataTypeComponent[] components;
-
- out = (dt instanceof Structure) ? "STRUCT$" : "UNION$";
- components = ((Composite) dt).getComponents();
- out += new NameComplex(dt).getGSLName() + "$";
- out += SIZE_X * dt.getLength() + "$";
- out += ALIGNMENT;
- for (DataTypeComponent component : components) {
- String comment = component.getComment();
- if ((comment != null) && (comment.compareTo(ALIGNMENT_TAG) != 0)) {
- NameComplex cComp = new NameComplex(component.getDataType());
- out += "$";
- out += component.getFieldName() + "$";
- out += cComp.getGSLName() + "$";
- out += SIZE_X * component.getOffset() + "$";
- out += SIZE_X * component.getLength() + "$";
- if (component.getDataType() instanceof Composite) {
- out += ALIGNMENT;
- }
- else {
- out += SIZE_X * cComp.getBaseTypeSize();
- }
- }
- }
- out += "\n";
- return out;
- }
-
- private String writeTypeDef(FileOutputStream stream, TypeDef def) {
- DataType dt = def.getDataType();
-
- String out = "TYPEDEF$";
- out += new NameComplex(dt).getGSLName() + "$";
- out += new NameComplex(def).getGSLName() + "\n";
- return out;
- }
-
- private String writeEnum(FileOutputStream stream, Enum enuum) {
- String out = "ENUM$";
- String[] names = enuum.getNames();
- long[] values = enuum.getValues();
-
- out += new NameComplex(enuum).getGSLName();
- for (int i = 0; i < names.length; i++) {
- out += "$";
- out += names[i] + "$";
- out += values[i];
- }
- out += "\n";
- return out;
- }
-
- private String writeFunctionDefinition(FileOutputStream stream, FunctionDefinition fn) {
- String out = "FUNCTION$";
- ParameterDefinition[] parameters = fn.getArguments();
- DataType ret = fn.getReturnType();
-
- out += new NameComplex(fn).getGSLName() + "$";
- out += new NameComplex(ret).getGSLName();
- for (ParameterDefinition parameter : parameters) {
- NameComplex cParm = new NameComplex(parameter.getDataType());
- out += "$";
- String pName = parameter.getName();
- out += ((pName.compareToIgnoreCase(" ") == 0) ? "" : pName);
- out += "$";
- out += cParm.getGSLName();
- }
- out += "\n";
- return out;
- }
-
- public class NameComplex {
-
- DataType dt;
- String myName;
- String dtNamespace;
- int baseTypeSize;
-
- public NameComplex(DataType dt) {
- this.dt = dt;
- dtNamespace = dt.getCategoryPath().getPath();
-
- DataType base = dt;
- boolean shouldDescend = false;
- myName = "";
- if (dt instanceof Pointer) {
- myName = "Pointer%";
- base = ((Pointer) dt).getDataType();
- shouldDescend = true;
- }
- else if (dt instanceof Array) {
- Array array = (Array) dt;
- myName =
- "Array%" + array.getNumElements() + "%" + (SIZE_X * array.getLength() + "%");
- base = array.getDataType();
- shouldDescend = true;
- }
-
- if (shouldDescend) {
- NameComplex cName = new NameComplex(base);
- myName += cName.getGSLName();
- if (dt instanceof Pointer) {
- baseTypeSize = dt.getLength();
- }
- else {
- baseTypeSize = cName.getBaseTypeSize();
- }
- }
- else {
- if (dtNamespace.compareToIgnoreCase(NO_NAMESPACE) != 0) {
- myName += dtNamespace + ":";
- }
- myName += dt.getName();
- baseTypeSize = dt.getLength();
- }
- }
-
- public String getGSLName() {
- return myName;
- }
-
- public int getBaseTypeSize() {
- return baseTypeSize;
- }
- }
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ComplexName.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ComplexName.java
deleted file mode 100644
index f5228e06b1..0000000000
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ComplexName.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/* ###
- * IP: GHIDRA
- * REVIEWED: YES
- *
- * 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.
- */
-/*
- * Created on Aug 9, 2005
- *
- * TODO To change the template for this generated file go to
- * Window - Preferences - Java - Code Style - Code Templates
- */
-package ghidra.app.plugin.prototype.dataArchiveUtilities;
-
-import ghidra.program.model.data.*;
-import ghidra.util.Msg;
-
-import java.util.Hashtable;
-
-public class ComplexName {
- boolean isPointer;
- boolean isArray;
- boolean usesNamespace;
- String complexName;
- String namespace;
- String name;
- int count = 1;
-
- public ComplexName(String complexName) {
- this.complexName = complexName;
- if (complexName.startsWith("Pointer%")) {
- isPointer = true;
- complexName = complexName.substring(complexName.indexOf("%")+1);
- }
- if (complexName.startsWith("Array%")) {
- isArray = true;
- complexName = complexName.substring(complexName.indexOf("%")+1);
- String countStr = complexName.substring(0, complexName.indexOf("%"));
- if (countStr.compareToIgnoreCase("") != 0) {
- count = new Integer(countStr).intValue();
- } else {
- count = 1;
- }
- complexName = complexName.substring(complexName.indexOf("%")+1);
- complexName = complexName.substring(complexName.indexOf("%")+1);
- }
- usesNamespace = (complexName.indexOf(":") >= 0);
- if (usesNamespace) {
- namespace = complexName.substring(complexName.indexOf("/"), complexName.indexOf(":"));
- name = complexName.substring(0, complexName.indexOf("/"))+
- complexName.substring(complexName.indexOf(":")+1, complexName.length());
- } else {
- namespace = "";
- name = complexName;
- }
- }
- public boolean isArray() {
- return isArray;
- }
- public boolean isPointer() {
- return isPointer;
- }
- public String getName() {
- return name;
- }
- public String getNamespace() {
- return namespace;
- }
- public boolean usesNamespace() {
- return usesNamespace;
- }
- public CategoryPath getCategoryPath() {
- return (usesNamespace ? new CategoryPath(namespace) : CategoryPath.ROOT);
- }
-
- public DataType getDataType(FileDataTypeManager dtMgr, Hashtable dataTypes) {
- return getDataType(dtMgr, dataTypes, true);
- }
- public DataType getDataType(DataTypeManager dtMgr, Hashtable dataTypes, boolean generateUI) {
- DataType dt;
- if (name.indexOf("%") >= 0) {
- ComplexName cName = new ComplexName(name);
- dt = cName.getDataType(dtMgr, dataTypes, generateUI);
- name = cName.getName();
- } else {
- if (dataTypes == null) {
- dt = dtMgr.getDataType(getCategoryPath(), getName());
- } else {
- dt = dataTypes.get(getCategoryPath()+getName());
- }
- //if (dt == null) {
- // dt = dtMgr.getDataType(getCategoryPath(), getName());
- //}
- }
- if (dt == null) {
- if (generateUI) {
- // Add placeholders for the archive entries that are missing
- dt = genUIData(dtMgr, dataTypes, new ComplexName(complexName), 4);
- } else {
- Msg.warn(this, "Data type ("+name+") not found.");
- return dt;
- }
- }
- if (isPointer) {
- dt = new Pointer32DataType(dt);
- }
- if (isArray) {
- if (dt.getLength() >= 0) {
- dt = new ArrayDataType(dt, count, dt.getLength());
- } else {
- Msg.error(this, "Error in array length ("+dt.getLength()+") for "+dt.getName());
- return null;
- }
- }
- return dt;
- }
-
- public DataType genUIData(DataTypeManager dtMgr, Hashtable dataTypes, ComplexName cName, int len) {
- TypedefDataType dt = new TypedefDataType(cName.getCategoryPath(), cName.getName(),
- new ArrayDataType(new ByteDataType(), len, 1));
- addDataType(dtMgr, dataTypes, dt);
- return dt;
- }
-
- private void addDataType(DataTypeManager dtMgr, Hashtable dataTypes, DataType dt) {
- DataType type = dtMgr.addDataType(dt, DataTypeConflictHandler.REPLACE_HANDLER);
- dataTypes.put(type.getCategoryPath()+type.getName(), type);
- }
-
-
-}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/EnumDataTypeHTMLRepresentation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/EnumDataTypeHTMLRepresentation.java
index fd7b837050..18920b2d2d 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/EnumDataTypeHTMLRepresentation.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/EnumDataTypeHTMLRepresentation.java
@@ -40,8 +40,7 @@ public class EnumDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation {
// private constructor for making diff copies
private EnumDataTypeHTMLRepresentation(Enum enumDataType, List headerLines,
- TextLine displayName,
- List bodyContent, TextLine footerLine) {
+ TextLine displayName, List bodyContent, TextLine footerLine) {
this.enumDataType = enumDataType;
this.headerContent = headerLines;
this.displayName = displayName;
@@ -113,7 +112,13 @@ public class EnumDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation {
int length = hexString.length();
hexString = hexString.substring(length - (n * 2));
}
- list.add(new TextLine(name + " = 0x" + hexString));
+
+ String comment = enumDataType.getComment(name);
+ if (trim && comment != null) {
+ comment = StringUtilities.trim(comment, ToolTipUtils.LINE_LENGTH);
+ }
+
+ list.add(new TextLine(name + " = 0x" + hexString + " " + comment));
}
return list;
@@ -147,7 +152,7 @@ public class EnumDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation {
displayNameText = HTMLUtilities.friendlyEncodeHTML(displayNameText);
displayNameText = wrapStringInColor(displayNameText, displayName.getTextColor());
//@formatter:off
- append(fullHtml, truncatedHtml, lineCount, TT_OPEN,
+ append(fullHtml, truncatedHtml, lineCount, TT_OPEN,
displayNameText,
TT_CLOSE,
HTML_SPACE,
@@ -192,8 +197,8 @@ public class EnumDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation {
return fullHtml.toString();
}
- private static void append(StringBuilder fullHtml, StringBuilder truncatedHtml,
- int lineCount, String... content) {
+ private static void append(StringBuilder fullHtml, StringBuilder truncatedHtml, int lineCount,
+ String... content) {
for (String string : content) {
fullHtml.append(string);
@@ -247,8 +252,7 @@ public class EnumDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation {
diffDisplayName, bodyDiff.getLeftLines(), footerLine),
new EnumDataTypeHTMLRepresentation(enumRepresentation.enumDataType,
headerDiff.getRightLines(), otherDiffDisplayName, bodyDiff.getRightLines(),
- enumRepresentation.footerLine)
- };
+ enumRepresentation.footerLine) };
}
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/HTMLDataTypeRepresentation.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/HTMLDataTypeRepresentation.java
index fc5a1bce7f..e5368f5797 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/HTMLDataTypeRepresentation.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/html/HTMLDataTypeRepresentation.java
@@ -70,7 +70,7 @@ public abstract class HTMLDataTypeRepresentation {
// Note 1: Indentation tags (note: we switched from tags because the Java rendering engine
// does not keep the color of the div's parent tags. The
tag seems to work).
// Note 2: Switch back to
from
, since the
tag gets broken by the
tag
- // used by composite types. If not inheriting the color becomes an issue, then we will need
+ // used by composite types. If not inheriting the color becomes an issue, then we will need
// to find another solution for indentation.
protected static final String INDENT_OPEN = "";
protected static final String INDENT_CLOSE = "
";
@@ -87,7 +87,7 @@ public abstract class HTMLDataTypeRepresentation {
protected final static Color DIFF_COLOR = ValidatableLine.INVALID_COLOR;
private static String createSpace(int numberOfSpaces) {
- StringBuffer buffer = new StringBuffer();
+ StringBuilder buffer = new StringBuilder();
for (int i = 0; i < numberOfSpaces; i++) {
buffer.append(HTML_SPACE);
}
@@ -134,7 +134,7 @@ public abstract class HTMLDataTypeRepresentation {
* Returns the plain-text value of the data type's description.
*
* If there were html tags in the string, they are escaped.
- *
+ *
* @param dataType the type to get the description / comment for
* @return plain-text string, w/html escaped
*/
@@ -167,7 +167,7 @@ public abstract class HTMLDataTypeRepresentation {
/**
* Formats a multi-line plain-text comment string into a HTML string where the text has been
* wrapped at MAX_LINE_LENGTH.
- *
+ *
* @param string plain-text string
* @return list of html strings
*/
@@ -241,9 +241,9 @@ public abstract class HTMLDataTypeRepresentation {
/**
* Formats a multi-line plain-text comment as a list of HTML marked-up lines.
- *
+ *
* @param comment multi-line plain-text string
- * @param maxLines max number of formatted lines to return
+ * @param maxLines max number of formatted lines to return
* @return list of html marked-up {@link TextLine}s
*/
protected static List createCommentLines(String comment, int maxLines) {
@@ -325,9 +325,9 @@ public abstract class HTMLDataTypeRepresentation {
}
/**
- * Returns an HTML string for this data representation object. The HTML returned will be
+ * Returns an HTML string for this data representation object. The HTML returned will be
* truncated if it is too long. To get the full HTML, call {@link #getFullHTMLString()}.
- *
+ *
* @return the html
* @see #getFullHTMLString()
*/
@@ -337,7 +337,7 @@ public abstract class HTMLDataTypeRepresentation {
/**
* Returns an HTML string for this data representation object
- *
+ *
* @return the html
* @see #getHTMLString()
*/
@@ -345,16 +345,16 @@ public abstract class HTMLDataTypeRepresentation {
return HTML_OPEN + originalHTMLData + HTML_CLOSE;
}
- /**
- * This is like {@link #getHTMLString()}, but does not put HTML tags around the data
+ /**
+ * This is like {@link #getHTMLString()}, but does not put HTML tags around the data
* @return the content
*/
public String getHTMLContentString() {
return originalHTMLData; // default to full text; subclasses can override
}
- /**
- * This is like {@link #getHTMLString()}, but does not put HTML tags around the data
+ /**
+ * This is like {@link #getHTMLString()}, but does not put HTML tags around the data
* @return the content
*/
public String getFullHTMLContentString() {
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge2Test.java
index ea3f654c3d..ecaad2678d 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge2Test.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/merge/datatypes/DataTypeMerge2Test.java
@@ -1115,12 +1115,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest {
@Test
public void testEditEnum() throws Exception {
- // edit DLL_Table in latest; edit DLL_Table in private
- // only DLL_Table should be in conflict; not the ones where it is used.
+
mtf.initialize("notepad", new ProgramModifierListener() {
- /* (non-Javadoc)
- * @see ghidra.framework.data.ProgramModifierListener#modifyLatest(ghidra.program.database.ProgramDB)
- */
@Override
public void modifyLatest(ProgramDB program) {
boolean commit = false;
@@ -1140,9 +1136,6 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest {
}
}
- /* (non-Javadoc)
- * @see ghidra.framework.data.ProgramModifierListener#modifyPrivate(ghidra.program.database.ProgramDB)
- */
@Override
public void modifyPrivate(ProgramDB program) {
boolean commit = false;
@@ -1180,7 +1173,164 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest {
assertNotNull(dt);
Enum enumm = (Enum) dt;
assertEquals(0x10, enumm.getValue("Pink"));
+ }
+ @Test
+ public void testEditEnumComments_NoConflict_CommentAddedInLatest() throws Exception {
+
+ mtf.initialize("notepad", new ProgramModifierListener() {
+
+ @Override
+ public void modifyLatest(ProgramDB program) {
+ boolean commit = false;
+ DataTypeManager dtm = program.getDataTypeManager();
+ int transactionID = program.startTransaction("test");
+ Category c = dtm.getCategory(new CategoryPath("/MISC"));
+ DataType dt = c.getDataType("FavoriteColors");
+ try {
+ Enum enumm = (Enum) dt;
+ String valueName = "Pink";
+ long value = enumm.getValue(valueName);
+ enumm.remove(valueName);
+ enumm.add(valueName, value, "This is the latest comment on server");
+ commit = true;
+ }
+ finally {
+ program.endTransaction(transactionID, commit);
+ }
+ }
+
+ @Override
+ public void modifyPrivate(ProgramDB program) {
+ // no change
+ }
+
+ });
+ executeMerge(DataTypeMergeManager.OPTION_MY);
+ DataTypeManager dtm = resultProgram.getDataTypeManager();
+
+ Category c = dtm.getCategory(new CategoryPath("/MISC"));
+ DataType dt = c.getDataType("FavoriteColors");
+ assertNotNull(dt);
+ Enum enumm = (Enum) dt;
+ assertEquals(0x3, enumm.getValue("Pink"));
+ assertEquals("This is the latest comment on server", enumm.getComment("Pink"));
+ }
+
+ @Test
+ public void testEditEnumComments_Conflict_TakeMyChanges() throws Exception {
+
+ mtf.initialize("notepad", new ProgramModifierListener() {
+
+ @Override
+ public void modifyLatest(ProgramDB program) {
+ boolean commit = false;
+ DataTypeManager dtm = program.getDataTypeManager();
+ int transactionID = program.startTransaction("test");
+ Category c = dtm.getCategory(new CategoryPath("/MISC"));
+ DataType dt = c.getDataType("FavoriteColors");
+ try {
+ Enum enumm = (Enum) dt;
+ String valueName = "Pink";
+ long value = enumm.getValue(valueName);
+ enumm.remove(valueName);
+ enumm.add(valueName, value, "This is the latest comment on server");
+ commit = true;
+ }
+ finally {
+ program.endTransaction(transactionID, commit);
+ }
+ }
+
+ @Override
+ public void modifyPrivate(ProgramDB program) {
+ boolean commit = false;
+ DataTypeManager dtm = program.getDataTypeManager();
+ int transactionID = program.startTransaction("test");
+ Category c = dtm.getCategory(new CategoryPath("/MISC"));
+ DataType dt = c.getDataType("FavoriteColors");
+
+ try {
+ Enum enumm = (Enum) dt;
+ String valueName = "Pink";
+ long value = enumm.getValue(valueName);
+ enumm.remove(valueName);
+ enumm.add(valueName, value, "This my local updated comment");
+ commit = true;
+ }
+ finally {
+ program.endTransaction(transactionID, commit);
+ }
+ }
+
+ });
+ executeMerge(DataTypeMergeManager.OPTION_MY);
+ DataTypeManager dtm = resultProgram.getDataTypeManager();
+
+ Category c = dtm.getCategory(new CategoryPath("/MISC"));
+ DataType dt = c.getDataType("FavoriteColors");
+ assertNotNull(dt);
+ Enum enumm = (Enum) dt;
+ assertEquals(0x3, enumm.getValue("Pink"));
+ assertEquals("This my local updated comment", enumm.getComment("Pink"));
+ }
+
+ @Test
+ public void testEditEnumComments_Conflict_TakeLatestChanges() throws Exception {
+
+ mtf.initialize("notepad", new ProgramModifierListener() {
+
+ @Override
+ public void modifyLatest(ProgramDB program) {
+ boolean commit = false;
+ DataTypeManager dtm = program.getDataTypeManager();
+ int transactionID = program.startTransaction("test");
+ Category c = dtm.getCategory(new CategoryPath("/MISC"));
+ DataType dt = c.getDataType("FavoriteColors");
+ try {
+ Enum enumm = (Enum) dt;
+ String valueName = "Pink";
+ long value = enumm.getValue(valueName);
+ enumm.remove(valueName);
+ enumm.add(valueName, value, "This is the latest comment on server");
+ commit = true;
+ }
+ finally {
+ program.endTransaction(transactionID, commit);
+ }
+ }
+
+ @Override
+ public void modifyPrivate(ProgramDB program) {
+ boolean commit = false;
+ DataTypeManager dtm = program.getDataTypeManager();
+ int transactionID = program.startTransaction("test");
+ Category c = dtm.getCategory(new CategoryPath("/MISC"));
+ DataType dt = c.getDataType("FavoriteColors");
+
+ try {
+ Enum enumm = (Enum) dt;
+ String valueName = "Pink";
+ long value = enumm.getValue(valueName);
+ enumm.remove(valueName);
+ enumm.add(valueName, value, "This my local updated comment");
+ commit = true;
+ }
+ finally {
+ program.endTransaction(transactionID, commit);
+ }
+ }
+
+ });
+ executeMerge(DataTypeMergeManager.OPTION_LATEST);
+ DataTypeManager dtm = resultProgram.getDataTypeManager();
+
+ Category c = dtm.getCategory(new CategoryPath("/MISC"));
+ DataType dt = c.getDataType("FavoriteColors");
+ assertNotNull(dt);
+ Enum enumm = (Enum) dt;
+ assertEquals(0x3, enumm.getValue("Pink"));
+ assertEquals("This is the latest comment on server", enumm.getComment("Pink"));
}
@Test
@@ -1355,9 +1505,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest {
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar");
- FunctionDefinition fd =
- (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"),
- "MyFunctionDef");
+ FunctionDefinition fd = (FunctionDefinition) dtm
+ .getDataType(new CategoryPath("/MISC"), "MyFunctionDef");
try {
fd.setReturnType(bar);
@@ -1376,9 +1525,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
- FunctionDefinition fd =
- (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"),
- "MyFunctionDef");
+ FunctionDefinition fd = (FunctionDefinition) dtm
+ .getDataType(new CategoryPath("/MISC"), "MyFunctionDef");
ParameterDefinition[] vars = fd.getArguments();
int transactionID = program.startTransaction("test");
@@ -1423,9 +1571,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest {
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
Structure bar = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Bar");
- FunctionDefinition fd =
- (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"),
- "MyFunctionDef");
+ FunctionDefinition fd = (FunctionDefinition) dtm
+ .getDataType(new CategoryPath("/MISC"), "MyFunctionDef");
try {
fd.setReturnType(bar);
@@ -1446,9 +1593,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
- FunctionDefinition fd =
- (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"),
- "MyFunctionDef");
+ FunctionDefinition fd = (FunctionDefinition) dtm
+ .getDataType(new CategoryPath("/MISC"), "MyFunctionDef");
ParameterDefinition[] vars = fd.getArguments();
int transactionID = program.startTransaction("test");
@@ -1491,9 +1637,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
- FunctionDefinition fd =
- (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"),
- "MyFunctionDef");
+ FunctionDefinition fd = (FunctionDefinition) dtm
+ .getDataType(new CategoryPath("/MISC"), "MyFunctionDef");
try {
fd.setVarArgs(true);
@@ -1514,9 +1659,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
- FunctionDefinition fd =
- (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"),
- "MyFunctionDef");
+ FunctionDefinition fd = (FunctionDefinition) dtm
+ .getDataType(new CategoryPath("/MISC"), "MyFunctionDef");
ParameterDefinition[] vars = fd.getArguments();
int transactionID = program.startTransaction("test");
@@ -1560,9 +1704,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
- FunctionDefinition fd =
- (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"),
- "MyFunctionDef");
+ FunctionDefinition fd = (FunctionDefinition) dtm
+ .getDataType(new CategoryPath("/MISC"), "MyFunctionDef");
try {
fd.setVarArgs(true);
@@ -1583,9 +1726,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
Structure foo = (Structure) dtm.getDataType(new CategoryPath("/MISC"), "Foo");
- FunctionDefinition fd =
- (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"),
- "MyFunctionDef");
+ FunctionDefinition fd = (FunctionDefinition) dtm
+ .getDataType(new CategoryPath("/MISC"), "MyFunctionDef");
ParameterDefinition[] vars = fd.getArguments();
int transactionID = program.startTransaction("test");
@@ -1633,9 +1775,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest {
DataTypeManager dtm = program.getDataTypeManager();
int transactionID = program.startTransaction("test");
- FunctionDefinition fd =
- (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"),
- "MyFunctionDef");
+ FunctionDefinition fd = (FunctionDefinition) dtm
+ .getDataType(new CategoryPath("/MISC"), "MyFunctionDef");
try {
fd.setReturnType(VoidDataType.dataType);
@@ -1655,9 +1796,8 @@ public class DataTypeMerge2Test extends AbstractDataTypeMergeTest {
boolean commit = false;
DataTypeManager dtm = program.getDataTypeManager();
- FunctionDefinition fd =
- (FunctionDefinition) dtm.getDataType(new CategoryPath("/MISC"),
- "MyFunctionDef");
+ FunctionDefinition fd = (FunctionDefinition) dtm
+ .getDataType(new CategoryPath("/MISC"), "MyFunctionDef");
ParameterDefinition[] vars = fd.getArguments();
int transactionID = program.startTransaction("test");
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor1Test.java
index 2a2dbce884..aee235f5e8 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor1Test.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor1Test.java
@@ -77,8 +77,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
public void testEnumFields() throws Exception {
Category c = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
Enum enumm = createEnum(c, "TestEnum", 1);
edit(enumm);
@@ -132,8 +131,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
public void testEnumSize1() throws Exception {
Category category = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
Enum enumm = createEnum(category, "TestEnum", 1);
edit(enumm);
@@ -184,8 +182,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
// test entering too large a value
Category category = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
Enum enumm = createEnum(category, "TestEnum", 1);
edit(enumm);
@@ -228,8 +225,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
public void testEnumSize4BadInput() throws Exception {
Category category = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
Enum enumm = createEnum(category, "MyTestEnum", 4);
edit(enumm);
@@ -280,8 +276,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
public void testBadInputForValue() throws Exception {
Category cat = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
Enum enumm = createEnum(cat, "TestEnum", 1);
edit(enumm);
@@ -310,8 +305,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
Category cat = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
final Enum enumm = new EnumDataType("Colors", 1);
enumm.add("Red", 0);
enumm.add("Green", 1);
@@ -375,8 +369,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
public void testValueForNewEntry() throws Exception {
Category cat = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
final Enum enumm = new EnumDataType("Colors", 1);
enumm.add("Red", 0x10);
enumm.add("Green", 0x20);
@@ -656,7 +649,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
EnumEditorPanel panel = findEditorPanel(tool.getToolFrame());
JTable table = panel.getTable();
- //
+ //
// First, let's try forward then backward
//
int startRow = 1;
@@ -665,12 +658,18 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
Component c = getEditorComponent(editor);
triggerActionKey(c, 0, KeyEvent.VK_TAB);
+ editor = assertEditingCell(table, startRow, startCol + 1);
+ c = getEditorComponent(editor);
+ triggerActionKey(c, 0, KeyEvent.VK_TAB);
+ editor = assertEditingCell(table, startRow, startCol + 2);
+ c = getEditorComponent(editor);
+
+ triggerActionKey(c, InputEvent.SHIFT_DOWN_MASK, KeyEvent.VK_TAB);
editor = assertEditingCell(table, startRow, startCol + 1);
c = getEditorComponent(editor);
triggerActionKey(c, InputEvent.SHIFT_DOWN_MASK, KeyEvent.VK_TAB);
-
editor = assertEditingCell(table, startRow, startCol);
c = getEditorComponent(editor);
@@ -678,7 +677,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
// Now, let's try going around the world and back
//
int lastRow = 2;
- int lastCol = 1;
+ int lastCol = 2;
editor = startEditTableCell(table, lastRow, lastCol);
c = getEditorComponent(editor);
@@ -703,7 +702,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
JTable table = getEditTable();
- //
+ //
// First, let's try up and down
//
int startRow = 0;
@@ -739,7 +738,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testNewEnumFromAction() throws Exception {
//
- // This test works differently that the others in that it uses the same path as the
+ // This test works differently that the others in that it uses the same path as the
// GUI action to start the editing process.
//
DataTypeManager dtm = program.getListing().getDataTypeManager();
@@ -764,8 +763,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
Category category = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
Enum enumm = createEnum(category, "EnumX", 2);
int transactionID = program.startTransaction("Test");
@@ -835,8 +833,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
Category category = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
Enum enumm = createEnum(category, "EnumX", 2);
int transactionID = program.startTransaction("Test");
@@ -1049,8 +1046,8 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
try {
Category category = dtm.getCategory(enummDt.getCategoryPath());
Category parentCategory = category.getParent();
- assertTrue("Did not remove category", parentCategory.removeCategory(category.getName(),
- TaskMonitor.DUMMY));
+ assertTrue("Did not remove category",
+ parentCategory.removeCategory(category.getName(), TaskMonitor.DUMMY));
}
finally {
program.endTransaction(txID, true);
@@ -1126,8 +1123,8 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
private TableCellEditor assertEditingCell(final JTable table, final int row, final int col) {
Pair rowCol = getEditingCell(table);
- assertEquals(row, (int) rowCol.first);
- assertEquals(col, (int) rowCol.second);
+ assertEquals("Not editing expected row", row, (int) rowCol.first);
+ assertEquals("Not editing expected column", col, (int) rowCol.second);
return runSwing(() -> table.getCellEditor());
}
@@ -1144,8 +1141,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
private Enum createRedGreenBlueEnum() {
Category cat = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
final Enum enumm = new EnumDataType("Colors", 1);
enumm.add("Red", 0);
enumm.add("Green", 1);
@@ -1235,7 +1231,7 @@ public class EnumEditor1Test extends AbstractGhidraHeadedIntegrationTest {
addEnumValue();
waitForSwing();
final int row = model.getRowCount() - 1;
- // change entry
+ // change entry
table.addRowSelectionInterval(row, row);
Rectangle rect = table.getCellRect(row, NAME_COL, true);
clickMouse(table, 1, rect.x, rect.y, 2, 0);
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor2Test.java
index dd1a1ba06f..e2564e0db9 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor2Test.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/datamgr/editor/EnumEditor2Test.java
@@ -41,10 +41,6 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
private TestEnv env;
private DataTypeManagerPlugin plugin;
- public EnumEditor2Test() {
- super();
- }
-
@Before
public void setUp() throws Exception {
@@ -70,8 +66,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
Category cat = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
final Enum enumm = new EnumDataType("Colors", 1);
enumm.add("Red", 0);
enumm.add("Green", 0x10);
@@ -107,8 +102,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
public void testSortColumns() throws Exception {
Category cat = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
final Enum enumm = new EnumDataType("Colors", 1);
enumm.add("Red", 0);
enumm.add("Green", 0x10);
@@ -151,8 +145,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
public void testSortOrder() throws Exception {
Category cat = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
final Enum enumm = new EnumDataType("Colors", 1);
enumm.add("Red", 0);
enumm.add("Green", 0x10);
@@ -197,8 +190,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
public void testInsertRowByName() throws Exception {
Category cat = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
final Enum enumm = new EnumDataType("Colors", 1);
enumm.add("Red", 0);
enumm.add("Green", 0x10);
@@ -324,8 +316,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
waitForSwing();
}
- final ComponentProvider provider =
- waitForComponentProvider(EnumEditorProvider.class);
+ final ComponentProvider provider = waitForComponentProvider(EnumEditorProvider.class);
assertNotNull(provider);
SwingUtilities.invokeLater(() -> provider.closeComponent());
waitForSwing();
@@ -360,8 +351,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
waitForSwing();
}
- final ComponentProvider provider =
- waitForComponentProvider(EnumEditorProvider.class);
+ final ComponentProvider provider = waitForComponentProvider(EnumEditorProvider.class);
assertNotNull(provider);
SwingUtilities.invokeLater(() -> provider.closeComponent());
waitForSwing();
@@ -390,8 +380,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
waitForSwing();
}
- final ComponentProvider provider =
- waitForComponentProvider(EnumEditorProvider.class);
+ final ComponentProvider provider = waitForComponentProvider(EnumEditorProvider.class);
assertNotNull(provider);
SwingUtilities.invokeLater(() -> provider.closeComponent());
waitForSwing();
@@ -455,8 +444,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
Category cat = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
final Enum enumm = new EnumDataType("Colors", 1);
enumm.add("Red", 0);
enumm.add("Green", 0x10);
@@ -760,7 +748,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
});
waitForSwing();
final int newRow = model.getRowCount() - 1;
- // change entry
+ // change entry
runSwing(() -> table.addRowSelectionInterval(newRow, newRow));
Rectangle rect = table.getCellRect(newRow, EnumTableModel.NAME_COL, true);
clickMouse(table, 1, rect.x, rect.y, 2, 0);
@@ -828,8 +816,7 @@ public class EnumEditor2Test extends AbstractGhidraHeadedIntegrationTest {
private Enum editSampleEnum() {
Category cat = program.getListing()
.getDataTypeManager()
- .getCategory(
- new CategoryPath(CategoryPath.ROOT, "Category1"));
+ .getCategory(new CategoryPath(CategoryPath.ROOT, "Category1"));
final Enum enumm = new EnumDataType("Colors", 1);
enumm.add("Red", 0);
enumm.add("Green", 0x10);
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java
index d2eb415322..fb5402e47f 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumDB.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.
@@ -19,6 +19,8 @@ import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
+import org.apache.commons.lang3.StringUtils;
+
import db.DBRecord;
import db.Field;
import ghidra.docking.settings.Settings;
@@ -33,7 +35,6 @@ import ghidra.util.UniversalID;
/**
* Database implementation for the enumerated data type.
- *
*/
class EnumDB extends DataTypeDB implements Enum {
@@ -42,6 +43,7 @@ class EnumDB extends DataTypeDB implements Enum {
private EnumDBAdapter adapter;
private EnumValueDBAdapter valueAdapter;
+
private Map nameMap; // name to value
private Map> valueMap; // value to names
private Map commentMap; // name to comment
@@ -88,21 +90,22 @@ class EnumDB extends DataTypeDB implements Enum {
commentMap = new HashMap<>();
Field[] ids = valueAdapter.getValueIdsInEnum(key);
-
for (Field id : ids) {
DBRecord rec = valueAdapter.getRecord(id.getLongValue());
String valueName = rec.getString(EnumValueDBAdapter.ENUMVAL_NAME_COL);
long value = rec.getLongValue(EnumValueDBAdapter.ENUMVAL_VALUE_COL);
- String valueNameComment = rec.getString(EnumValueDBAdapter.ENUMVAL_COMMENT_COL);
- addToCache(valueName, value, valueNameComment);
+ String comment = rec.getString(EnumValueDBAdapter.ENUMVAL_COMMENT_COL);
+ addToCache(valueName, value, comment);
}
}
- private void addToCache(String valueName, long value, String valueNameComment) {
+ private void addToCache(String valueName, long value, String comment) {
nameMap.put(valueName, value);
List list = valueMap.computeIfAbsent(value, v -> new ArrayList<>());
list.add(valueName);
- commentMap.put(valueName, valueNameComment);
+ if (!StringUtils.isBlank(comment)) {
+ commentMap.put(valueName, comment);
+ }
}
private boolean removeFromCache(String valueName) {
@@ -121,10 +124,7 @@ class EnumDB extends DataTypeDB implements Enum {
if (list.isEmpty()) {
valueMap.remove(value);
}
- String comment = commentMap.remove(valueName);
- if (comment == null) {
- return false;
- }
+ commentMap.remove(valueName);
return true;
}
@@ -168,7 +168,7 @@ class EnumDB extends DataTypeDB implements Enum {
}
@Override
- public String getComment(String valueName) throws NoSuchElementException {
+ public String getComment(String valueName) {
lock.acquire();
try {
checkIsValid();
@@ -214,19 +214,6 @@ class EnumDB extends DataTypeDB implements Enum {
}
}
- @Override
- public String[] getComments() {
- lock.acquire();
- try {
- checkIsValid();
- initializeIfNeeded();
- return commentMap.keySet().toArray(new String[commentMap.size()]);
- }
- finally {
- lock.release();
- }
- }
-
@Override
public int getCount() {
lock.acquire();
@@ -242,12 +229,11 @@ class EnumDB extends DataTypeDB implements Enum {
@Override
public void add(String valueName, long value) {
- String valueNameComment = "";
- add(valueName, value, valueNameComment);
+ add(valueName, value, null);
}
@Override
- public void add(String valueName, long value, String valueNameComment) {
+ public void add(String valueName, long value, String comment) {
lock.acquire();
try {
checkDeleted();
@@ -256,10 +242,15 @@ class EnumDB extends DataTypeDB implements Enum {
if (nameMap.containsKey(valueName)) {
throw new IllegalArgumentException(valueName + " already exists in this enum");
}
+
+ if (StringUtils.isBlank(comment)) {
+ comment = null; // use null values in the db to save space
+ }
+
bitGroups = null;
- valueAdapter.createRecord(key, valueName, value, valueNameComment);
+ valueAdapter.createRecord(key, valueName, value, comment);
adapter.updateRecord(record, true);
- addToCache(valueName, value, valueNameComment);
+ addToCache(valueName, value, comment);
dataMgr.dataTypeChanged(this, false);
}
catch (IOException e) {
@@ -294,10 +285,9 @@ class EnumDB extends DataTypeDB implements Enum {
if (!removeFromCache(valueName)) {
return;
}
+
bitGroups = null;
-
Field[] ids = valueAdapter.getValueIdsInEnum(key);
-
for (Field id : ids) {
DBRecord rec = valueAdapter.getRecord(id.getLongValue());
if (valueName.equals(rec.getString(EnumValueDBAdapter.ENUMVAL_NAME_COL))) {
@@ -321,6 +311,7 @@ class EnumDB extends DataTypeDB implements Enum {
if (!(dataType instanceof Enum)) {
throw new IllegalArgumentException();
}
+
Enum enumm = (Enum) dataType;
lock.acquire();
try {
@@ -338,19 +329,21 @@ class EnumDB extends DataTypeDB implements Enum {
int oldLength = getLength();
int newLength = enumm.getLength();
-
if (oldLength != newLength) {
record.setByteValue(EnumDBAdapter.ENUM_SIZE_COL, (byte) newLength);
adapter.updateRecord(record, true);
}
String[] names = enumm.getNames();
- for (String name2 : names) {
- long value = enumm.getValue(name2);
- String comment = enumm.getComment(name2);
- valueAdapter.createRecord(key, name2, value, comment);
+ for (String valueName : names) {
+ long value = enumm.getValue(valueName);
+ String comment = enumm.getComment(valueName);
+ if (StringUtils.isBlank(comment)) {
+ comment = null; // use null values in the db to save space
+ }
+ valueAdapter.createRecord(key, valueName, value, comment);
adapter.updateRecord(record, true);
- addToCache(name2, value, comment);
+ addToCache(valueName, value, comment);
}
if (oldLength != newLength) {
@@ -525,7 +518,7 @@ class EnumDB extends DataTypeDB implements Enum {
return "0";
}
List list = getBitGroups();
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
for (BitGroup bitGroup : list) {
long subValue = bitGroup.getMask() & value;
if (subValue != 0) {
@@ -576,21 +569,39 @@ class EnumDB extends DataTypeDB implements Enum {
getLength() != enumm.getLength() || getCount() != enumm.getCount()) {
return false;
}
+
+ if (!isEachValueEquivalent(enumm)) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isEachValueEquivalent(Enum enumm) {
String[] names = getNames();
String[] otherNames = enumm.getNames();
try {
for (int i = 0; i < names.length; i++) {
+ if (!names[i].equals(otherNames[i])) {
+ return false;
+ }
+
long value = getValue(names[i]);
long otherValue = enumm.getValue(names[i]);
- if (!names[i].equals(otherNames[i]) || value != otherValue) {
+ if (value != otherValue) {
+ return false;
+ }
+
+ String comment = getComment(names[i]);
+ String otherComment = enumm.getComment(names[i]);
+ if (!comment.equals(otherComment)) {
return false;
}
}
+ return true;
}
catch (NoSuchElementException e) {
return false; // named element not found
}
- return true;
}
@Override
@@ -598,6 +609,7 @@ class EnumDB extends DataTypeDB implements Enum {
try {
nameMap = null;
valueMap = null;
+ commentMap = null;
bitGroups = null;
DBRecord rec = adapter.getRecord(key);
if (rec != null) {
@@ -731,5 +743,4 @@ class EnumDB extends DataTypeDB implements Enum {
lock.release();
}
}
-
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java
index 863e8c2552..bb0adfeba2 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapter.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.
@@ -30,15 +30,16 @@ import ghidra.util.task.TaskMonitor;
/**
* Adapter to access the Enumeration data type values tables.
*/
-abstract class EnumValueDBAdapter {
+abstract class EnumValueDBAdapter implements RecordTranslator {
static final String ENUM_VALUE_TABLE_NAME = "Enumeration Values";
- static final Schema ENUM_VALUE_SCHEMA = EnumValueDBAdapterV0.V0_ENUM_VALUE_SCHEMA;
+ static final Schema ENUM_VALUE_SCHEMA = EnumValueDBAdapterV1.SCHEMA;
+
// Enum Value Columns
- static final int ENUMVAL_NAME_COL = EnumValueDBAdapterV0.V0_ENUMVAL_NAME_COL;
- static final int ENUMVAL_VALUE_COL = EnumValueDBAdapterV0.V0_ENUMVAL_VALUE_COL;
- static final int ENUMVAL_ID_COL = EnumValueDBAdapterV0.V0_ENUMVAL_ID_COL;
- static final int ENUMVAL_COMMENT_COL = EnumValueDBAdapterV0.V0_ENUMVAL_COMMENT_COL;
+ static final int ENUMVAL_NAME_COL = 0;
+ static final int ENUMVAL_VALUE_COL = 1;
+ static final int ENUMVAL_ID_COL = 2;
+ static final int ENUMVAL_COMMENT_COL = 3;
/**
* Gets an adapter for working with the enumeration data type values database table. The adapter is based
@@ -53,16 +54,16 @@ abstract class EnumValueDBAdapter {
static EnumValueDBAdapter getAdapter(DBHandle handle, int openMode, TaskMonitor monitor)
throws VersionException, IOException {
if (openMode == DBConstants.CREATE) {
- return new EnumValueDBAdapterV0(handle, true);
+ return new EnumValueDBAdapterV1(handle, true);
}
try {
- return new EnumValueDBAdapterV0(handle, false);
+ return new EnumValueDBAdapterV1(handle, false);
}
catch (VersionException e) {
if (!e.isUpgradable() || openMode == DBConstants.UPDATE) {
throw e;
}
- EnumValueDBAdapter adapter = new EnumValueDBAdapterNoTable(handle);
+ EnumValueDBAdapter adapter = findReadOnlyAdapter(handle);
if (openMode == DBConstants.UPGRADE) {
adapter = upgrade(handle, adapter);
}
@@ -70,8 +71,18 @@ abstract class EnumValueDBAdapter {
}
}
+ static EnumValueDBAdapter findReadOnlyAdapter(DBHandle handle) {
+ try {
+ return new EnumValueDBAdapterV0(handle);
+ }
+ catch (VersionException e) {
+ return new EnumValueDBAdapterNoTable(handle);
+ }
+ }
+
/**
- * Upgrades the Enumeration Data Type Values table from the oldAdapter's version to the current version.
+ * Upgrades the Enumeration Data Type Values table from the oldAdapter's version to the current
+ * version.
* @param handle handle to the database whose table is to be upgraded to a newer version.
* @param oldAdapter the adapter for the existing table to be upgraded.
* @return the adapter for the new upgraded version of the table.
@@ -81,7 +92,30 @@ abstract class EnumValueDBAdapter {
*/
static EnumValueDBAdapter upgrade(DBHandle handle, EnumValueDBAdapter oldAdapter)
throws VersionException, IOException {
- return new EnumValueDBAdapterV0(handle, true);
+
+ DBHandle tmpHandle = new DBHandle();
+ long id = tmpHandle.startTransaction();
+ EnumValueDBAdapter tmpAdapter = null;
+ try {
+ tmpAdapter = new EnumValueDBAdapterV1(tmpHandle, true);
+ RecordIterator it = oldAdapter.getRecords();
+ while (it.hasNext()) {
+ DBRecord rec = it.next();
+ tmpAdapter.updateRecord(rec);
+ }
+ oldAdapter.deleteTable(handle);
+ EnumValueDBAdapter newAdapter = new EnumValueDBAdapterV1(handle, true);
+ it = tmpAdapter.getRecords();
+ while (it.hasNext()) {
+ DBRecord rec = it.next();
+ newAdapter.updateRecord(rec);
+ }
+ return newAdapter;
+ }
+ finally {
+ tmpHandle.endTransaction(id, true);
+ tmpHandle.close();
+ }
}
/**
@@ -90,7 +124,7 @@ abstract class EnumValueDBAdapter {
* @param name value name
* @param value numeric value
* @param comment the field comment
- * @throws IOException if IO error occurs
+ * @throws IOException if there was a problem accessing the database
*/
abstract void createRecord(long enumID, String name, long value, String comment)
throws IOException;
@@ -99,10 +133,26 @@ abstract class EnumValueDBAdapter {
* Get enum value record which corresponds to specified value record ID
* @param valueID value record ID
* @return value record or null
- * @throws IOException if IO error occurs
+ * @throws IOException if there was a problem accessing the database
*/
abstract DBRecord getRecord(long valueID) throws IOException;
+ /**
+ * Returns an iterator over the value records inside of this Enum
+ * @return the iterator
+ * @throws IOException if there was a problem accessing the database
+ */
+ abstract RecordIterator getRecords() throws IOException;
+
+ /**
+ * Deletes the table; used when upgrading
+ * @param handle the handle used to delete the table
+ * @throws IOException if there was a problem accessing the database
+ */
+ void deleteTable(DBHandle handle) throws IOException {
+ handle.deleteTable(ENUM_VALUE_TABLE_NAME);
+ }
+
/**
* Remove the record for the given enum Value ID.
* @param valueID ID of the value record to delete
@@ -113,7 +163,7 @@ abstract class EnumValueDBAdapter {
/**
* Updates the enum data type values table with the provided record.
* @param record the new record
- * @throws IOException if the database can't be accessed.
+ * @throws IOException if there was a problem accessing the database
*/
abstract void updateRecord(DBRecord record) throws IOException;
@@ -121,8 +171,7 @@ abstract class EnumValueDBAdapter {
* Get enum value record IDs which correspond to specified enum datatype ID
* @param enumID enum datatype ID
* @return enum value record IDs as LongField values within Field array
- * @throws IOException if IO error occurs
+ * @throws IOException if there was a problem accessing the database
*/
abstract Field[] getValueIdsInEnum(long enumID) throws IOException;
-
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java
index ef709967a3..a07ff31f4e 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterNoTable.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.
@@ -17,7 +17,10 @@ package ghidra.program.database.data;
import java.io.IOException;
+import javax.help.UnsupportedOperationException;
+
import db.*;
+import ghidra.program.database.util.EmptyRecordIterator;
/**
* Adapter needed for a read-only version of data type manager that is not going
@@ -59,4 +62,18 @@ class EnumValueDBAdapterNoTable extends EnumValueDBAdapter {
return Field.EMPTY_ARRAY;
}
+ @Override
+ RecordIterator getRecords() throws IOException {
+ return new EmptyRecordIterator();
+ }
+
+ @Override
+ protected void deleteTable(DBHandle handle) throws IOException {
+ // do nothing
+ }
+
+ @Override
+ public DBRecord translateRecord(DBRecord rec) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java
index e23903e5a8..87dfa5fd83 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV0.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.
@@ -18,92 +18,84 @@ package ghidra.program.database.data;
import java.io.IOException;
import db.*;
-import ghidra.program.model.lang.ConstantPool.Record;
import ghidra.util.exception.VersionException;
/**
* Version 0 implementation for the enumeration tables adapter.
- *
*/
class EnumValueDBAdapterV0 extends EnumValueDBAdapter {
static final int VERSION = 0;
- // Enum Value Columns
- static final int V0_ENUMVAL_NAME_COL = 0;
- static final int V0_ENUMVAL_VALUE_COL = 1;
- static final int V0_ENUMVAL_ID_COL = 2;
- static final int V0_ENUMVAL_COMMENT_COL = 3;
+// Keep for reference
+// static final Schema SCHEMA = new Schema(0, "Enum Value ID",
+// new Field[] { StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE },
+// new String[] { "Name", "Value", "Enum ID" });
- static final Schema V0_ENUM_VALUE_SCHEMA = new Schema(0, "Enum Value ID",
- new Field[] { StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE },
- new String[] { "Name", "Value", "Enum ID" });
-
-// static final Schema V1_ENUM_VALUE_SCHEMA = new Schema(0, "Enum Value ID",
-// new Class[] { StringField.class, LongField.class, LongField.class, StringField.class },
-// new String[] { "Name", "Value", "Enum ID", "Comment" });
-
- private Table valueTable;
+ private Table table;
/**
* Gets a version 0 adapter for the Enumeration Data Type Values database table.
* @param handle handle to the database containing the table.
- * @param create true if this constructor should create the table.
* @throws VersionException if the the table's version does not match the expected version
* for this adapter.
- * @throws IOException if IO error occurs
*/
- public EnumValueDBAdapterV0(DBHandle handle, boolean create)
- throws VersionException, IOException {
+ public EnumValueDBAdapterV0(DBHandle handle) throws VersionException {
- if (create) {
- valueTable = handle.createTable(ENUM_VALUE_TABLE_NAME, V0_ENUM_VALUE_SCHEMA,
- new int[] { V0_ENUMVAL_ID_COL });
+ table = handle.getTable(ENUM_VALUE_TABLE_NAME);
+ if (table == null) {
+ throw new VersionException("Missing Table: " + ENUM_VALUE_TABLE_NAME);
}
- else {
- valueTable = handle.getTable(ENUM_VALUE_TABLE_NAME);
- if (valueTable == null) {
- throw new VersionException("Missing Table: " + ENUM_VALUE_TABLE_NAME);
- }
- int version = valueTable.getSchema().getVersion();
- if (version != VERSION) {
- String msg = "Expected version " + VERSION + " for table " + ENUM_VALUE_TABLE_NAME +
- " but got " + valueTable.getSchema().getVersion();
- if (version < VERSION) {
- throw new VersionException(msg, VersionException.OLDER_VERSION, true);
- }
- throw new VersionException(msg, VersionException.NEWER_VERSION, false);
- }
+
+ int version = table.getSchema().getVersion();
+ if (version != VERSION) {
+ String msg = "Expected version " + VERSION + " for table " + ENUM_VALUE_TABLE_NAME +
+ " but got " + table.getSchema().getVersion();
+ throw new VersionException(msg, VersionException.NEWER_VERSION, false);
}
}
@Override
public void createRecord(long enumID, String name, long value, String comment)
throws IOException {
- Record record = V0_ENUM_VALUE_SCHEMA.createRecord(valueTable.getKey());
- record.setLongValue(V0_ENUMVAL_ID_COL, enumID);
- record.setString(V0_ENUMVAL_NAME_COL, name);
- record.setLongValue(V0_ENUMVAL_VALUE_COL, value);
- record.setString(V0_ENUMVAL_COMMENT_COL, comment);
- valueTable.putRecord(record);
+ throw new UnsupportedOperationException("Cannot update Version 0");
}
@Override
public DBRecord getRecord(long valueID) throws IOException {
- return valueTable.getRecord(valueID);
+ return translateRecord(table.getRecord(valueID));
}
@Override
public void removeRecord(long valueID) throws IOException {
- valueTable.deleteRecord(valueID);
+ throw new UnsupportedOperationException("Cannot remove Version 0");
}
@Override
public void updateRecord(DBRecord record) throws IOException {
- valueTable.putRecord(record);
+ throw new UnsupportedOperationException("Cannot update Version 0");
}
@Override
public Field[] getValueIdsInEnum(long enumID) throws IOException {
- return valueTable.findRecords(new LongField(enumID), V0_ENUMVAL_ID_COL);
+ return table.findRecords(new LongField(enumID), ENUMVAL_ID_COL);
+ }
+
+ @Override
+ public DBRecord translateRecord(DBRecord oldRec) {
+ if (oldRec == null) {
+ return null;
+ }
+
+ DBRecord record = EnumValueDBAdapter.ENUM_VALUE_SCHEMA.createRecord(oldRec.getKey());
+ record.setLongValue(ENUMVAL_ID_COL, oldRec.getLongValue(ENUMVAL_ID_COL));
+ record.setString(ENUMVAL_NAME_COL, oldRec.getString(ENUMVAL_NAME_COL));
+ record.setLongValue(ENUMVAL_VALUE_COL, oldRec.getLongValue(ENUMVAL_VALUE_COL));
+ record.setString(ENUMVAL_COMMENT_COL, null);
+ return record;
+ }
+
+ @Override
+ RecordIterator getRecords() throws IOException {
+ return new TranslatedRecordIterator(table.iterator(), this);
}
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV1.java
new file mode 100644
index 0000000000..1b3b922767
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV1.java
@@ -0,0 +1,107 @@
+/* ###
+ * 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.program.database.data;
+
+import java.io.IOException;
+
+import db.*;
+import ghidra.util.exception.VersionException;
+
+/**
+ * Version 1 implementation for the enumeration tables adapter.
+ */
+class EnumValueDBAdapterV1 extends EnumValueDBAdapter {
+
+ static final int VERSION = 1;
+
+ static final Schema SCHEMA = new Schema(VERSION, "Enum Value ID",
+ new Class[] { StringField.class, LongField.class, LongField.class, StringField.class },
+ new String[] { "Name", "Value", "Enum ID", "Comment" });
+
+ private Table table;
+
+ /**
+ * Gets a version 1 adapter for the Enumeration Data Type Values database table.
+ * @param handle handle to the database containing the table.
+ * @param create true if this constructor should create the table.
+ * @throws VersionException if the the table's version does not match the expected version
+ * for this adapter.
+ * @throws IOException if IO error occurs
+ */
+ public EnumValueDBAdapterV1(DBHandle handle, boolean create)
+ throws VersionException, IOException {
+
+ if (create) {
+ table = handle.createTable(ENUM_VALUE_TABLE_NAME, SCHEMA, new int[] { ENUMVAL_ID_COL });
+ }
+ else {
+ table = handle.getTable(ENUM_VALUE_TABLE_NAME);
+ if (table == null) {
+ throw new VersionException(true);
+ }
+ int version = table.getSchema().getVersion();
+ if (version != VERSION) {
+ String msg = "Expected version " + VERSION + " for table " + ENUM_VALUE_TABLE_NAME +
+ " but got " + table.getSchema().getVersion();
+ if (version < VERSION) {
+ throw new VersionException(msg, VersionException.OLDER_VERSION, true);
+ }
+ throw new VersionException(msg, VersionException.NEWER_VERSION, false);
+ }
+ }
+ }
+
+ @Override
+ public void createRecord(long enumID, String name, long value, String comment)
+ throws IOException {
+ DBRecord record = SCHEMA.createRecord(table.getKey());
+ record.setLongValue(ENUMVAL_ID_COL, enumID);
+ record.setString(ENUMVAL_NAME_COL, name);
+ record.setLongValue(ENUMVAL_VALUE_COL, value);
+ record.setString(ENUMVAL_COMMENT_COL, comment);
+ table.putRecord(record);
+ }
+
+ @Override
+ public DBRecord getRecord(long valueID) throws IOException {
+ return table.getRecord(valueID);
+ }
+
+ @Override
+ public void removeRecord(long valueID) throws IOException {
+ table.deleteRecord(valueID);
+ }
+
+ @Override
+ public void updateRecord(DBRecord record) throws IOException {
+ table.putRecord(record);
+ }
+
+ @Override
+ public Field[] getValueIdsInEnum(long enumID) throws IOException {
+ return table.findRecords(new LongField(enumID), ENUMVAL_ID_COL);
+ }
+
+ @Override
+ RecordIterator getRecords() throws IOException {
+ return table.iterator();
+ }
+
+ @Override
+ public DBRecord translateRecord(DBRecord r) {
+ return r;
+ }
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java
index 255256c520..2d9499cb1d 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapter.java
@@ -24,9 +24,9 @@ import ghidra.util.task.TaskMonitor;
/**
* Adapter to access the Pointer database table for Pointer data types.
- *
+ *
*/
-abstract class PointerDBAdapter {
+abstract class PointerDBAdapter implements RecordTranslator {
static final String POINTER_TABLE_NAME = "Pointers";
static final Schema SCHEMA = new Schema(PointerDBAdapterV2.VERSION, "Pointer ID",
@@ -96,7 +96,7 @@ abstract class PointerDBAdapter {
}
/**
- *
+ *
*/
abstract void deleteTable(DBHandle handle) throws IOException;
@@ -105,6 +105,7 @@ abstract class PointerDBAdapter {
* @param dataTypeID data type ID of the date type being pointed to
* @param categoryID the category ID of the datatype
* @param length pointer size in bytes
+ * @return the record
* @throws IOException if there was a problem accessing the database
*/
abstract DBRecord createRecord(long dataTypeID, long categoryID, int length) throws IOException;
@@ -117,6 +118,11 @@ abstract class PointerDBAdapter {
*/
abstract DBRecord getRecord(long pointerID) throws IOException;
+ /**
+ * An iterator over the records of this adapter
+ * @return the iterator
+ * @throws IOException if there was a problem accessing the database
+ */
abstract RecordIterator getRecords() throws IOException;
/**
@@ -135,51 +141,11 @@ abstract class PointerDBAdapter {
abstract void updateRecord(DBRecord record) throws IOException;
/**
- * Gets all the pointer data types that are contained in the category that
+ * Gets all the pointer data types that are contained in the category that
* have the indicated ID.
* @param categoryID the category whose pointer data types are wanted.
* @return an array of IDs for the pointer data types in the category.
* @throws IOException if the database can't be accessed.
*/
abstract Field[] getRecordIdsInCategory(long categoryID) throws IOException;
-
- DBRecord translateRecord(DBRecord rec) {
- return rec;
- }
-
- class TranslatedRecordIterator implements RecordIterator {
- private RecordIterator it;
-
- TranslatedRecordIterator(RecordIterator it) {
- this.it = it;
- }
-
- @Override
- public boolean delete() throws IOException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean hasNext() throws IOException {
- return it.hasNext();
- }
-
- @Override
- public boolean hasPrevious() throws IOException {
- return it.hasPrevious();
- }
-
- @Override
- public DBRecord next() throws IOException {
- DBRecord rec = it.next();
- return translateRecord(rec);
- }
-
- @Override
- public DBRecord previous() throws IOException {
- DBRecord rec = it.previous();
- return translateRecord(rec);
- }
- }
-
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV0.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV0.java
index 24f7f0c00f..d14661ff47 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV0.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV0.java
@@ -22,14 +22,15 @@ import ghidra.util.exception.VersionException;
/**
* Version 0 implementation for the accessing the pointer database table.
- *
+ *
*/
class PointerDBAdapterV0 extends PointerDBAdapter {
final static int VERSION = 0;
- static final int OLD_PTR_DTD_COL = 0;
- static final int OLD_PTR_SIZE_COL = 1;
+ static final int V0_PTR_DTD_COL_ = 0;
+ static final int V0_PTR_SIZE_COL = 1;
+// Keep for reference
// static final Schema SCHEMA = new Schema(VERSION, "Pointer ID",
// new Class[] {LongField.class, IntField.class},
// new String[] {"Data Type ID", "Size"});
@@ -60,7 +61,7 @@ class PointerDBAdapterV0 extends PointerDBAdapter {
@Override
RecordIterator getRecords() throws IOException {
- return new TranslatedRecordIterator(table.iterator());
+ return new TranslatedRecordIterator(table.iterator(), this);
}
@Override
@@ -79,13 +80,14 @@ class PointerDBAdapterV0 extends PointerDBAdapter {
}
@Override
- DBRecord translateRecord(DBRecord oldRec) {
+ public DBRecord translateRecord(DBRecord oldRec) {
if (oldRec == null) {
return null;
}
DBRecord rec = PointerDBAdapter.SCHEMA.createRecord(oldRec.getKey());
- rec.setLongValue(PTR_DT_ID_COL, oldRec.getLongValue(OLD_PTR_DTD_COL));
+ rec.setLongValue(PTR_DT_ID_COL, oldRec.getLongValue(V0_PTR_DTD_COL_));
rec.setLongValue(PTR_CATEGORY_COL, 0);
+ rec.setByteValue(PTR_LENGTH_COL, (byte) -1);
return rec;
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV1.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV1.java
index d6537c86b1..63e8976f81 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV1.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV1.java
@@ -26,11 +26,13 @@ import ghidra.util.exception.VersionException;
class PointerDBAdapterV1 extends PointerDBAdapter {
final static int VERSION = 1;
- static final int OLD_PTR_DT_ID_COL = 0;
- static final int OLD_PTR_CATEGORY_COL = 1;
- static final Schema V1_SCHEMA =
- new Schema(VERSION, "Pointer ID", new Field[] { LongField.INSTANCE, LongField.INSTANCE },
- new String[] { "Data Type ID", "Category ID" });
+ static final int V1_PTR_DT_ID_COL = 0;
+ static final int V1_PTR_CATEGORY_COL = 1;
+
+// Keep for reference
+// static final Schema V1_SCHEMA =
+// new Schema(VERSION, "Pointer ID", new Field[] { LongField.INSTANCE, LongField.INSTANCE },
+// new String[] { "Data Type ID", "Category ID" });
private Table table;
@@ -49,13 +51,13 @@ class PointerDBAdapterV1 extends PointerDBAdapter {
}
@Override
- DBRecord translateRecord(DBRecord oldRec) {
+ public DBRecord translateRecord(DBRecord oldRec) {
if (oldRec == null) {
return null;
}
DBRecord rec = PointerDBAdapter.SCHEMA.createRecord(oldRec.getKey());
- rec.setLongValue(PTR_DT_ID_COL, oldRec.getLongValue(OLD_PTR_DT_ID_COL));
- rec.setLongValue(PTR_CATEGORY_COL, oldRec.getLongValue(OLD_PTR_CATEGORY_COL));
+ rec.setLongValue(PTR_DT_ID_COL, oldRec.getLongValue(V1_PTR_DT_ID_COL));
+ rec.setLongValue(PTR_CATEGORY_COL, oldRec.getLongValue(V1_PTR_CATEGORY_COL));
rec.setByteValue(PTR_LENGTH_COL, (byte) -1);
return rec;
}
@@ -72,7 +74,7 @@ class PointerDBAdapterV1 extends PointerDBAdapter {
@Override
RecordIterator getRecords() throws IOException {
- return new TranslatedRecordIterator(table.iterator());
+ return new TranslatedRecordIterator(table.iterator(), this);
}
@Override
@@ -87,7 +89,7 @@ class PointerDBAdapterV1 extends PointerDBAdapter {
@Override
Field[] getRecordIdsInCategory(long categoryID) throws IOException {
- return table.findRecords(new LongField(categoryID), OLD_PTR_CATEGORY_COL);
+ return table.findRecords(new LongField(categoryID), V1_PTR_CATEGORY_COL);
}
@Override
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java
index ded3e665d4..3890b5e22a 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/PointerDBAdapterV2.java
@@ -86,7 +86,10 @@ class PointerDBAdapterV2 extends PointerDBAdapter {
@Override
void deleteTable(DBHandle handle) throws IOException {
handle.deleteTable(POINTER_TABLE_NAME);
-
}
+ @Override
+ public DBRecord translateRecord(DBRecord rec) {
+ return rec;
+ }
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapter.java
index 01f95d6868..a402775711 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapter.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/map/AddressMapDBAdapter.java
@@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
- * REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,16 +15,15 @@
*/
package ghidra.program.database.map;
-import ghidra.program.model.address.Address;
-import ghidra.program.model.address.AddressFactory;
-import ghidra.util.exception.VersionException;
-import ghidra.util.task.TaskMonitor;
-
import java.io.IOException;
import java.util.List;
import db.DBConstants;
import db.DBHandle;
+import ghidra.program.model.address.Address;
+import ghidra.program.model.address.AddressFactory;
+import ghidra.util.exception.VersionException;
+import ghidra.util.task.TaskMonitor;
/**
* Database adapter for address map
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Enum.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Enum.java
index 299df2c259..bc6cf210fc 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Enum.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/Enum.java
@@ -24,9 +24,9 @@ public interface Enum extends DataType {
/**
* Get the value for the given name.
- * @param name name of the entry
- * @return the value
- * @throws NoSuchElementException if the name does not exist in this Enum
+ * @param name name of the entry.
+ * @return the value.
+ * @throws NoSuchElementException if the name does not exist in this Enum.
*/
public long getValue(String name) throws NoSuchElementException;
@@ -39,11 +39,11 @@ public interface Enum extends DataType {
/**
* Get the comment for the given name.
- * @param name name of the entry
- * @return the comment
- * @throws NoSuchElementException if the name does not exist in this Enum
+ * @param name name of the entry.
+ * @return the comment or the empty string if the name does not exist in this enum or if no
+ * comment is set.
*/
- public String getComment(String name) throws NoSuchElementException;
+ public String getComment(String name);
/**
* Get the values of the enum entries.
@@ -52,17 +52,15 @@ public interface Enum extends DataType {
public long[] getValues();
/**
- * Get the names of the enum entries.
+ * Get the names of the enum entries. The returned names are sorted using String's natural
+ * sort order.
+ * @return the names of the enum entries.
*/
public String[] getNames();
/**
- * Get the comments of the enum entries.
- */
- public String[] getComments();
-
- /**
- * Get the number of entries in this Enum.
+ * Get the number of entries in this Enum.
+ * @return the number of entries in this Enum.
*/
public int getCount();
@@ -82,14 +80,14 @@ public interface Enum extends DataType {
public void add(String name, long value, String comment);
/**
- * Remove the enum entry with the given name.
+ * Remove the enum entry with the given name.
* @param name name of entry to remove.
*/
public void remove(String name);
/**
* Set the description for this Enum.
- * @param description
+ * @param description the description
*/
@Override
public void setDescription(String description);
@@ -98,6 +96,7 @@ public interface Enum extends DataType {
* Get enum representation of the big-endian value.
* @param bigInt BigInteger value with the appropriate sign
* @param settings integer format settings (PADDING, FORMAT, etc.)
+ * @param bitLength the bit length
* @return formatted integer string
*/
public String getRepresentation(BigInteger bigInt, Settings settings, int bitLength);
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java
index 81be3e7d7f..796883dd02 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/EnumDataType.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.
@@ -18,6 +18,8 @@ package ghidra.program.model.data;
import java.math.BigInteger;
import java.util.*;
+import org.apache.commons.lang3.StringUtils;
+
import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.database.data.DataTypeUtilities;
@@ -95,12 +97,12 @@ public class EnumDataType extends GenericDataType implements Enum {
}
@Override
- public String getComment(String valueName) throws NoSuchElementException {
- String valueNameComment = commentMap.get(valueName);
- if (valueNameComment == null) {
- valueNameComment = "";
+ public String getComment(String valueName) {
+ String comment = commentMap.get(valueName);
+ if (comment == null) {
+ comment = "";
}
- return valueNameComment;
+ return comment;
}
@Override
@@ -117,11 +119,6 @@ public class EnumDataType extends GenericDataType implements Enum {
return names;
}
- @Override
- public String[] getComments() {
- return commentMap.values().toArray(new String[commentMap.size()]);
- }
-
@Override
public int getCount() {
return nameMap.size();
@@ -129,28 +126,25 @@ public class EnumDataType extends GenericDataType implements Enum {
@Override
public void add(String valueName, long value) {
- String valueNameComment = "";
- add(valueName, value, valueNameComment);
+ add(valueName, value, null);
}
@Override
- public void add(String valueName, long value, String valueNameComment) {
+ public void add(String valueName, long value, String comment) {
bitGroups = null;
checkValue(value);
if (nameMap.containsKey(valueName)) {
throw new IllegalArgumentException(valueName + " already exists in this enum");
}
+
nameMap.put(valueName, value);
- List list = valueMap.get(value);
- if (list == null) {
- list = new ArrayList<>();
- valueMap.put(value, list);
- }
+ List list = valueMap.computeIfAbsent(value, v -> new ArrayList<>());
list.add(valueName);
- if (valueNameComment == null) {
- valueNameComment = "";
+
+ if (!StringUtils.isBlank(comment)) {
+ commentMap.put(valueName, comment);
}
- commentMap.put(valueName, valueNameComment);
+
}
private void checkValue(long value) {
@@ -180,21 +174,24 @@ public class EnumDataType extends GenericDataType implements Enum {
public void remove(String valueName) {
bitGroups = null;
Long value = nameMap.get(valueName);
- if (value != null) {
- nameMap.remove(valueName);
- List list = valueMap.get(value);
- Iterator iter = list.iterator();
- while (iter.hasNext()) {
- if (valueName.equals(iter.next())) {
- iter.remove();
- break;
- }
- }
- if (list.isEmpty()) {
- valueMap.remove(value);
- }
- commentMap.remove(valueName);
+ if (value == null) {
+ return;
}
+
+ nameMap.remove(valueName);
+ List list = valueMap.get(value);
+ Iterator iter = list.iterator();
+ while (iter.hasNext()) {
+ if (valueName.equals(iter.next())) {
+ iter.remove();
+ break;
+ }
+ }
+ if (list.isEmpty()) {
+ valueMap.remove(value);
+ }
+
+ commentMap.remove(valueName);
}
@Override
@@ -231,11 +228,11 @@ public class EnumDataType extends GenericDataType implements Enum {
public void setLength(int length) {
String[] names = getNames();
- for (String enumName : names) {
- long value = getValue(enumName);
+ for (String valueName : names) {
+ long value = getValue(valueName);
if (isTooBig(length, value)) {
throw new IllegalArgumentException("Setting the length of this Enum to a size " +
- "that cannot contain the current value for \"" + enumName + "\" of " +
+ "that cannot contain the current value for \"" + valueName + "\" of " +
Long.toHexString(value));
}
}
@@ -327,7 +324,7 @@ public class EnumDataType extends GenericDataType implements Enum {
return "0";
}
List list = getBitGroups();
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
for (BitGroup bitGroup : list) {
long subValue = bitGroup.getMask() & value;
if (subValue != 0) {
@@ -372,30 +369,45 @@ public class EnumDataType extends GenericDataType implements Enum {
if (dt == null || !(dt instanceof Enum)) {
return false;
}
- Enum enumm = (Enum) dt;
+ Enum enumm = (Enum) dt;
if (!DataTypeUtilities.equalsIgnoreConflict(name, enumm.getName()) ||
length != enumm.getLength() || getCount() != enumm.getCount()) {
return false;
}
+
+ if (!isEachValueEquivalent(enumm)) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isEachValueEquivalent(Enum enumm) {
String[] names = getNames();
String[] otherNames = enumm.getNames();
try {
for (int i = 0; i < names.length; i++) {
+ if (!names[i].equals(otherNames[i])) {
+ return false;
+ }
+
long value = getValue(names[i]);
long otherValue = enumm.getValue(names[i]);
+ if (value != otherValue) {
+ return false;
+ }
+
String comment = getComment(names[i]);
String otherComment = enumm.getComment(names[i]);
- if (!names[i].equals(otherNames[i]) || value != otherValue ||
- !comment.equals(otherComment)) {
+ if (!comment.equals(otherComment)) {
return false;
}
}
+ return true;
}
catch (NoSuchElementException e) {
return false; // named element not found
}
- return true;
}
@Override
@@ -410,14 +422,14 @@ public class EnumDataType extends GenericDataType implements Enum {
commentMap = new HashMap<>();
setLength(enumm.getLength());
String[] names = enumm.getNames();
- for (String name2 : names) {
- add(name2, enumm.getValue(name2), enumm.getComment(name2));
+ for (String valueName : names) {
+ add(valueName, enumm.getValue(valueName), enumm.getComment(valueName));
}
stateChanged(null);
}
@Override
public String getDefaultLabelPrefix() {
- return name == null ? null : name.toUpperCase();
+ return name;
}
}
diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java
index 0f1b09d39c..8b384508c5 100644
--- a/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java
+++ b/Ghidra/Test/IntegrationTest/src/screen/java/help/screenshot/DataTypeEditorsScreenShots.java
@@ -66,7 +66,7 @@ public class DataTypeEditorsScreenShots extends GhidraScreenShotGenerator {
dataTypeWindows.add(dataTypeDialog);
captureComponents(dataTypeWindows);
- closeAllWindowsAndFrames();
+ closeAllWindows();
}
private DropDownSelectionTextField> showTypeChooserDialog() throws Exception {
@@ -189,7 +189,7 @@ public class DataTypeEditorsScreenShots extends GhidraScreenShotGenerator {
waitForSwing();
captureDialog();
- closeAllWindowsAndFrames();
+ closeAllWindows();
}
@Test