mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch 'origin/GP-5272-dragonmacher-comment-fix--SQUASHED' into Ghidra_11.3
This commit is contained in:
commit
cb9098e382
10 changed files with 235 additions and 243 deletions
|
@ -73,8 +73,8 @@ public class SetCommentCmd implements Command<Program> {
|
||||||
" Is this address valid?";
|
" Is this address valid?";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String updatedComment = CommentUtils.fixupAnnotations(comment, program);
|
|
||||||
updatedComment = CommentUtils.sanitize(updatedComment);
|
String updatedComment = CommentUtils.sanitize(comment);
|
||||||
if (commentChanged(cu.getComment(commentType), updatedComment)) {
|
if (commentChanged(cu.getComment(commentType), updatedComment)) {
|
||||||
cu.setComment(commentType, updatedComment);
|
cu.setComment(commentType, updatedComment);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ import docking.widgets.OptionDialog;
|
||||||
import docking.widgets.checkbox.GCheckBox;
|
import docking.widgets.checkbox.GCheckBox;
|
||||||
import docking.widgets.combobox.GComboBox;
|
import docking.widgets.combobox.GComboBox;
|
||||||
import ghidra.app.util.viewer.field.AnnotatedStringHandler;
|
import ghidra.app.util.viewer.field.AnnotatedStringHandler;
|
||||||
import ghidra.app.util.viewer.field.Annotation;
|
import ghidra.app.util.viewer.field.CommentUtils;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.model.listing.CodeUnit;
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
|
@ -225,7 +225,7 @@ public class CommentsDialog extends ReusableDialogComponentProvider implements K
|
||||||
}
|
}
|
||||||
|
|
||||||
private AnnotationAdapterWrapper[] getAnnotationAdapterWrappers() {
|
private AnnotationAdapterWrapper[] getAnnotationAdapterWrappers() {
|
||||||
List<AnnotatedStringHandler> annotations = Annotation.getAnnotatedStringHandlers();
|
List<AnnotatedStringHandler> annotations = CommentUtils.getAnnotatedStringHandlers();
|
||||||
int count = annotations.size();
|
int count = annotations.size();
|
||||||
AnnotationAdapterWrapper[] retVal = new AnnotationAdapterWrapper[count];
|
AnnotationAdapterWrapper[] retVal = new AnnotationAdapterWrapper[count];
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
|
|
|
@ -15,134 +15,36 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.viewer.field;
|
package ghidra.app.util.viewer.field;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import docking.widgets.fieldpanel.field.AttributedString;
|
|
||||||
import ghidra.app.nav.Navigatable;
|
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.classfinder.ClassSearcher;
|
|
||||||
|
|
||||||
public class Annotation {
|
public class Annotation {
|
||||||
|
|
||||||
public static final String ESCAPABLE_CHARS = "{}\"\\";
|
public static final String ESCAPABLE_CHARS = "{}\"\\";
|
||||||
|
|
||||||
private static List<AnnotatedStringHandler> ANNOTATED_STRING_HANDLERS;
|
|
||||||
private static Map<String, AnnotatedStringHandler> ANNOTATED_STRING_MAP;
|
|
||||||
|
|
||||||
private String annotationText;
|
private String annotationText;
|
||||||
private String[] annotationParts;
|
private String[] annotationParts;
|
||||||
private AnnotatedStringHandler annotatedStringHandler;
|
|
||||||
private AttributedString displayString;
|
|
||||||
|
|
||||||
public static List<AnnotatedStringHandler> getAnnotatedStringHandlers() {
|
|
||||||
if (ANNOTATED_STRING_HANDLERS == null) {
|
|
||||||
ANNOTATED_STRING_HANDLERS = getSupportedAnnotationHandlers();
|
|
||||||
}
|
|
||||||
return ANNOTATED_STRING_HANDLERS;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<String, AnnotatedStringHandler> getAnnotatedStringHandlerMap() {
|
|
||||||
if (ANNOTATED_STRING_MAP == null) { // lazy init due to our use of ClassSearcher
|
|
||||||
ANNOTATED_STRING_MAP = createAnnotatedStringHandlerMap();
|
|
||||||
}
|
|
||||||
return ANNOTATED_STRING_MAP;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<String, AnnotatedStringHandler> createAnnotatedStringHandlerMap() {
|
|
||||||
Map<String, AnnotatedStringHandler> map = new HashMap<>();
|
|
||||||
for (AnnotatedStringHandler instance : getAnnotatedStringHandlers()) {
|
|
||||||
String[] supportedAnnotations = instance.getSupportedAnnotations();
|
|
||||||
for (String supportedAnnotation : supportedAnnotations) {
|
|
||||||
map.put(supportedAnnotation, instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableMap(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
// locates AnnotatedStringHandler implementations to handle annotations
|
|
||||||
private static List<AnnotatedStringHandler> getSupportedAnnotationHandlers() {
|
|
||||||
List<AnnotatedStringHandler> list = new ArrayList<>();
|
|
||||||
for (AnnotatedStringHandler h : ClassSearcher.getInstances(AnnotatedStringHandler.class)) {
|
|
||||||
if (h.getSupportedAnnotations().length != 0) {
|
|
||||||
list.add(h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Collections.unmodifiableList(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* <b>Note</b>: This constructor assumes that the string starts with "{<pre>@</pre>" and ends with '}'
|
* <b>Note</b>: This constructor assumes that the string starts with "{<pre>@</pre>" and ends with '}'
|
||||||
*
|
*
|
||||||
* @param annotationText The complete annotation text.
|
* @param annotationText The complete annotation text.
|
||||||
* @param prototypeString An AttributedString that provides the attributes for the display
|
|
||||||
* text this Annotation can create
|
* text this Annotation can create
|
||||||
* @param program the program
|
* @param program the program
|
||||||
*/
|
*/
|
||||||
public Annotation(String annotationText, AttributedString prototypeString, Program program) {
|
public Annotation(String annotationText, Program program) {
|
||||||
|
|
||||||
this.annotationText = annotationText;
|
this.annotationText = annotationText;
|
||||||
annotationParts = parseAnnotationText(annotationText);
|
this.annotationParts = parseAnnotationText(annotationText);
|
||||||
|
|
||||||
annotatedStringHandler = getHandler(annotationParts);
|
|
||||||
|
|
||||||
try {
|
|
||||||
displayString = annotatedStringHandler.createAnnotatedString(prototypeString,
|
|
||||||
annotationParts, program);
|
|
||||||
}
|
|
||||||
catch (AnnotationException ae) {
|
|
||||||
// uh-oh
|
|
||||||
annotatedStringHandler =
|
|
||||||
new InvalidAnnotatedStringHandler("Annotation Exception: " + ae.getMessage());
|
|
||||||
displayString = annotatedStringHandler.createAnnotatedString(prototypeString,
|
|
||||||
annotationParts, program);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AnnotatedStringHandler getHandler(String[] annotationPieces) {
|
public String[] getAnnotationParts() {
|
||||||
|
|
||||||
if (annotationPieces.length <= 1) {
|
|
||||||
return new InvalidAnnotatedStringHandler(
|
|
||||||
"Invalid annotation format." + " Expected at least two strings.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// the first part is the annotation (@xxx)
|
|
||||||
String keyword = annotationPieces[0];
|
|
||||||
AnnotatedStringHandler handler = getAnnotatedStringHandlerMap().get(keyword);
|
|
||||||
|
|
||||||
if (handler == null) {
|
|
||||||
return new InvalidAnnotatedStringHandler("Invalid annotation keyword: " + keyword);
|
|
||||||
}
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] getAnnotationParts() {
|
|
||||||
return annotationParts;
|
return annotationParts;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnnotatedStringHandler getHandler() {
|
|
||||||
return annotatedStringHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttributedString getDisplayString() {
|
|
||||||
return displayString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a mouse click occurs on a FieldElement containing this Annotation.
|
|
||||||
*
|
|
||||||
* @param sourceNavigatable The source navigatable associated with the mouse click.
|
|
||||||
* @param serviceProvider The service provider to be used when creating
|
|
||||||
* {@link AnnotatedStringHandler} instances.
|
|
||||||
* @return true if the handler desires to handle the mouse click.
|
|
||||||
*/
|
|
||||||
public boolean handleMouseClick(Navigatable sourceNavigatable,
|
|
||||||
ServiceProvider serviceProvider) {
|
|
||||||
return annotatedStringHandler.handleMouseClick(annotationParts, sourceNavigatable,
|
|
||||||
serviceProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAnnotationText() {
|
public String getAnnotationText() {
|
||||||
return annotationText;
|
return annotationText;
|
||||||
}
|
}
|
||||||
|
@ -152,10 +54,6 @@ public class Annotation {
|
||||||
return annotationText;
|
return annotationText;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ static Set<String> getAnnotationNames() {
|
|
||||||
return Collections.unmodifiableSet(getAnnotatedStringHandlerMap().keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] parseAnnotationText(String text) {
|
private String[] parseAnnotationText(String text) {
|
||||||
|
|
||||||
String trimmed = text.substring(2, text.length() - 1); // remove "{@" and '}'
|
String trimmed = text.substring(2, text.length() - 1); // remove "{@" and '}'
|
||||||
|
|
|
@ -15,9 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.viewer.field;
|
package ghidra.app.util.viewer.field;
|
||||||
|
|
||||||
import docking.widgets.fieldpanel.field.AbstractTextFieldElement;
|
|
||||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
|
||||||
|
|
||||||
public class AnnotationCommentPart extends CommentPart {
|
public class AnnotationCommentPart extends CommentPart {
|
||||||
|
|
||||||
private Annotation annotation;
|
private Annotation annotation;
|
||||||
|
@ -32,13 +29,12 @@ public class AnnotationCommentPart extends CommentPart {
|
||||||
return annotation.getAnnotationText();
|
return annotation.getAnnotationText();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
AbstractTextFieldElement createElement(int row, int column) {
|
|
||||||
return new AnnotatedTextFieldElement(annotation, row, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return annotation.toString();
|
return annotation.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Annotation getAnnotation() {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.viewer.field;
|
package ghidra.app.util.viewer.field;
|
||||||
|
|
||||||
import docking.widgets.fieldpanel.field.AbstractTextFieldElement;
|
|
||||||
|
|
||||||
public abstract class CommentPart {
|
public abstract class CommentPart {
|
||||||
|
|
||||||
protected String displayText;
|
protected String displayText;
|
||||||
|
@ -25,8 +23,6 @@ public abstract class CommentPart {
|
||||||
this.displayText = displayText;
|
this.displayText = displayText;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract AbstractTextFieldElement createElement(int row, int column);
|
|
||||||
|
|
||||||
abstract String getRawText();
|
abstract String getRawText();
|
||||||
|
|
||||||
String getDisplayText() {
|
String getDisplayText() {
|
||||||
|
|
|
@ -16,9 +16,8 @@
|
||||||
package ghidra.app.util.viewer.field;
|
package ghidra.app.util.viewer.field;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -28,15 +27,24 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
import docking.widgets.fieldpanel.field.*;
|
import docking.widgets.fieldpanel.field.*;
|
||||||
import generic.theme.GThemeDefaults.Colors;
|
import generic.theme.GThemeDefaults.Colors;
|
||||||
import generic.theme.Gui;
|
import generic.theme.Gui;
|
||||||
|
import ghidra.app.util.NamespaceUtils;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.model.symbol.Symbol;
|
||||||
|
import ghidra.program.model.symbol.SymbolTable;
|
||||||
import ghidra.util.StringUtilities;
|
import ghidra.util.StringUtilities;
|
||||||
import ghidra.util.WordLocation;
|
import ghidra.util.WordLocation;
|
||||||
|
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||||
|
import ghidra.util.classfinder.ClassSearcher;
|
||||||
|
|
||||||
public class CommentUtils {
|
public class CommentUtils {
|
||||||
|
|
||||||
// looks like: {@sym|symbol|...
|
// looks like: {@sym|symbol|...
|
||||||
private static final Pattern ANNOTATION_START_PATTERN = createAnnotationStartPattern();
|
private static final Pattern ANNOTATION_START_PATTERN = createAnnotationStartPattern();
|
||||||
|
|
||||||
|
private static List<AnnotatedStringHandler> ANNOTATED_STRING_HANDLERS;
|
||||||
|
private static Map<String, AnnotatedStringHandler> ANNOTATED_STRING_MAP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes adjustments as necessary to any annotations in the given text.
|
* Makes adjustments as necessary to any annotations in the given text.
|
||||||
*
|
*
|
||||||
|
@ -50,31 +58,28 @@ public class CommentUtils {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
AttributedString prototype = createPrototype();
|
|
||||||
|
|
||||||
// this function will take any given Symbol annotations and change the text, replacing
|
// this function will take any given Symbol annotations and change the text, replacing
|
||||||
// the symbol name with the address of the symbol
|
// the symbol name with the address of the symbol
|
||||||
Function<Annotation, Annotation> symbolFixer = annotation -> {
|
Function<Annotation, Annotation> symbolFixer = annotation -> {
|
||||||
|
|
||||||
AnnotatedStringHandler handler = annotation.getHandler();
|
String[] annotationParts = annotation.getAnnotationParts();
|
||||||
|
AnnotatedStringHandler handler = getAnnotationHandler(annotationParts);
|
||||||
if (!(handler instanceof SymbolAnnotatedStringHandler)) {
|
if (!(handler instanceof SymbolAnnotatedStringHandler)) {
|
||||||
return annotation; // nothing to change
|
return annotation; // nothing to change
|
||||||
}
|
}
|
||||||
|
|
||||||
String rawText = annotation.getAnnotationText();
|
String rawText = annotation.getAnnotationText();
|
||||||
String[] annotationParts = annotation.getAnnotationParts();
|
String updatedText =
|
||||||
String updatedText = SymbolAnnotatedStringHandler.convertAnnotationSymbolToAddress(
|
convertAnnotationSymbolToAddress(annotationParts, rawText, program);
|
||||||
annotationParts, rawText, program);
|
|
||||||
if (updatedText == null) {
|
if (updatedText == null) {
|
||||||
return annotation; // nothing to change
|
return annotation; // nothing to change
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Annotation(updatedText, prototype, program);
|
return new Annotation(updatedText, program);
|
||||||
};
|
};
|
||||||
|
|
||||||
StringBuilder buffy = new StringBuilder();
|
StringBuilder buffy = new StringBuilder();
|
||||||
List<CommentPart> parts =
|
List<CommentPart> parts = doParseTextIntoParts(rawCommentText, symbolFixer, program);
|
||||||
doParseTextIntoTextAndAnnotations(rawCommentText, symbolFixer, program, prototype);
|
|
||||||
for (CommentPart part : parts) {
|
for (CommentPart part : parts) {
|
||||||
buffy.append(part.getRawText());
|
buffy.append(part.getRawText());
|
||||||
}
|
}
|
||||||
|
@ -121,7 +126,7 @@ public class CommentUtils {
|
||||||
AttributedString prototypeString, int row) {
|
AttributedString prototypeString, int row) {
|
||||||
|
|
||||||
Function<Annotation, Annotation> noFixing = Function.identity();
|
Function<Annotation, Annotation> noFixing = Function.identity();
|
||||||
return doParseTextForAnnotations(text, noFixing, program, prototypeString, row);
|
return createFieldElementForAnnotations(text, noFixing, program, prototypeString, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,7 +159,7 @@ public class CommentUtils {
|
||||||
* @param row the row of the newly created FieldElement
|
* @param row the row of the newly created FieldElement
|
||||||
* @return A field element containing {@link AttributedString}s
|
* @return A field element containing {@link AttributedString}s
|
||||||
*/
|
*/
|
||||||
private static FieldElement doParseTextForAnnotations(String text,
|
private static FieldElement createFieldElementForAnnotations(String text,
|
||||||
Function<Annotation, Annotation> fixerUpper, Program program,
|
Function<Annotation, Annotation> fixerUpper, Program program,
|
||||||
AttributedString prototype, int row) {
|
AttributedString prototype, int row) {
|
||||||
|
|
||||||
|
@ -163,16 +168,30 @@ public class CommentUtils {
|
||||||
|
|
||||||
int column = 0;
|
int column = 0;
|
||||||
List<CommentPart> parts =
|
List<CommentPart> parts =
|
||||||
doParseTextIntoTextAndAnnotations(text, fixerUpper, program, prototype);
|
doParseTextIntoParts(text, fixerUpper, program);
|
||||||
List<FieldElement> fields = new ArrayList<>();
|
List<FieldElement> fields = new ArrayList<>();
|
||||||
for (CommentPart part : parts) {
|
for (CommentPart part : parts) {
|
||||||
fields.add(part.createElement(row, column));
|
|
||||||
|
FieldElement f = createElement(part, prototype, program, row, column);
|
||||||
|
fields.add(f);
|
||||||
column += part.getDisplayText().length();
|
column += part.getDisplayText().length();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CompositeFieldElement(fields.toArray(new FieldElement[fields.size()]));
|
return new CompositeFieldElement(fields.toArray(new FieldElement[fields.size()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static FieldElement createElement(CommentPart part, AttributedString prototype,
|
||||||
|
Program p, int row, int column) {
|
||||||
|
|
||||||
|
if (part instanceof AnnotationCommentPart annotationPart) {
|
||||||
|
Annotation annotation = annotationPart.getAnnotation();
|
||||||
|
return new AnnotatedTextFieldElement(annotation, prototype, p, row, column);
|
||||||
|
}
|
||||||
|
|
||||||
|
AttributedString as = prototype.deriveAttributedString(part.getDisplayText());
|
||||||
|
return new TextFieldElement(as, row, column);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split the given text into parts where the returned list contains either a String or
|
* Split the given text into parts where the returned list contains either a String or
|
||||||
* an Annotation
|
* an Annotation
|
||||||
|
@ -180,18 +199,16 @@ public class CommentUtils {
|
||||||
* @param text the text to parse
|
* @param text the text to parse
|
||||||
* @param fixerUpper a function that is given a chance to convert an Annotation into a new one
|
* @param fixerUpper a function that is given a chance to convert an Annotation into a new one
|
||||||
* @param program the program
|
* @param program the program
|
||||||
* @param prototype the prototype string that contains decoration attributes
|
|
||||||
* @return a list that contains a mixture String or an Annotation entries
|
* @return a list that contains a mixture String or an Annotation entries
|
||||||
*/
|
*/
|
||||||
private static List<CommentPart> doParseTextIntoTextAndAnnotations(String text,
|
private static List<CommentPart> doParseTextIntoParts(String text,
|
||||||
Function<Annotation, Annotation> fixerUpper, Program program,
|
Function<Annotation, Annotation> fixerUpper, Program program) {
|
||||||
AttributedString prototype) {
|
|
||||||
|
|
||||||
List<CommentPart> results = new ArrayList<>();
|
List<CommentPart> results = new ArrayList<>();
|
||||||
|
|
||||||
List<WordLocation> annotations = getCommentAnnotations(text);
|
List<WordLocation> annotations = getCommentAnnotations(text);
|
||||||
if (annotations.isEmpty()) {
|
if (annotations.isEmpty()) {
|
||||||
results.add(new StringCommentPart(text, prototype));
|
results.add(new StringCommentPart(text));
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,11 +220,11 @@ public class CommentUtils {
|
||||||
if (offset != start) {
|
if (offset != start) {
|
||||||
// text between annotations
|
// text between annotations
|
||||||
String preceding = text.substring(offset, start);
|
String preceding = text.substring(offset, start);
|
||||||
results.add(new StringCommentPart(preceding, prototype));
|
results.add(new StringCommentPart(preceding));
|
||||||
}
|
}
|
||||||
|
|
||||||
String annotationText = word.getWord();
|
String annotationText = word.getWord();
|
||||||
Annotation annotation = new Annotation(annotationText, prototype, program);
|
Annotation annotation = new Annotation(annotationText, program);
|
||||||
annotation = fixerUpper.apply(annotation);
|
annotation = fixerUpper.apply(annotation);
|
||||||
results.add(new AnnotationCommentPart(annotationText, annotation));
|
results.add(new AnnotationCommentPart(annotationText, annotation));
|
||||||
|
|
||||||
|
@ -216,7 +233,7 @@ public class CommentUtils {
|
||||||
|
|
||||||
if (offset != text.length()) { // trailing text
|
if (offset != text.length()) { // trailing text
|
||||||
String trailing = text.substring(offset);
|
String trailing = text.substring(offset);
|
||||||
results.add(new StringCommentPart(trailing, prototype));
|
results.add(new StringCommentPart(trailing));
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
@ -249,7 +266,7 @@ public class CommentUtils {
|
||||||
|
|
||||||
private static Pattern createAnnotationStartPattern() {
|
private static Pattern createAnnotationStartPattern() {
|
||||||
|
|
||||||
Set<String> names = Annotation.getAnnotationNames();
|
Set<String> names = getAnnotationNames();
|
||||||
String namePatternString = StringUtils.join(names, "|");
|
String namePatternString = StringUtils.join(names, "|");
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
|
@ -304,4 +321,123 @@ public class CommentUtils {
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String convertAnnotationSymbolToAddress(String[] annotationParts, String rawText,
|
||||||
|
Program program) {
|
||||||
|
if (annotationParts.length <= 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (program == null) { // this can happen during merge operations
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Address address = program.getAddressFactory().getAddress(annotationParts[1]);
|
||||||
|
if (address != null) {
|
||||||
|
return null; // nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
String originalValue = annotationParts[1];
|
||||||
|
List<Symbol> symbols = getSymbols(originalValue, program);
|
||||||
|
if (symbols.size() != 1) {
|
||||||
|
// no unique symbol, so leave it as string name
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Address symbolAddress = symbols.get(0).getAddress();
|
||||||
|
return rawText.replaceFirst(Pattern.quote(originalValue), symbolAddress.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all symbols that match the given text or an empty list.
|
||||||
|
* @param rawText the raw symbol text
|
||||||
|
* @param program the program
|
||||||
|
* @return the symbols
|
||||||
|
*/
|
||||||
|
public static List<Symbol> getSymbols(String rawText, Program program) {
|
||||||
|
List<Symbol> list = NamespaceUtils.getSymbols(rawText, program);
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we get here, then see if the value is an address
|
||||||
|
Address address = program.getAddressFactory().getAddress(rawText);
|
||||||
|
if (address != null) {
|
||||||
|
SymbolTable symbolTable = program.getSymbolTable();
|
||||||
|
Symbol symbol = symbolTable.getPrimarySymbol(address);
|
||||||
|
if (symbol != null) {
|
||||||
|
return Arrays.asList(symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the annotation handler for the given annotation parts. If no handler can be found,
|
||||||
|
* then the {@link InvalidAnnotatedStringHandler} will be returned with n error message.
|
||||||
|
* @param annotationParts the annotation parts
|
||||||
|
* @return the handler
|
||||||
|
*/
|
||||||
|
public static AnnotatedStringHandler getAnnotationHandler(String[] annotationParts) {
|
||||||
|
|
||||||
|
if (annotationParts.length <= 1) {
|
||||||
|
return new InvalidAnnotatedStringHandler(
|
||||||
|
"Invalid annotation format." + " Expected at least two strings.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// the first part is the annotation (@xxx)
|
||||||
|
String keyword = annotationParts[0];
|
||||||
|
AnnotatedStringHandler handler = getAnnotatedStringHandlerMap().get(keyword);
|
||||||
|
|
||||||
|
if (handler == null) {
|
||||||
|
return new InvalidAnnotatedStringHandler("Invalid annotation keyword: " + keyword);
|
||||||
|
}
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all known annotation handlers
|
||||||
|
* @return the handlers
|
||||||
|
*/
|
||||||
|
public static List<AnnotatedStringHandler> getAnnotatedStringHandlers() {
|
||||||
|
if (ANNOTATED_STRING_HANDLERS == null) {
|
||||||
|
ANNOTATED_STRING_HANDLERS = getSupportedAnnotationHandlers();
|
||||||
|
}
|
||||||
|
return ANNOTATED_STRING_HANDLERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, AnnotatedStringHandler> getAnnotatedStringHandlerMap() {
|
||||||
|
if (ANNOTATED_STRING_MAP == null) { // lazy init due to our use of ClassSearcher
|
||||||
|
ANNOTATED_STRING_MAP = createAnnotatedStringHandlerMap();
|
||||||
|
}
|
||||||
|
return ANNOTATED_STRING_MAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, AnnotatedStringHandler> createAnnotatedStringHandlerMap() {
|
||||||
|
Map<String, AnnotatedStringHandler> map = new HashMap<>();
|
||||||
|
for (AnnotatedStringHandler instance : getAnnotatedStringHandlers()) {
|
||||||
|
String[] supportedAnnotations = instance.getSupportedAnnotations();
|
||||||
|
for (String supportedAnnotation : supportedAnnotations) {
|
||||||
|
map.put(supportedAnnotation, instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
// locates AnnotatedStringHandler implementations to handle annotations
|
||||||
|
private static List<AnnotatedStringHandler> getSupportedAnnotationHandlers() {
|
||||||
|
List<AnnotatedStringHandler> list = new ArrayList<>();
|
||||||
|
for (AnnotatedStringHandler h : ClassSearcher.getInstances(AnnotatedStringHandler.class)) {
|
||||||
|
if (h.getSupportedAnnotations().length != 0) {
|
||||||
|
list.add(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*package*/ static Set<String> getAnnotationNames() {
|
||||||
|
return Collections.unmodifiableSet(getAnnotatedStringHandlerMap().keySet());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -388,9 +388,11 @@ public class EolCommentFieldFactory extends FieldFactory {
|
||||||
RowColLocation startRowCol = commentElement.getDataLocationForCharacterIndex(0);
|
RowColLocation startRowCol = commentElement.getDataLocationForCharacterIndex(0);
|
||||||
int encodedRow = startRowCol.row();
|
int encodedRow = startRowCol.row();
|
||||||
int encodedCol = startRowCol.col();
|
int encodedCol = startRowCol.col();
|
||||||
Annotation annotation = new Annotation(refAddrComment, currentPrefixString, program);
|
Annotation annotation = new Annotation(refAddrComment, program);
|
||||||
FieldElement addressElement =
|
FieldElement addressElement =
|
||||||
new AnnotatedTextFieldElement(annotation, encodedRow, encodedCol);
|
new AnnotatedTextFieldElement(annotation, currentPrefixString, program, encodedRow,
|
||||||
|
encodedCol);
|
||||||
|
|
||||||
// Space character
|
// Space character
|
||||||
AttributedString spaceStr = new AttributedString(" ", currentPrefixString.getColor(0),
|
AttributedString spaceStr = new AttributedString(" ", currentPrefixString.getColor(0),
|
||||||
currentPrefixString.getFontMetrics(0), false, null);
|
currentPrefixString.getFontMetrics(0), false, null);
|
||||||
|
|
|
@ -15,15 +15,10 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.viewer.field;
|
package ghidra.app.util.viewer.field;
|
||||||
|
|
||||||
import docking.widgets.fieldpanel.field.*;
|
|
||||||
|
|
||||||
public class StringCommentPart extends CommentPart {
|
public class StringCommentPart extends CommentPart {
|
||||||
|
|
||||||
private AttributedString prototype;
|
StringCommentPart(String text) {
|
||||||
|
|
||||||
StringCommentPart(String text, AttributedString prototype) {
|
|
||||||
super(text);
|
super(text);
|
||||||
this.prototype = prototype;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -31,12 +26,6 @@ public class StringCommentPart extends CommentPart {
|
||||||
return getDisplayText();
|
return getDisplayText();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
AbstractTextFieldElement createElement(int row, int column) {
|
|
||||||
AttributedString as = prototype.deriveAttributedString(displayText);
|
|
||||||
return new TextFieldElement(as, row, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getDisplayText();
|
return getDisplayText();
|
||||||
|
|
|
@ -15,20 +15,17 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.app.util.viewer.field;
|
package ghidra.app.util.viewer.field;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import docking.widgets.fieldpanel.field.AttributedString;
|
import docking.widgets.fieldpanel.field.AttributedString;
|
||||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||||
import ghidra.app.nav.Navigatable;
|
import ghidra.app.nav.Navigatable;
|
||||||
import ghidra.app.services.GoToService;
|
import ghidra.app.services.GoToService;
|
||||||
import ghidra.app.util.NamespaceUtils;
|
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.symbol.Symbol;
|
import ghidra.program.model.symbol.Symbol;
|
||||||
import ghidra.program.model.symbol.SymbolTable;
|
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,32 +40,6 @@ public class SymbolAnnotatedStringHandler implements AnnotatedStringHandler {
|
||||||
"@symbol annotation must have a valid " + "symbol name or address";
|
"@symbol annotation must have a valid " + "symbol name or address";
|
||||||
private static final String[] SUPPORTED_ANNOTATIONS = { "symbol", "sym" };
|
private static final String[] SUPPORTED_ANNOTATIONS = { "symbol", "sym" };
|
||||||
|
|
||||||
public static String convertAnnotationSymbolToAddress(String[] annotationParts, String rawText,
|
|
||||||
Program program) {
|
|
||||||
if (annotationParts.length <= 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (program == null) { // this can happen during merge operations
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Address address = program.getAddressFactory().getAddress(annotationParts[1]);
|
|
||||||
if (address != null) {
|
|
||||||
return null; // nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
String originalValue = annotationParts[1];
|
|
||||||
List<Symbol> symbols = getSymbols(originalValue, program);
|
|
||||||
if (symbols.size() != 1) {
|
|
||||||
// no unique symbol, so leave it as string name
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Address symbolAddress = symbols.get(0).getAddress();
|
|
||||||
return rawText.replaceFirst(Pattern.quote(originalValue), symbolAddress.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AttributedString createAnnotatedString(AttributedString prototypeString, String[] text,
|
public AttributedString createAnnotatedString(AttributedString prototypeString, String[] text,
|
||||||
Program program) {
|
Program program) {
|
||||||
|
@ -82,7 +53,7 @@ public class SymbolAnnotatedStringHandler implements AnnotatedStringHandler {
|
||||||
return createUndecoratedString(prototypeString, text);
|
return createUndecoratedString(prototypeString, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Symbol> symbols = getSymbols(text[1], program);
|
List<Symbol> symbols = CommentUtils.getSymbols(text[1], program);
|
||||||
|
|
||||||
// check for a symbol of the given name first
|
// check for a symbol of the given name first
|
||||||
if (symbols.size() >= 1) {
|
if (symbols.size() >= 1) {
|
||||||
|
@ -106,25 +77,6 @@ public class SymbolAnnotatedStringHandler implements AnnotatedStringHandler {
|
||||||
prototypeString.getFontMetrics(0));
|
prototypeString.getFontMetrics(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Symbol> getSymbols(String rawText, Program program) {
|
|
||||||
List<Symbol> list = NamespaceUtils.getSymbols(rawText, program);
|
|
||||||
if (!list.isEmpty()) {
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we get here, then see if the value is an address
|
|
||||||
Address address = program.getAddressFactory().getAddress(rawText);
|
|
||||||
if (address != null) {
|
|
||||||
SymbolTable symbolTable = program.getSymbolTable();
|
|
||||||
Symbol symbol = symbolTable.getPrimarySymbol(address);
|
|
||||||
if (symbol != null) {
|
|
||||||
return Arrays.asList(symbol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getSupportedAnnotations() {
|
public String[] getSupportedAnnotations() {
|
||||||
return SUPPORTED_ANNOTATIONS;
|
return SUPPORTED_ANNOTATIONS;
|
||||||
|
@ -136,7 +88,7 @@ public class SymbolAnnotatedStringHandler implements AnnotatedStringHandler {
|
||||||
|
|
||||||
String symbolText = annotationParts[1];
|
String symbolText = annotationParts[1];
|
||||||
Program program = sourceNavigatable.getProgram();
|
Program program = sourceNavigatable.getProgram();
|
||||||
List<Symbol> symbols = getSymbols(symbolText, program);
|
List<Symbol> symbols = CommentUtils.getSymbols(symbolText, program);
|
||||||
|
|
||||||
GoToService goToService = serviceProvider.getService(GoToService.class);
|
GoToService goToService = serviceProvider.getService(GoToService.class);
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,9 @@ package ghidra.util.bean.field;
|
||||||
import docking.widgets.fieldpanel.field.*;
|
import docking.widgets.fieldpanel.field.*;
|
||||||
import docking.widgets.fieldpanel.support.RowColLocation;
|
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||||
import ghidra.app.nav.Navigatable;
|
import ghidra.app.nav.Navigatable;
|
||||||
import ghidra.app.util.viewer.field.Annotation;
|
import ghidra.app.util.viewer.field.*;
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.util.ProgramLocation;
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,34 +42,51 @@ final public class AnnotatedTextFieldElement extends AbstractTextFieldElement {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor that initializes this text field element with the given annotation and row
|
* Constructor that initializes this text field element with the given annotation and row
|
||||||
* and column information. The text of this element is the text returned from
|
* and column information. The text of this element is the display text created by the
|
||||||
* {@link Annotation#getDisplayString()}.
|
* annotation handler for the given annotation.
|
||||||
*
|
*
|
||||||
* @param annotation The Annotation that this element is describing.
|
* @param annotation The Annotation that this element is describing.
|
||||||
|
* @param prototype the prototype string used to create new strings
|
||||||
|
* @param program the program
|
||||||
* @param row The row that this element is on
|
* @param row The row that this element is on
|
||||||
* @param column The column value of this element (the column index where this element starts)
|
* @param column The column value of this element (the column index where this element starts)
|
||||||
*/
|
*/
|
||||||
public AnnotatedTextFieldElement(Annotation annotation, int row, int column) {
|
public AnnotatedTextFieldElement(Annotation annotation, AttributedString prototype,
|
||||||
this(annotation, annotation.getDisplayString(), row, column);
|
Program program, int row, int column) {
|
||||||
|
this(annotation, getDisplayString(annotation, prototype, program), row, column);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the original annotation text in the data model, which will differ from the display
|
* Returns the original annotation text in the data model, which will differ from the display
|
||||||
* text.
|
* text.
|
||||||
* @return the original annotation text in the data model.
|
* @return the original annotation text in the data model.
|
||||||
* @see #getDisplayString()
|
|
||||||
*/
|
*/
|
||||||
public String getRawText() {
|
public String getRawText() {
|
||||||
return annotation.getAnnotationText();
|
return annotation.getAnnotationText();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the display string of annotation
|
|
||||||
* @return the display string
|
|
||||||
* @see #getRawText()
|
|
||||||
*/
|
|
||||||
public String getDisplayString() {
|
public String getDisplayString() {
|
||||||
return annotation.getDisplayString().getText();
|
return attributedString.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AttributedString getDisplayString(Annotation a, AttributedString prototype,
|
||||||
|
Program program) {
|
||||||
|
|
||||||
|
String[] annotationParts = a.getAnnotationParts();
|
||||||
|
AnnotatedStringHandler handler = CommentUtils.getAnnotationHandler(annotationParts);
|
||||||
|
|
||||||
|
AttributedString displayString;
|
||||||
|
try {
|
||||||
|
displayString = handler.createAnnotatedString(prototype, annotationParts, program);
|
||||||
|
}
|
||||||
|
catch (AnnotationException ae) {
|
||||||
|
// uh-oh
|
||||||
|
handler =
|
||||||
|
new InvalidAnnotatedStringHandler("Annotation Exception: " + ae.getMessage());
|
||||||
|
displayString = handler.createAnnotatedString(prototype, annotationParts, program);
|
||||||
|
}
|
||||||
|
|
||||||
|
return displayString;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,7 +99,11 @@ final public class AnnotatedTextFieldElement extends AbstractTextFieldElement {
|
||||||
*/
|
*/
|
||||||
public boolean handleMouseClicked(Navigatable sourceNavigatable,
|
public boolean handleMouseClicked(Navigatable sourceNavigatable,
|
||||||
ServiceProvider serviceProvider) {
|
ServiceProvider serviceProvider) {
|
||||||
return annotation.handleMouseClick(sourceNavigatable, serviceProvider);
|
|
||||||
|
String[] annotationParts = annotation.getAnnotationParts();
|
||||||
|
AnnotatedStringHandler handler = CommentUtils.getAnnotationHandler(annotationParts);
|
||||||
|
return handler.handleMouseClick(annotationParts, sourceNavigatable,
|
||||||
|
serviceProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -113,4 +135,5 @@ final public class AnnotatedTextFieldElement extends AbstractTextFieldElement {
|
||||||
return new AnnotatedTextFieldElement(annotation,
|
return new AnnotatedTextFieldElement(annotation,
|
||||||
attributedString.replaceAll(targets, replacement), row, column);
|
attributedString.replaceAll(targets, replacement), row, column);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue