From e80dedcbdf90e04de4a71ffbe28dc6555512a6e7 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Thu, 30 Sep 2021 14:44:52 -0400 Subject: [PATCH] GP-1316 - Enums - Updates for PR to add comments to enum values --- .../topics/DataTypeEditors/EnumEditor.htm | 13 +- .../DataTypeEditors/images/EnumEditor.png | Bin 18568 -> 21220 bytes .../app/merge/datatypes/DataTypePanel.java | 71 +- .../plugin/core/datamgr/editor/EnumEntry.java | 8 +- .../core/datamgr/editor/EnumTableModel.java | 4 +- .../ArchiveConverterPlugin.java | 698 ------------------ .../dataArchiveUtilities/ComplexName.java | 141 ---- .../html/EnumDataTypeHTMLRepresentation.java | 20 +- .../util/html/HTMLDataTypeRepresentation.java | 26 +- .../merge/datatypes/DataTypeMerge2Test.java | 216 +++++- .../core/datamgr/editor/EnumEditor1Test.java | 56 +- .../core/datamgr/editor/EnumEditor2Test.java | 33 +- .../ghidra/program/database/data/EnumDB.java | 97 +-- .../database/data/EnumValueDBAdapter.java | 85 ++- .../data/EnumValueDBAdapterNoTable.java | 21 +- .../database/data/EnumValueDBAdapterV0.java | 90 +-- .../database/data/EnumValueDBAdapterV1.java | 107 +++ .../database/data/PointerDBAdapter.java | 54 +- .../database/data/PointerDBAdapterV0.java | 14 +- .../database/data/PointerDBAdapterV1.java | 22 +- .../database/data/PointerDBAdapterV2.java | 5 +- .../database/map/AddressMapDBAdapter.java | 10 +- .../java/ghidra/program/model/data/Enum.java | 31 +- .../program/model/data/EnumDataType.java | 108 +-- .../DataTypeEditorsScreenShots.java | 4 +- 25 files changed, 715 insertions(+), 1219 deletions(-) delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ArchiveConverterPlugin.java delete mode 100644 Ghidra/Features/Base/src/main/java/ghidra/app/plugin/prototype/dataArchiveUtilities/ComplexName.java create mode 100644 Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/data/EnumValueDBAdapterV1.java 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 @@

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 3dfef311d2ea0e56019516bc85d335e5d63df22a..4eceff2d80f2e592aad1665249c1ed615164e530 100644 GIT binary patch literal 21220 zcmb5Wby!sG*9MA-5`w^hNSDMQ2qN7`cMU`P4$|EXDk3EzDcw0Em8ri)k8{p7vzz)Q5}=$Y-VPjV7R<2Vm?y& zK!Wft@a?&VcYQ_Rr8pT(```oco%0+rLUDw)TJA~&#)5@KOZ5Z~O?yDSs-(-SaV34@bUuuv z$c9i$KGRB@YPcwOcS)u(foK?>uVxaHA%pYEdi``APEtR^ekvHKSMD-YTqoViCs_Zb zK$AJJTK}Hw##%vwL{-G{bC)s;l)kJY)pP&B zy-E9HLA^DL;fXZ8%KrYmZfYu9*D~AuY{QE>udAYit|{AjXF+SK@{nq2Ud{~4pfzUz;t`p?IR->jdXc&fFsP%+4EJb86a7)~AY z3rc08@kFS{u+@WV;o0gFvESd{{Awn7w)T}@w>76-)?z=FByasI_ff%{^)iyYjjy~? zt%u~TcVC+2y-;lJ8mIoC%k3;|acn^4yi85jm74I&hw6aT182FDeF*jDOGIE;-QBRa z@vG3E#lXR?ugZZo+JK6RGG5puPe zU6U2sblIGW`(;PvR&#Z)X47?ZhQZnBO@mHrnlJ0-D9YU*YsMQ_Zb;c>L=LK zej0@9)4Q>Y8x=x$YTV~2`r^aww;VD!90miApD;RWJM5K2C^iaykbGPGkRxcl† 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�DEwzs%pyXczgvvb_Ity* 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 S{wJ-_YZpwc&Q%#*iTV=*t=oqH literal 18568 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 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