GP-1403-3 Improved datatype preview formatting. Datatype Diff may not

tolerate formating changes. More work is needed.
This commit is contained in:
ghidra1 2022-02-23 14:28:11 -05:00
parent 8f0589a6d8
commit 332480a422
17 changed files with 402 additions and 267 deletions

View File

@ -100,30 +100,31 @@ public class ToolTipUtils {
*/
public static HTMLDataTypeRepresentation getHTMLRepresentation(DataType dataType) {
if (dataType != null) {
if (dataType instanceof BuiltIn) {
return new DefaultDataTypeHTMLRepresentation(dataType);
}
if (dataType instanceof TypeDef) {
return new TypeDefDataTypeHTMLRepresentation((TypeDef) dataType);
}
else if (dataType instanceof Composite) {
if (dataType instanceof Composite) {
return new CompositeDataTypeHTMLRepresentation((Composite) dataType);
}
else if (dataType instanceof Enum) {
if (dataType instanceof Enum) {
return new EnumDataTypeHTMLRepresentation((Enum) dataType);
}
else if (dataType instanceof FunctionDefinition) {
if (dataType instanceof FunctionDefinition) {
return new FunctionDataTypeHTMLRepresentation((FunctionDefinition) dataType);
}
else if (dataType instanceof Pointer) {
if (dataType instanceof Pointer) {
return new PointerDataTypeHTMLRepresentation((Pointer) dataType);
}
else if (dataType instanceof Array) {
if (dataType instanceof Array) {
return new ArrayDataTypeHTMLRepresentation((Array) dataType);
}
else if (dataType instanceof BitFieldDataType) {
if (dataType instanceof BitFieldDataType) {
return new BitFieldDataTypeHTMLRepresentation((BitFieldDataType) dataType);
}
else {
return new DefaultDataTypeHTMLRepresentation(dataType);
}
return new DefaultDataTypeHTMLRepresentation(dataType);
}
return new NullDataTypeHTMLRepresentation();

View File

@ -48,7 +48,11 @@ public class DataTypeUrl {
*/
public DataTypeUrl(DataType dt) {
DataTypeManager dtm = dt.getDataTypeManager();
dataTypeManagerId = Objects.requireNonNull(dtm.getUniversalID());
if (dtm == null) {
// Tolerate tests which may produce datatypes without datatype manager
dtm = BuiltInDataTypeManager.getDataTypeManager();
}
dataTypeManagerId = dtm.getUniversalID();
dataTypeId = dt.getUniversalID();
dataTypeName = Objects.requireNonNull(dt.getName());
}

View File

@ -17,6 +17,8 @@ package ghidra.app.util.html;
import java.awt.Color;
import org.apache.commons.lang3.StringUtils;
import ghidra.app.util.ToolTipUtils;
import ghidra.app.util.html.diff.DataTypeDiff;
import ghidra.app.util.html.diff.DataTypeDiffBuilder;
@ -72,16 +74,22 @@ public class ArrayDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation
StringBuilder buffy = new StringBuilder();
DataType baseDataType = getBaseDataType();
buffy.append("Array Base Data Type: ").append(BR);
buffy.append(INDENT_OPEN);
if (baseDataType instanceof BuiltInDataType) {
String simpleName = baseDataType.getClass().getSimpleName();
buffy.append(simpleName);
addDataTypeLength(baseDataType, buffy);
String simpleName = baseDataType.getDisplayName();
buffy.append(TT_OPEN).append(simpleName).append(TT_CLOSE);
buffy.append(BR).append(INDENT_OPEN);
String description = baseDataType.getDescription();
if (!StringUtils.isBlank(description)) {
String encodedDescription =
HTMLUtilities.friendlyEncodeHTML(description);
buffy.append(encodedDescription).append(BR);
}
addDataTypeLengthAndAlignment(baseDataType, buffy);
buffy.append(INDENT_CLOSE);
}
else {
HTMLDataTypeRepresentation representation =
ToolTipUtils.getHTMLRepresentation(baseDataType);
@ -93,18 +101,15 @@ public class ArrayDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation
buffy.append(baseHTML);
if (baseHTML.indexOf(LENGTH_PREFIX) < 0) {
addDataTypeLength(baseDataType, buffy);
addDataTypeLengthAndAlignment(baseDataType, buffy);
}
}
buffy.append(INDENT_CLOSE);
return buffy.toString();
}
private ValidatableLine buildHeaderContent() {
StringBuilder buffy = new StringBuilder();
buffy.append(FORWARD_SLASH).append(FORWARD_SLASH).append(HTML_SPACE);
buffy.append(HTMLUtilities.friendlyEncodeHTML(array.getName()));
return new TextLine(buffy.toString());
}
@ -112,9 +117,9 @@ public class ArrayDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation
private ValidatableLine buildFooterContent() {
int len = array.getLength();
if (array.isZeroLength()) {
return new TextLine("Size: 0 (reported size is " + len + ")");
return new TextLine(LENGTH_PREFIX + "0 (reported length is " + len + ")");
}
return new TextLine("Size: " + len);
return new TextLine(LENGTH_PREFIX + len);
}
private String buildHTMLText(ValidatableLine header, String body, ValidatableLine info,
@ -130,13 +135,13 @@ public class ArrayDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation
headerText = wrapStringInColor(headerText, headerLine.getTextColor());
buffy.append(headerText);
buffy.append(BR);
TextLine infoLine = (TextLine) info;
buffy.append(INDENT_OPEN);
TextLine infoLine = (TextLine) info; // TODO: Should include Alignment as well as Length (using footer prevents this)
String infoText = info.getText();
infoText = wrapStringInColor(infoText, infoLine.getTextColor());
buffy.append(infoText);
buffy.append(BR).append(BR);
buffy.append(BR).append(BR).append(INDENT_CLOSE);
buffy.append(body);
return buffy.toString();

View File

@ -21,10 +21,8 @@ import java.awt.Color;
import java.util.*;
import ghidra.app.util.ToolTipUtils;
import ghidra.app.util.datatype.DataTypeUrl;
import ghidra.app.util.html.diff.*;
import ghidra.program.model.data.*;
import ghidra.util.HTMLUtilities;
import ghidra.util.StringUtilities;
public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation {
@ -40,8 +38,6 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat
protected List<ValidatableLine> alignmentText;
protected TextLine alignmentValueText;
protected static final String ALIGNMENT_VALUE_PREFIX = "Alignment: ";
private String truncatedHtmlData;
// private constructor for making diff copies
@ -90,14 +86,6 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat
return list;
}
@Override
protected TextLine buildFooterText(DataType dataType) {
if (dataType.isZeroLength()) {
return new TextLine("0");
}
return super.buildFooterText(dataType);
}
protected List<ValidatableLine> buildAlignmentText(Composite dataType) {
List<ValidatableLine> list = new ArrayList<>();
String alignStr = CompositeInternal.getMinAlignmentString(dataType);
@ -118,8 +106,20 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat
private List<ValidatableLine> buildContent(Composite comp) {
List<ValidatableLine> list = new ArrayList<>();
int count = 0;
DataTypeComponent[] components = comp.getComponents();
boolean showPadding = (comp instanceof Structure) && !comp.isPackingEnabled();
int nextPadStart = 0;
int length = comp.isZeroLength() ? 0 : comp.getLength();
DataTypeComponent[] components = comp.getDefinedComponents();
for (DataTypeComponent dataTypeComponent : components) {
int offset = dataTypeComponent.getOffset();
if (showPadding && offset > nextPadStart) {
int padLen = offset - nextPadStart;
list.add(new TextLine("-- " + padLen + " undefined bytes at offset 0x" +
Long.toHexString(nextPadStart) + " --"));
}
nextPadStart = dataTypeComponent.getEndOffset() + 1;
String fieldName = dataTypeComponent.getFieldName();
String comment = dataTypeComponent.getComment();
@ -139,6 +139,11 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat
break;
}
}
if (showPadding && length > nextPadStart) {
int padLen = length - nextPadStart;
list.add(new TextLine("-- " + padLen + " undefined bytes at offset 0x" +
Long.toHexString(nextPadStart) + " --"));
}
return list;
}
@ -162,21 +167,12 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat
String warningLine = wrapStringInColor(warning, Color.RED);
//@formatter:off
append(fullHtml, truncatedHtml, lineCount, warningLine,
BR,
BR);
append(fullHtml, truncatedHtml, lineCount++, warningLine, BR);
//@formatter:on
lineCount++;
}
//@formatter:off
append(fullHtml, truncatedHtml, lineCount, ALIGNMENT_VALUE_PREFIX,
alignmentValueText.getText(),
BR);
append(fullHtml, truncatedHtml, lineCount, LENGTH_PREFIX,
footerText.getText(),
BR, BR);
//@formatter:on
// FIXME: Only component output lines should be limited by max line count
// and not initial/critical output which could mess-up HTML ouput (e.g., indent open/close)
// header
Iterator<ValidatableLine> iterator = headerContent.iterator();
@ -187,8 +183,7 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat
text = truncateAsNecessary(text);
}
String headerLine = wrapStringInColor(text, line.getTextColor());
append(fullHtml, truncatedHtml, lineCount, headerLine);
lineCount++;
append(fullHtml, truncatedHtml, lineCount++, headerLine);
}
// "<TT> displayName</TT> { "
@ -200,42 +195,61 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat
displayNameText = wrapStringInColor(displayNameText, displayName.getTextColor());
//@formatter:off
append(fullHtml, truncatedHtml, lineCount, TT_OPEN,
append(fullHtml, truncatedHtml, lineCount++, TT_OPEN,
displayNameText,
TT_CLOSE,
HTML_SPACE,
"{",
HTML_SPACE);
//@formatter:on
lineCount++;
BR);
append(fullHtml, truncatedHtml, lineCount++,
INDENT_OPEN,
LENGTH_PREFIX,
footerText.getText(), // length
HTML_SPACE,
HTML_SPACE,
ALIGNMENT_PREFIX,
alignmentValueText.getText(),
INDENT_CLOSE);
append(fullHtml, truncatedHtml, lineCount++,
"{",
BR);
String tableOpen = "<TABLE BORDER=0 CELLSPACING=5 CELLPADDING=0>";
//@formatter:on
String tableOpen = "<TABLE BORDER=0 CELLSPACING=5 CELLPADDING=0>"; // 3 columns
fullHtml.append(tableOpen);
truncatedHtml.append(tableOpen);
iterator = bodyContent.iterator();
for (; iterator.hasNext(); lineCount++) {
iterator.hasNext();
while (iterator.hasNext()) {
StringBuilder lineBuffer = new StringBuilder();
DataTypeLine line = (DataTypeLine) iterator.next();
String typeName = generateTypeName(line, trim);
ValidatableLine line = iterator.next();
if (!(line instanceof DataTypeLine)) {
append(fullHtml, truncatedHtml, lineCount++, TR_OPEN,
"<TD COLSPAN=3><FONT COLOR=\"gray\">", TAB, TAB,
line.getText(), "</FONT>", TD_CLOSE, TR_CLOSE);
continue;
}
DataTypeLine dtLine = (DataTypeLine) line;
String typeName = generateTypeName(dtLine, trim);
int fieldLength = ToolTipUtils.LINE_LENGTH / 2;
String fieldName = line.getName();
String fieldName = dtLine.getName();
if (trim) {
fieldName = StringUtilities.trimMiddle(fieldName, fieldLength);
}
fieldName = friendlyEncodeHTML(fieldName);
fieldName = wrapStringInColor(fieldName, line.getNameColor());
fieldName = wrapStringInColor(fieldName, dtLine.getNameColor());
String typeComment = line.getComment();
String typeComment = dtLine.getComment();
if (trim) {
typeComment = truncateAsNecessary(typeComment, fieldLength);
}
typeComment = friendlyEncodeHTML(typeComment);
typeComment = wrapStringInColor(typeComment, line.getCommentColor());
typeComment = wrapStringInColor(typeComment, dtLine.getCommentColor());
// start the table row
lineBuffer.append(TR_OPEN);
@ -264,7 +278,7 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat
lineBuffer.append(TR_CLOSE);
String lineString = lineBuffer.toString();
append(fullHtml, truncatedHtml, lineCount, lineString);
append(fullHtml, truncatedHtml, lineCount++, lineString);
}
// show ellipses if needed; the truncated html is much shorter than the full html
@ -322,24 +336,15 @@ public class CompositeDataTypeHTMLRepresentation extends HTMLDataTypeRepresentat
private static String generateTypeName(DataTypeLine line, boolean trim) {
String type = line.getType();
if (true) {
type = truncateAsNecessary(line.getType());
}
type = friendlyEncodeHTML(type);
type = wrapStringInColor(type, line.getTypeColor());
if (!line.hasUniversalId()) {
return type;
}
//
// Markup the name with info for later hyperlink capability, as needed by the client
//
Color color = line.getTypeColor();
DataType dt = line.getDataType();
DataTypeUrl url = new DataTypeUrl(dt);
String wrapped = HTMLUtilities.wrapWithLinkPlaceholder(type, url.toString());
return wrapped;
if (dt != null) {
return generateTypeName(dt, color, trim);
}
String type = truncateAsNecessary(line.getType());
type = friendlyEncodeHTML(type);
return wrapStringInColor(type, line.getTypeColor());
}
// overridden to return truncated text by default

View File

@ -21,7 +21,6 @@ import java.util.Objects;
import ghidra.program.model.data.DataType;
import ghidra.util.StringUtilities;
import ghidra.util.UniversalID;
import ghidra.util.exception.AssertException;
public class DataTypeLine implements ValidatableLine {
@ -105,6 +104,11 @@ public class DataTypeLine implements ValidatableLine {
this.commentColor = commentColor;
}
@Override
public void setTextColor(Color color) {
setAllColors(color);
}
void setAllColors(Color diffColor) {
setNameColor(diffColor);
setTypeColor(diffColor);
@ -125,9 +129,10 @@ public class DataTypeLine implements ValidatableLine {
}
if (!(otherValidatableLine instanceof DataTypeLine)) {
throw new AssertException("DataTypeLine can only be matched against other " +
"DataTypeLine implementations.");
otherValidatableLine.setTextColor(invalidColor);
return;
}
DataTypeLine otherLine = (DataTypeLine) otherValidatableLine;
// note: use the other line here, so if it is a special, overridden case, then we will
@ -205,8 +210,7 @@ public class DataTypeLine implements ValidatableLine {
}
if (!(otherValidatableLine instanceof DataTypeLine)) {
throw new AssertException("DataTypeLine can only be matched against other " +
"DataTypeLine implementations.");
return false;
}
DataTypeLine otherLine = (DataTypeLine) otherValidatableLine;

View File

@ -17,8 +17,6 @@ package ghidra.app.util.html;
import java.awt.Color;
import ghidra.util.exception.AssertException;
public class EmptyDataTypeLine extends DataTypeLine implements PlaceHolderLine {
public EmptyDataTypeLine() {
super("", "", "", null);
@ -39,14 +37,8 @@ public class EmptyDataTypeLine extends DataTypeLine implements PlaceHolderLine {
return;
}
if (!(otherValidatableLine instanceof DataTypeLine)) {
throw new AssertException("DataTypeLine can only be matched against other " +
"DataTypeLine implementations.");
}
DataTypeLine otherLine = (DataTypeLine) otherValidatableLine;
// since we are the empty line, the other line is all a mismatch
otherLine.setAllColors(invalidColor);
otherValidatableLine.setTextColor(invalidColor);
}
boolean matches(DataTypeLine otherLine) {

View File

@ -15,8 +15,6 @@
*/
package ghidra.app.util.html;
import ghidra.util.exception.AssertException;
import java.awt.Color;
public class EmptyTextLine extends TextLine implements PlaceHolderLine {
@ -61,14 +59,8 @@ public class EmptyTextLine extends TextLine implements PlaceHolderLine {
return;
}
if (!(otherValidatableLine instanceof TextLine)) {
throw new AssertException("TextLine can only be matched against other "
+ "TextLine implementations.");
}
TextLine otherLine = (TextLine) otherValidatableLine;
// since we are the empty line, the other line is all a mismatch
otherLine.setTextColor(invalidColor);
otherValidatableLine.setTextColor(invalidColor);
}
@Override

View File

@ -17,8 +17,6 @@ package ghidra.app.util.html;
import java.awt.Color;
import ghidra.util.exception.AssertException;
public class EmptyVariableTextLine extends VariableTextLine implements PlaceHolderLine {
private int numberOfCharacters;
@ -54,14 +52,9 @@ public class EmptyVariableTextLine extends VariableTextLine implements PlaceHold
(otherValidatableLine instanceof EmptyVariableTextLine)) {
return;
}
if (!(otherValidatableLine instanceof VariableTextLine)) {
throw new AssertException("VariableTextLine can only be matched against other " +
"VariableTextLine implementations.");
}
VariableTextLine otherLine = (VariableTextLine) otherValidatableLine;
// since we are the empty line, the other line is all a mismatch
otherLine.setAllColors(invalidColor);
otherValidatableLine.setTextColor(invalidColor);
}
@Override

View File

@ -144,9 +144,6 @@ public class EnumDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation {
lineCount++;
}
append(fullHtml, truncatedHtml, lineCount, LENGTH_PREFIX, infoLine.getText());
append(fullHtml, truncatedHtml, lineCount, BR, BR);
// "<TT> displayName { "
String displayNameText = displayName.getText();
if (trim) {
@ -155,15 +152,17 @@ 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,
"{",
HTML_SPACE,
BR);
// TODO: show alignment
append(fullHtml, truncatedHtml, lineCount++, INDENT_OPEN, LENGTH_PREFIX, infoLine.getText(), INDENT_CLOSE);
append(fullHtml, truncatedHtml, lineCount++, "{", BR);
//@formatter:on
lineCount++;
int length = bodyLines.size();
for (int i = 0; i < length; i++, lineCount++) {

View File

@ -15,10 +15,13 @@
*/
package ghidra.app.util.html;
import static ghidra.util.HTMLUtilities.*;
import java.awt.Color;
import java.util.*;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.util.datatype.DataTypeUrl;
import ghidra.program.model.data.*;
import ghidra.util.*;
import ghidra.util.exception.AssertException;
@ -77,6 +80,7 @@ public abstract class HTMLDataTypeRepresentation {
protected static final String ELLIPSES = "...";
protected static final String LENGTH_PREFIX = "Length: ";
protected static final String ALIGNMENT_PREFIX = "Alignment: ";
protected final static String FORWARD_SLASH = "&#47;";
@ -104,12 +108,17 @@ public abstract class HTMLDataTypeRepresentation {
return buffer;
}
protected static StringBuilder addDataTypeLength(DataType dt, StringBuilder buffer) {
protected static StringBuilder addDataTypeLengthAndAlignment(DataType dt,
StringBuilder buffer) {
buffer.append(BR);
buffer.append(LENGTH_PREFIX);
buffer.append(getDataTypeLengthString(dt));
if (dt != null && dt.getLength() >= 0) {
buffer.append(HTML_SPACE);
buffer.append(HTML_SPACE);
buffer.append(ALIGNMENT_PREFIX);
buffer.append(dt.getAlignment());
}
return buffer;
}
@ -119,7 +128,7 @@ public abstract class HTMLDataTypeRepresentation {
lengthString = "<i>Unknown</i>";
}
else {
int length = dt.getLength();
int length = dt.isZeroLength() ? 0 : dt.getLength();
if (length >= 0) {
lengthString = Integer.toString(length);
}
@ -380,18 +389,115 @@ public abstract class HTMLDataTypeRepresentation {
headerLines.addAll(createCommentLines(comment, 4));
// put the path info in; don't display a floating '/' when the path is the root path
CategoryPath path = dataType.getCategoryPath();
if (!path.equals(CategoryPath.ROOT)) {
headerLines.add(new TextLine(HTMLUtilities.escapeHTML(path.getPath())));
headerLines.add(new TextLine(BR));
}
// CategoryPath path = dataType.getCategoryPath();
// if (!path.equals(CategoryPath.ROOT)) {
// headerLines.add(new TextLine(HTMLUtilities.escapeHTML(path.getPath())));
// headerLines.add(new TextLine(BR));
// }
return headerLines;
}
protected TextLine buildFooterText(DataType dataType) {
int length = dataType.getLength();
return new TextLine((length >= 0) ? Integer.toString(length) : " <i>Unsized</i>");
if (length < 0) {
return new TextLine(" <i>Unsized</i>");
}
if (dataType.isZeroLength()) {
length = 0;
}
return new TextLine(Integer.toString(length));
}
private static boolean canLinkDataType(DataType dt) {
if (dt instanceof AbstractDataType) {
return false;
}
UniversalID universalID = dt.getUniversalID();
if (universalID == null) {
return false;
}
DataTypeManager dtm = dt.getDataTypeManager();
if (dtm == null) {
return false;
}
return dtm.findDataTypeForID(dt.getUniversalID()) != null;
}
protected static String generateTypeName(DataType dt, Color color, boolean trim) {
String[] pointerArraySplit = splitPointerArray(dt);
if (pointerArraySplit != null) {
DataType baseDt = DataTypeUtils.getNamedBaseDataType(dt);
if (baseDt != null && canLinkDataType(baseDt)) {
dt = baseDt; // ok to split for link
}
else {
pointerArraySplit = null; // do not split/link
}
}
String type = dt.getName();
if (trim) {
type = truncateAsNecessary(type);
}
type = friendlyEncodeHTML(type);
if (pointerArraySplit == null && !canLinkDataType(dt)) {
type = wrapStringInColor(type, color);
return type;
}
//
// Markup the name with info for later hyperlink capability, as needed by the client
//
DataTypeUrl url = new DataTypeUrl(dt);
String wrapped = HTMLUtilities.wrapWithLinkPlaceholder(type, url.toString());
if (pointerArraySplit != null) {
wrapped += friendlyEncodeHTML(pointerArraySplit[1], false);
}
wrapped = wrapStringInColor(wrapped, color);
return wrapped;
}
private static String[] splitPointerArray(DataType dt) {
if (!(dt instanceof Pointer) && !(dt instanceof Array)) {
return null;
}
String name = dt.getName();
int lastIndex = -1;
int index = name.length() - 1;
while (true) {
if (index <= 0) {
// unexpected condition
lastIndex = -1;
break;
}
char c = name.charAt(index);
if (c == '*' || c == '[') {
lastIndex = index;
}
else if (c != ' ' && c != ']' && !Character.isDigit(c)) {
break;
}
--index;
}
if (lastIndex <= 0) {
return null;
}
if (name.charAt(lastIndex - 1) == ' ') {
--lastIndex;
}
String[] split = new String[2];
split[0] = name.substring(0, lastIndex).trim();
split[1] = name.substring(lastIndex);
return split;
}
//==================================================================================================

View File

@ -66,25 +66,30 @@ public class PointerDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio
String fullDescription = bits + " Pointer";
fullDescription = HTMLUtilities.friendlyEncodeHTML(fullDescription);
buffer.append(FORWARD_SLASH).append(FORWARD_SLASH).append(HTML_SPACE);
buffer.append(HTMLUtilities.friendlyEncodeHTML(pointer.getName()));
buffer.append(BR);
buffer.append(INDENT_OPEN);
buffer.append(fullDescription);
buffer.append(BR);
buffer.append("Size: ").append((length >= 0) ? length : "default");
addDataTypeLengthAndAlignment(pointer, buffer);
buffer.append(INDENT_CLOSE);
buffer.append(BR).append(BR);
buffer.append("Pointer Base Data Type: ").append(BR);
buffer.append(BR);
if (baseDataType instanceof BuiltInDataType) {
String simpleName = baseDataType.getClass().getSimpleName();
buffer.append(INDENT_OPEN);
buffer.append(simpleName);
addDataTypeLength(baseDataType, buffer);
String simpleName = baseDataType.getDisplayName();
buffer.append(TT_OPEN).append(simpleName).append(TT_CLOSE);
buffer.append(BR).append(INDENT_OPEN);
String description = baseDataType.getDescription();
if (!StringUtils.isBlank(description)) {
String encodedDescription =
HTMLUtilities.friendlyEncodeHTML(description);
buffer.append(encodedDescription).append(BR);
}
addDataTypeLengthAndAlignment(baseDataType, buffer);
buffer.append(INDENT_CLOSE);
}
else {
buffer.append(INDENT_OPEN);
HTMLDataTypeRepresentation representation =
ToolTipUtils.getHTMLRepresentation(baseDataType);
String baseHTML = representation.getFullHTMLContentString();
@ -95,10 +100,8 @@ public class PointerDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio
buffer.append(baseHTML);
if (baseHTML.indexOf(LENGTH_PREFIX) < 0) {
addDataTypeLength(baseDataType, buffer);
addDataTypeLengthAndAlignment(baseDataType, buffer);
}
buffer.append(INDENT_CLOSE);
}
return buffer.toString();
@ -118,7 +121,7 @@ public class PointerDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio
int length = pointer.getLength();
description += BR;
description += "Size: " + (length >= 0 ? length : "default");
description += LENGTH_PREFIX + (length >= 0 ? length : "default");
return description;
}

View File

@ -18,8 +18,6 @@ package ghidra.app.util.html;
import java.awt.Color;
import java.util.Objects;
import ghidra.util.exception.AssertException;
public class TextLine implements ValidatableLine {
private String text;
@ -50,6 +48,7 @@ public class TextLine implements ValidatableLine {
return textColor;
}
@Override
public void setTextColor(Color color) {
this.textColor = color;
}
@ -75,8 +74,7 @@ public class TextLine implements ValidatableLine {
@Override
public boolean matches(ValidatableLine otherLine) {
if (!(otherLine instanceof TextLine)) {
throw new AssertException(
"TextLine can only be matched against other " + "TextLine implementations.");
return false;
}
TextLine textLine = (TextLine) otherLine;
return text.equals(textLine.getText());
@ -93,15 +91,9 @@ public class TextLine implements ValidatableLine {
return;
}
if (!(otherLine instanceof TextLine)) {
throw new AssertException(
"TextLine can only be matched against other " + "TextLine implementations.");
}
TextLine textLine = (TextLine) otherLine;
if (!matches(textLine)) {
if (!matches(otherLine)) {
setTextColor(invalidColor);
textLine.setTextColor(invalidColor);
otherLine.setTextColor(invalidColor);
}
}

View File

@ -18,6 +18,8 @@ package ghidra.app.util.html;
import java.awt.Color;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import ghidra.app.util.ToolTipUtils;
import ghidra.app.util.html.diff.DataTypeDiff;
import ghidra.app.util.html.diff.DataTypeDiffBuilder;
@ -25,7 +27,6 @@ import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.model.data.*;
import ghidra.util.HTMLUtilities;
import ghidra.util.StringUtilities;
public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentation {
@ -45,7 +46,7 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio
this.headerContent = headerLines;
this.bodyContent = bodyLines;
List<ValidatableLine> trimmedHeaderContent = buildHeaderText(typeDef, true);
List<ValidatableLine> trimmedHeaderContent = buildHeaderText(true);
List<ValidatableLine> trimmedBodyContent = buildBodyText(getBaseDataType(), true);
truncatedHtmlData =
buildHTMLText(typeDef, warningLines, trimmedHeaderContent, trimmedBodyContent, true);
@ -55,19 +56,36 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio
this.typeDef = typeDef;
warningLines = buildWarnings();
headerContent = buildHeaderText(typeDef, false);
headerContent = buildHeaderText(false);
bodyContent = buildBodyText(getBaseDataType(), false);
originalHTMLData = buildHTMLText(typeDef, warningLines, headerContent, bodyContent, false);
List<ValidatableLine> trimmedHeaderContent = buildHeaderText(typeDef, true);
List<ValidatableLine> trimmedHeaderContent = buildHeaderText(true);
List<ValidatableLine> trimmedBodyContent = buildBodyText(getBaseDataType(), true);
truncatedHtmlData =
buildHTMLText(typeDef, warningLines, trimmedHeaderContent, trimmedBodyContent, true);
}
protected DataType getBaseDataType() {
return getBaseDataType(typeDef);
private DataType getBaseDataType() {
DataType baseDataType = typeDef;
while (!(baseDataType instanceof BuiltInDataType) && (baseDataType instanceof TypeDef)) {
TypeDef td = (TypeDef) baseDataType;
baseDataType = getBasePointerArrayDataType(td.getDataType());
}
return baseDataType;
}
private static DataType getBasePointerArrayDataType(DataType dt) {
while ((dt instanceof Pointer) || (dt instanceof Array)) {
if (dt instanceof Pointer) {
dt = ((Pointer) dt).getDataType();
}
else {
dt = ((Array) dt).getDataType();
}
}
return dt;
}
// overridden to return truncated text by default
@ -82,14 +100,6 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio
return truncatedHtmlData;
}
private static DataType getBaseDataType(DataType dataType) {
DataType basedataType = dataType;
while (basedataType instanceof TypeDef) {
basedataType = ((TypeDef) basedataType).getDataType();
}
return basedataType;
}
protected List<String> buildWarnings() {
DataType baseType = typeDef.getBaseDataType();
if (!(baseType instanceof Composite) || !baseType.isZeroLength()) {
@ -109,64 +119,84 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio
return super.buildFooterText(dataType);
}
protected List<ValidatableLine> buildHeaderText(DataType dataType, boolean trim) {
DataType baseDataType = typeDef;
private String getDataTypeNameHTML(TypeDef td, boolean trim) {
String name = td.getName();
if (trim) {
name = truncateAsNecessary(name);
}
name = HTMLUtilities.friendlyEncodeHTML(name);
StringBuilder buffy = new StringBuilder(TT_OPEN);
if (td.isAutoNamed()) {
buffy.append("auto-typedef ");
buffy.append(name);
}
else {
buffy.append("typedef ");
buffy.append(td != typeDef ? generateTypeName(td, null, trim) : name);
buffy.append(" ");
buffy.append(generateTypeName(td.getDataType(), null, trim));
}
buffy.append(TT_CLOSE).append(BR);
return buffy.toString();
}
protected List<ValidatableLine> buildHeaderText(boolean trim) {
DataType baseDataType = typeDef.getDataType();
List<ValidatableLine> lines = new ArrayList<>();
while (baseDataType instanceof TypeDef) {
lines.add(new TextLine(getDataTypeNameHTML(typeDef, trim)));
if (!typeDef.isAutoNamed()) {
// Show modified default settings details (i.e., TypeDefSettingsDefinition)
StringBuilder buffy = new StringBuilder();
String baseDtString = baseDataType.toString();
if (trim) {
baseDtString = StringUtilities.trimMiddle(baseDtString, ToolTipUtils.LINE_LENGTH);
Settings defaultSettings = typeDef.getDefaultSettings();
for (SettingsDefinition settingsDef : typeDef.getSettingsDefinitions()) {
if (!(settingsDef instanceof TypeDefSettingsDefinition) ||
!settingsDef.hasValue(defaultSettings)) {
continue;
}
if (buffy.length() == 0) {
buffy.append(INDENT_OPEN);
}
else {
buffy.append(BR);
}
buffy.append(TT_OPEN)
.append(settingsDef.getName())
.append(": ")
.append(settingsDef.getValueString(defaultSettings));
buffy.append(TT_CLOSE);
}
String encodedBaseDt = HTMLUtilities.friendlyEncodeHTML(baseDtString);
buffy.append(TT_OPEN).append(encodedBaseDt).append(TT_CLOSE).append(BR);
lines.add(new TextLine(buffy.toString()));
baseDataType = ((TypeDef) baseDataType).getDataType();
while (baseDataType instanceof Pointer) {
baseDataType = ((Pointer) baseDataType).getDataType();
if (buffy.length() != 0) {
buffy.append(INDENT_CLOSE);
lines.add(new TextLine(buffy.toString()));
}
}
baseDataType = typeDef.getBaseDataType();
if (baseDataType instanceof Pointer || baseDataType instanceof Array) {
String lengthAndAlignmentStr =
addDataTypeLengthAndAlignment(typeDef, new StringBuilder()).toString();
lines.add(new TextLine(INDENT_OPEN + lengthAndAlignmentStr + INDENT_CLOSE));
}
// Show modified default settings details
StringBuilder buffy = new StringBuilder();
Settings defaultSettings = typeDef.getDefaultSettings();
HashSet<Class<?>> ignoredSettings = new HashSet<>();
for (SettingsDefinition settingsDef : typeDef.getSettingsDefinitions()) {
if (!(settingsDef instanceof TypeDefSettingsDefinition) ||
!settingsDef.hasValue(defaultSettings)) {
continue;
}
if (settingsDef instanceof PointerTypeSettingsDefinition) {
ignoredSettings.add(AddressSpaceSettingsDefinition.class);
baseDataType = getBasePointerArrayDataType(baseDataType);
boolean firstBaseTypedef = true;
while (baseDataType instanceof TypeDef) {
TypeDef td = (TypeDef) baseDataType;
if (!td.isAutoNamed()) {
String br = "";
if (firstBaseTypedef) {
br = "<BR>";
firstBaseTypedef = false;
}
lines.add(new TextLine(br + getDataTypeNameHTML(td, trim)));
}
baseDataType = getBasePointerArrayDataType(td.getDataType());
}
for (SettingsDefinition settingsDef : typeDef.getSettingsDefinitions()) {
if (!(settingsDef instanceof TypeDefSettingsDefinition) ||
!settingsDef.hasValue(defaultSettings)) {
continue;
}
boolean ignored = ignoredSettings.contains(settingsDef.getClass());
if (buffy.length() == 0) {
buffy.append(INDENT_OPEN);
}
else {
buffy.append(BR);
}
buffy.append(TT_OPEN)
.append(settingsDef.getName())
.append(": ")
.append(settingsDef.getValueString(defaultSettings));
if (ignored) {
buffy.append(" (ignored)");
}
buffy.append(TT_CLOSE);
}
if (buffy.length() != 0) {
buffy.append(INDENT_CLOSE);
lines.add(new TextLine(buffy.toString()));
}
return lines;
}
@ -175,7 +205,7 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio
if (baseDataType instanceof BuiltInDataType) {
buildHTMLTextForBuiltIn(lines, baseDataType);
}
else {
else if (baseDataType != null) {
buildHTMLTextForBaseDataType(lines, baseDataType, trim);
}
return lines;
@ -208,10 +238,6 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio
// body
buffy.append(BR);
if (typeDef.isPointer()) {
buffy.append("Pointer-");
}
buffy.append("TypeDef Base Data Type: ").append(BR);
iterator = bodyLines.iterator();
for (; iterator.hasNext();) {
@ -227,33 +253,33 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio
}
private static void buildHTMLTextForBuiltIn(List<ValidatableLine> lines,
DataType basedataType) {
lines.add(new TextLine(INDENT_OPEN));
DataType baseDataType) {
lines.add(new TextLine(TT_OPEN));
String dataTypeDescriptionOrName = getDataTypeDescriptionOrName(basedataType);
String encodedDescriptionOrName =
HTMLUtilities.friendlyEncodeHTML(dataTypeDescriptionOrName);
lines.add(new TextLine(encodedDescriptionOrName));
String encodedName =
HTMLUtilities.friendlyEncodeHTML(baseDataType.getDisplayName());
lines.add(new TextLine(encodedName));
lines.add(new TextLine(TT_CLOSE));
StringBuilder buffy = addDataTypeLength(basedataType, new StringBuilder());
lines.add(new TextLine(buffy.toString()));
lines.add(new TextLine(BR));
lines.add(new TextLine(INDENT_OPEN));
String description = baseDataType.getDescription();
if (!StringUtils.isBlank(description)) {
String encodedDescription =
HTMLUtilities.friendlyEncodeHTML(description);
lines.add(new TextLine(encodedDescription));
lines.add(new TextLine(BR));
}
lines.add(new TextLine(
addDataTypeLengthAndAlignment(baseDataType, new StringBuilder()).toString()));
lines.add(new TextLine(INDENT_CLOSE));
}
private static String getDataTypeDescriptionOrName(DataType dataType) {
String description = dataType.getDescription();
if (description == null || description.length() == 0) {
return dataType.getName();
}
return description;
}
private static void buildHTMLTextForBaseDataType(List<ValidatableLine> lines,
DataType basedataType, boolean trim) {
lines.add(new TextLine(INDENT_OPEN));
DataType baseDataType, boolean trim) {
HTMLDataTypeRepresentation baseRepresentation =
ToolTipUtils.getHTMLRepresentation(basedataType);
ToolTipUtils.getHTMLRepresentation(baseDataType);
String baseHTML = baseRepresentation.getFullHTMLContentString();
if (trim) {
@ -262,12 +288,11 @@ public class TypeDefDataTypeHTMLRepresentation extends HTMLDataTypeRepresentatio
lines.add(new TextLine(baseHTML));
if (baseHTML.indexOf(LENGTH_PREFIX) < 0) {
StringBuilder buffy = addDataTypeLength(basedataType, new StringBuilder());
if (baseHTML.indexOf(LENGTH_PREFIX) < 0 && baseDataType.getLength() >= 0) {
StringBuilder buffy = new StringBuilder();
addDataTypeLengthAndAlignment(baseDataType, buffy);
lines.add(new TextLine(buffy.toString()));
}
lines.add(new TextLine(INDENT_CLOSE));
}
@Override

View File

@ -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.
@ -39,6 +38,12 @@ public interface ValidatableLine {
public String getText();
/**
* Set color for all text.
* @param color text color
*/
public void setTextColor(Color color);
/**
* Sets the other line that this line is validated against. The other line may be a full,
* partial, or no match at all.

View File

@ -20,7 +20,6 @@ import java.util.Objects;
import ghidra.program.model.data.DataType;
import ghidra.util.UniversalID;
import ghidra.util.exception.AssertException;
public class VariableTextLine implements ValidatableLine {
@ -117,8 +116,7 @@ public class VariableTextLine implements ValidatableLine {
}
if (!(otherValidatableLine instanceof VariableTextLine)) {
throw new AssertException("VariableTextLine can only be matched against other " +
"VariableTextLine implementations.");
return false;
}
VariableTextLine otherLine = (VariableTextLine) otherValidatableLine;
@ -146,8 +144,8 @@ public class VariableTextLine implements ValidatableLine {
}
if (!(otherValidatableLine instanceof VariableTextLine)) {
throw new AssertException("VariableTextLine can only be matched against other " +
"VariableTextLine implementations.");
otherValidatableLine.setTextColor(invalidColor);
return;
}
VariableTextLine otherLine = (VariableTextLine) otherValidatableLine;
@ -162,6 +160,11 @@ public class VariableTextLine implements ValidatableLine {
}
}
@Override
public void setTextColor(Color color) {
setAllColors(color);
}
void setAllColors(Color color) {
variableTypeColor = color;
variableNameColor = color;

View File

@ -813,19 +813,25 @@ public class HTMLDataTypeRepresentationTest extends AbstractGenericTest {
List<ValidatableLine> h1 = td1.headerContent;
List<ValidatableLine> h2 = td2.headerContent;
Assert.assertNotEquals("TypeDef diff should have different headers", h1, h2);
Assert.assertNotEquals("Typedef diff should have different headers", h1, h2);
List<ValidatableLine> b1 = td1.bodyContent;
List<ValidatableLine> b2 = td2.bodyContent;
// List<ValidatableLine> b1 = td1.bodyContent;
// List<ValidatableLine> b2 = td2.bodyContent;
// crude, but effective
String s1 = b1.toString();
String s2 = b2.toString();
String s1 = h1.toString();
String s2 = h2.toString();
String size1 = s1.replaceAll(".*Size: (\\d+).*", "$1");
String size2 = s2.replaceAll(".*Size: (\\d+).*", "$1");
Pattern p = Pattern.compile(".*Length: (\\d+).*");
Matcher m1 = p.matcher(s1);
assertTrue("Typedef length not found", m1.find());
String size1 = m1.group(1);
Assert.assertNotEquals("TypeDef diff should have different Size values", size1, size2);
Matcher m2 = p.matcher(s2);
assertTrue("Typedef length not found", m2.find());
String size2 = m2.group(1);
Assert.assertNotEquals("Typedef diff should have different Length values", size1, size2);
}
private void assertTypeDefsSame(HTMLDataTypeRepresentation[] diff) {

View File

@ -526,7 +526,7 @@ public class HTMLUtilities {
* This is useful when line wrapping to force wrapped lines to the left
* @return the encoded HTML string
*/
private static String friendlyEncodeHTML(String text, boolean skipLeadingWhitespace) {
public static String friendlyEncodeHTML(String text, boolean skipLeadingWhitespace) {
StringBuilder buffer = new StringBuilder();