mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-20 23:08:31 +08:00
GP-3760 - Annotations - Updated annotation display to not render escape
characters for braces
This commit is contained in:
+1
-1
@@ -56,7 +56,7 @@ public class AddressAnnotatedStringHandler implements AnnotatedStringHandler {
|
||||
|
||||
String addressText = address.toString();
|
||||
if (text.length > 2) { // address and display text
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
for (int i = 2; i < text.length; i++) {
|
||||
buffer.append(text[i]).append(" ");
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ public class Annotation {
|
||||
}
|
||||
|
||||
private String[] parseAnnotationText(String theAnnotationText) {
|
||||
StringBuffer buffer = new StringBuffer(theAnnotationText);
|
||||
StringBuilder buffer = new StringBuilder(theAnnotationText);
|
||||
|
||||
// strip off the brackets
|
||||
buffer.delete(0, 2); // remove '{' and '@'
|
||||
@@ -196,8 +196,12 @@ public class Annotation {
|
||||
return annotationText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return annotationText;
|
||||
}
|
||||
|
||||
/*package*/ static Set<String> getAnnotationNames() {
|
||||
return Collections.unmodifiableSet(getAnnotatedStringHandlerMap().keySet());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/* ###
|
||||
* 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.app.util.viewer.field;
|
||||
|
||||
import docking.widgets.fieldpanel.field.AbstractTextFieldElement;
|
||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||
|
||||
public class AnnotationCommentPart extends CommentPart {
|
||||
|
||||
private Annotation annotation;
|
||||
|
||||
AnnotationCommentPart(String displayText, Annotation annotation) {
|
||||
super(displayText);
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getRawText() {
|
||||
return annotation.getAnnotationText();
|
||||
}
|
||||
|
||||
@Override
|
||||
AbstractTextFieldElement createElement(int row, int column) {
|
||||
return new AnnotatedTextFieldElement(annotation, row, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return annotation.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* ###
|
||||
* 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.app.util.viewer.field;
|
||||
|
||||
import docking.widgets.fieldpanel.field.AbstractTextFieldElement;
|
||||
|
||||
public abstract class CommentPart {
|
||||
|
||||
protected String displayText;
|
||||
|
||||
CommentPart(String displayText) {
|
||||
this.displayText = displayText;
|
||||
}
|
||||
|
||||
abstract AbstractTextFieldElement createElement(int row, int column);
|
||||
|
||||
abstract String getRawText();
|
||||
|
||||
String getDisplayText() {
|
||||
return displayText;
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,6 @@ import generic.theme.Gui;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.StringUtilities;
|
||||
import ghidra.util.WordLocation;
|
||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
public class CommentUtils {
|
||||
|
||||
@@ -75,21 +73,10 @@ public class CommentUtils {
|
||||
};
|
||||
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
List<Object> parts =
|
||||
List<CommentPart> parts =
|
||||
doParseTextIntoTextAndAnnotations(rawCommentText, symbolFixer, program, prototype);
|
||||
for (Object part : parts) {
|
||||
|
||||
if (part instanceof String) {
|
||||
String s = (String) part;
|
||||
buffy.append(s);
|
||||
}
|
||||
else if (part instanceof Annotation) {
|
||||
Annotation a = (Annotation) part;
|
||||
buffy.append(a.getAnnotationText());
|
||||
}
|
||||
else {
|
||||
throw new AssertException("Unhandled annotation piece: " + part);
|
||||
}
|
||||
for (CommentPart part : parts) {
|
||||
buffy.append(part.getRawText());
|
||||
}
|
||||
return buffy.toString();
|
||||
}
|
||||
@@ -136,7 +123,7 @@ public class CommentUtils {
|
||||
Function<Annotation, Annotation> noFixing = Function.identity();
|
||||
return doParseTextForAnnotations(text, noFixing, program, prototypeString, row);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sanitizes the given text, removing or replacing illegal characters.
|
||||
* <p>
|
||||
@@ -175,25 +162,12 @@ public class CommentUtils {
|
||||
text = StringUtilities.convertTabsToSpaces(text);
|
||||
|
||||
int column = 0;
|
||||
List<Object> parts =
|
||||
List<CommentPart> parts =
|
||||
doParseTextIntoTextAndAnnotations(text, fixerUpper, program, prototype);
|
||||
List<FieldElement> fields = new ArrayList<>();
|
||||
for (Object part : parts) {
|
||||
|
||||
if (part instanceof String) {
|
||||
String s = (String) part;
|
||||
AttributedString as = prototype.deriveAttributedString(s);
|
||||
fields.add(new TextFieldElement(as, row, column));
|
||||
column += s.length();
|
||||
}
|
||||
else if (part instanceof Annotation) {
|
||||
Annotation a = (Annotation) part;
|
||||
fields.add(new AnnotatedTextFieldElement(a, row, column));
|
||||
column += a.getAnnotationText().length();
|
||||
}
|
||||
else {
|
||||
throw new AssertException("Unhandled annotation piece: " + part);
|
||||
}
|
||||
for (CommentPart part : parts) {
|
||||
fields.add(part.createElement(row, column));
|
||||
column += part.getDisplayText().length();
|
||||
}
|
||||
|
||||
return new CompositeFieldElement(fields.toArray(new FieldElement[fields.size()]));
|
||||
@@ -204,21 +178,21 @@ public class CommentUtils {
|
||||
* an Annotation
|
||||
*
|
||||
* @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 prototype the prototype string that contains decoration attributes
|
||||
* @return a list that contains a mixture String or an Annotation entries
|
||||
*/
|
||||
private static List<Object> doParseTextIntoTextAndAnnotations(String text,
|
||||
private static List<CommentPart> doParseTextIntoTextAndAnnotations(String text,
|
||||
Function<Annotation, Annotation> fixerUpper, Program program,
|
||||
AttributedString prototype) {
|
||||
|
||||
List<Object> results = new ArrayList<>();
|
||||
List<CommentPart> results = new ArrayList<>();
|
||||
|
||||
List<WordLocation> annotations = getCommentAnnotations(text);
|
||||
if (annotations.isEmpty()) {
|
||||
results.add(text);
|
||||
String updatedText = removeEscapeChars(text);
|
||||
results.add(new StringCommentPart(text, updatedText, prototype));
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -230,19 +204,23 @@ public class CommentUtils {
|
||||
if (offset != start) {
|
||||
// text between annotations
|
||||
String preceeding = text.substring(offset, start);
|
||||
results.add(preceeding);
|
||||
String updatedText = removeEscapeChars(preceeding);
|
||||
results.add(new StringCommentPart(preceeding, updatedText, prototype));
|
||||
}
|
||||
|
||||
String annotationText = word.getWord();
|
||||
Annotation annotation = new Annotation(annotationText, prototype, program);
|
||||
String updatedText = removeEscapeChars(annotationText);
|
||||
Annotation annotation = new Annotation(updatedText, prototype, program);
|
||||
annotation = fixerUpper.apply(annotation);
|
||||
results.add(annotation);
|
||||
results.add(new AnnotationCommentPart(updatedText, annotation));
|
||||
|
||||
offset = start + annotationText.length();
|
||||
}
|
||||
|
||||
if (offset != text.length()) { // trailing text
|
||||
results.add(text.substring(offset));
|
||||
String trailing = text.substring(offset);
|
||||
String updatedText = removeEscapeChars(trailing);
|
||||
results.add(new StringCommentPart(trailing, updatedText, prototype));
|
||||
}
|
||||
|
||||
return results;
|
||||
@@ -291,6 +269,23 @@ public class CommentUtils {
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
// remove any backslashes that escape special annotation characters, like '{' and '}'
|
||||
private static String removeEscapeChars(String text) {
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c == '\\') {
|
||||
char next = i == text.length() ? '\0' : text.charAt(i + 1);
|
||||
if (next == '{' || next == '}') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
buffy.append(c);
|
||||
}
|
||||
|
||||
return buffy.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* Starts at the given index and looks for the end an annotation, ignoring quoted text
|
||||
* and escaped characters along the way. The value returned is the index after the last
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/* ###
|
||||
* 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.app.util.viewer.field;
|
||||
|
||||
import docking.widgets.fieldpanel.field.*;
|
||||
|
||||
public class StringCommentPart extends CommentPart {
|
||||
|
||||
private AttributedString prototype;
|
||||
private String rawText;
|
||||
|
||||
StringCommentPart(String rawText, AttributedString prototype) {
|
||||
this(rawText, rawText, prototype);
|
||||
}
|
||||
|
||||
StringCommentPart(String rawText, String displayText, AttributedString prototype) {
|
||||
super(displayText);
|
||||
this.rawText = rawText;
|
||||
this.prototype = prototype;
|
||||
}
|
||||
|
||||
@Override
|
||||
String getRawText() {
|
||||
return rawText;
|
||||
}
|
||||
|
||||
@Override
|
||||
AbstractTextFieldElement createElement(int row, int column) {
|
||||
AttributedString as = prototype.deriveAttributedString(displayText);
|
||||
return new TextFieldElement(as, row, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return rawText;
|
||||
}
|
||||
}
|
||||
+9
-2
@@ -201,14 +201,21 @@ public class AnnotationTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
public void testSymbolAnnotation_WithBracesInName_Escaped() {
|
||||
String rawComment = "This is a symbol {@sym mySym\\{0\\}} annotation";
|
||||
String display = CommentUtils.getDisplayString(rawComment, program);
|
||||
assertEquals("This is a symbol mySym\\{0\\} annotation", display);
|
||||
assertEquals("This is a symbol mySym{0} annotation", display);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSymbolAnnotation_WithEscapedItemsOutsideOfAnnotation() {
|
||||
String rawComment = "This is a foo\\} symbol {@sym mySym\\{0\\}} annotation \\{bar";
|
||||
String display = CommentUtils.getDisplayString(rawComment, program);
|
||||
assertEquals("This is a foo} symbol mySym{0} annotation {bar", display);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSymbolAnnotation_FullyEscaped() {
|
||||
String rawComment = "This is a symbol \\{@sym bob\\} annotation";
|
||||
String display = CommentUtils.getDisplayString(rawComment, program);
|
||||
assertEquals(rawComment, display);
|
||||
assertEquals("This is a symbol {@sym bob} annotation", display);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-2
@@ -106,11 +106,12 @@ public class CommentUtilsTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
assertEquals(1, annotations.size());
|
||||
WordLocation word = annotations.get(0);
|
||||
assertEquals("{@symbol symbol\\{Name\\}}", word.getWord());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSanitize() {
|
||||
|
||||
|
||||
String comment = null;
|
||||
String sanitized = CommentUtils.sanitize(comment);
|
||||
assertNull(sanitized);
|
||||
|
||||
Reference in New Issue
Block a user