diff --git a/Ghidra/Features/FunctionGraph/src/test.slow/java/ghidra/app/plugin/core/functiongraph/AbstractFunctionGraphTest.java b/Ghidra/Features/FunctionGraph/src/test.slow/java/ghidra/app/plugin/core/functiongraph/AbstractFunctionGraphTest.java index ca991f3735..2303c67bde 100644 --- a/Ghidra/Features/FunctionGraph/src/test.slow/java/ghidra/app/plugin/core/functiongraph/AbstractFunctionGraphTest.java +++ b/Ghidra/Features/FunctionGraph/src/test.slow/java/ghidra/app/plugin/core/functiongraph/AbstractFunctionGraphTest.java @@ -401,7 +401,6 @@ public abstract class AbstractFunctionGraphTest extends AbstractGhidraHeadedInte @After public void tearDown() throws Exception { waitForSwing(); - env.closeTool(tool); env.dispose(); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java b/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java index 1894e9df69..4b64f2f064 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/GlobalMenuAndToolBarManager.java @@ -216,7 +216,10 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener { // has not yet completed DialogComponentProvider provider = dialog.getDialogComponent(); if (provider != null) { - return provider.getActionContext(null); + ActionContext context = provider.getActionContext(null); + if (context != null) { + return context; + } } } @@ -225,7 +228,7 @@ public class GlobalMenuAndToolBarManager implements DockingWindowListener { private ActionContext getComponentProviderContext(WindowNode windowNode) { if (windowNode == null) { - return null; + return new DefaultActionContext(); } ActionContext context = null; diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/Highlight.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/Highlight.java index 39d72bb694..f45d17c824 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/Highlight.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/support/Highlight.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -17,6 +17,8 @@ package docking.widgets.fieldpanel.support; import java.awt.Color; +import generic.json.Json; + public class Highlight { private int start; private int end; @@ -36,21 +38,28 @@ public class Highlight { } /** - * Returns the starting position of the highlight. + * {@return the starting position of the highlight} */ public int getStart() { return start + offset; } /** - * Returns the ending position (inclusive) of the highlight. + * {@return the ending position (inclusive) of the highlight} */ public int getEnd() { return end + offset; } /** - * Returns the color to use as the background highlight color. + * {@return the number of characters in the match.} + */ + public int length() { + return (end - start) + 1; // +1 because 'end' is inclusive + } + + /** + * {@return the color to use as the background highlight color.} */ public Color getColor() { return color; @@ -59,11 +68,20 @@ public class Highlight { /** * Sets the offset of this highlights start and end values. The effect of the offset is that * calls to {@link #getStart()} and {@link #getEnd()} will return their values with the - * offset added. + * offset added. + *

+ * This useful when highlights are using offsets for widgets that embedded inside of composite + * containers. the parent container turn these relative values into absolute values that work + * when all sub-parts are combined. * * @param newOffset The new offset into this highlight. */ public void setOffset(int newOffset) { offset = newOffset; } + + @Override + public String toString() { + return Json.toString(this); + } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/search/TextComponentSearchResults.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/search/TextComponentSearchResults.java index d9d55af1bd..386556dc43 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/search/TextComponentSearchResults.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/search/TextComponentSearchResults.java @@ -16,6 +16,7 @@ package docking.widgets.search; import java.awt.*; +import java.awt.geom.Rectangle2D; import java.net.URL; import java.util.*; import java.util.List; @@ -307,7 +308,11 @@ public class TextComponentSearchResults extends SearchResults { try { int start = location.getStartIndexInclusive(); int end = location.getEndIndexInclusive(); - Rectangle startR = editorPane.modelToView2D(start).getBounds(); + Rectangle2D start2d = editorPane.modelToView2D(start); + if (start2d == null) { + return; // not yet realized + } + Rectangle startR = start2d.getBounds(); Rectangle endR = editorPane.modelToView2D(end).getBounds(); endR.width += 20; // a little extra space so the view is not right at the text end Rectangle union = startR.union(endR); @@ -449,7 +454,7 @@ public class TextComponentSearchResults extends SearchResults { } Color c = location.isActive() ? activeHighlightColor : highlightColor; - HighlightPainter painter = new DefaultHighlightPainter(c); + HighlightPainter painter = new SearchResultsHighlightPainter(c); int start = location.getStartIndexInclusive(); int end = location.getEndIndexInclusive() + 1; // +1 to make inclusive be exclusive try { @@ -463,6 +468,7 @@ public class TextComponentSearchResults extends SearchResults { @Override public void dispose() { + caretUpdater.dispose(); if (editorPane != null) { @@ -557,7 +563,11 @@ public class TextComponentSearchResults extends SearchResults { } if (nonSearchDelegate) { + // Calling setHighlighter() will cause all old highlights to get removed. We would + // like the non-search highlights to remain. Grab them now and put them back after. + Highlight[] nonSearchHighlights = delegate.getHighlights(); editorPane.setHighlighter(delegate); + restoreNonSearchHighlights(nonSearchHighlights); } else { editorPane.setHighlighter(null); @@ -566,7 +576,30 @@ public class TextComponentSearchResults extends SearchResults { @Override public void install(JTextComponent c) { + Highlight[] nonSearchHighlights = delegate.getHighlights(); delegate.install(c); + + // Calling delegate.install() will cause its existing highlights to get removed. We + // would like to keep them, so we have save and restore them. + restoreNonSearchHighlights(nonSearchHighlights); + } + + private void restoreNonSearchHighlights(Highlight[] oldHighlights) { + for (Highlight hl : oldHighlights) { + int start = hl.getStartOffset(); + int end = hl.getEndOffset(); + HighlightPainter painter = hl.getPainter(); + if (painter instanceof SearchResultsHighlightPainter) { + continue; + } + try { + delegate.addHighlight(start, end, painter); + } + catch (BadLocationException e) { + // shouldn't happen + Msg.error(this, "Could not add existing highlight", e); + } + } } @Override @@ -599,7 +632,11 @@ public class TextComponentSearchResults extends SearchResults { @Override public void removeAllHighlights() { - delegate.removeAllHighlights(); + // only remove our highlights, not any pre-existing client non-search highlights + for (TextComponentSearchLocation loc : searchLocations) { + Object tag = loc.getHighlightTag(); + removeHighlight(tag); + } } @Override @@ -611,6 +648,12 @@ public class TextComponentSearchResults extends SearchResults { public Highlight[] getHighlights() { return delegate.getHighlights(); } + } + // marker class + private class SearchResultsHighlightPainter extends DefaultHighlightPainter { + public SearchResultsHighlightPainter(Color c) { + super(c); + } } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/search/TextComponentSearcher.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/search/TextComponentSearcher.java index 6965ea1249..5573de4538 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/search/TextComponentSearcher.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/search/TextComponentSearcher.java @@ -23,6 +23,8 @@ import javax.swing.JEditorPane; import javax.swing.text.BadLocationException; import javax.swing.text.Document; +import org.apache.commons.lang3.StringUtils; + import docking.widgets.CursorPosition; import docking.widgets.SearchLocation; import ghidra.util.Msg; @@ -235,6 +237,11 @@ public class TextComponentSearcher implements FindDialogSearcher { return; } + if (StringUtils.isBlank(fullText)) { + Msg.error(this, "Cannot search a blank document"); + return; + } + TreeMap lineRangeMap = mapLines(fullText); Pattern pattern = createSearchPattern(searchText, useRegex); @@ -252,7 +259,6 @@ public class TextComponentSearcher implements FindDialogSearcher { context); matchesByPosition.put(start, location); } - } private TreeMap mapLines(String fullText) { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GDynamicColumnTableModel.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GDynamicColumnTableModel.java index 22b90f8c2f..c852424007 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GDynamicColumnTableModel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/GDynamicColumnTableModel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -62,10 +62,14 @@ public abstract class GDynamicColumnTableModel protected ServiceProvider serviceProvider; private TableColumnDescriptor columnDescriptor; - protected List> tableColumns = new ArrayList<>(); - private List> defaultTableColumns = new ArrayList<>(); protected Map, Settings> columnSettings = new HashMap<>(); + /** All currently visible columns */ + protected List> tableColumns = new ArrayList<>(); + + /** The initially visible columns before user changes or state restoring */ + private List> defaultTableColumns = new ArrayList<>(); + private boolean ignoreSettingChanges = false; public GDynamicColumnTableModel(ServiceProvider serviceProvider) { @@ -298,7 +302,7 @@ public abstract class GDynamicColumnTableModel protected void addTableColumns(Set> columns, boolean isDefault) { for (DynamicTableColumn column : columns) { - doAddTableColumn(column, getDefaultTableColumns().size(), isDefault); + doAddTableColumn(column, -1, isDefault); } fireTableStructureChanged(); } @@ -327,16 +331,30 @@ public abstract class GDynamicColumnTableModel private void doAddTableColumn(DynamicTableColumn column, int index, boolean isDefault) { - if (index < 0 || index > tableColumns.size()) { - index = getDefaultTableColumns().size(); + int adjustedIndex = index; + if (adjustedIndex < 0 || adjustedIndex > tableColumns.size()) { + adjustedIndex = tableColumns.size(); } - tableColumns.add(index, column); + tableColumns.add(adjustedIndex, column); columnSettings.put(column, new SettingsImpl(this, column)); - if (isDefault) { - List> defaultColumns = getDefaultTableColumns(); - defaultColumns.add(index, column); + + if (!isDefault) { + return; } + + // Note: this method is typically called when 'tableColumns' and 'defaultTableColumns' have + // the same columns. But, that is not a requirement. When they have the same columns, the + // insertion index is correct for both lists. If they have different columns, then the + // insertion index for the default columns may or may not be what the caller intended. In + // practice, it should not matter where the column is inserted into the default columns, as + // that is only used to query whether a column is in the list or not. If we ever need to + // have accurate positioning in the default list when both lists are not equivalent, then we + // will have to add a new method or change this method to allow callers to dictate where the + // column should go in the default list. For now, just add the column to the end. + adjustedIndex = defaultTableColumns.size(); + List> defaultColumns = getDefaultTableColumns(); + defaultColumns.add(adjustedIndex, column); } /** @@ -470,7 +488,7 @@ public abstract class GDynamicColumnTableModel DATA_SOURCE dataSource = getDataSource(); @SuppressWarnings("unchecked") - // TODO: We are casting now, as in practice the type should never be different that + // Note: We are casting now, as in practice the type should never be different that // the declared type. We want to remove entirely the 'dataSource' value and then // the templating will be simpler. DynamicTableColumn column = diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/TableColumnModelState.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/TableColumnModelState.java index a367cd80e1..5bed5cde1e 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/TableColumnModelState.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/TableColumnModelState.java @@ -470,29 +470,30 @@ public class TableColumnModelState implements SortListener { if (preferenceKey != null) { return preferenceKey; } - TableModel tableModel = table.getModel(); - int columnCount = getDefaultColumnCount(); - StringBuffer buffer = new StringBuffer(); + TableModel model = table.getModel(); + int n = model.getColumnCount(); + StringBuilder buffer = new StringBuilder(); buffer.append(getTableModelName()); buffer.append(":"); - for (int i = 0; i < columnCount; i++) { - String columnName = tableModel.getColumnName(i); - buffer.append(columnName).append(":"); + for (int i = 0; i < n; i++) { + if (isDefaultColumn(i)) { + String columnName = model.getColumnName(i); + buffer.append(columnName).append(":"); + } } return buffer.toString(); } - private int getDefaultColumnCount() { - + private boolean isDefaultColumn(int col) { TableModel tableModel = table.getUnwrappedTableModel(); if (tableModel instanceof VariableColumnTableModel) { VariableColumnTableModel variableTableModel = (VariableColumnTableModel) tableModel; // VariableColumnTableModels have default columns and 'found' columns. We only want to // create a key based upon the default columns - return variableTableModel.getDefaultColumnCount(); + return variableTableModel.isDefaultColumn(col); } - return tableModel.getColumnCount(); + return true; } private void setDefaultColumnsVisible() { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/VariableColumnTableModel.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/VariableColumnTableModel.java index b469d25198..e2ef0f6d09 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/VariableColumnTableModel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/VariableColumnTableModel.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,6 +27,7 @@ public interface VariableColumnTableModel extends TableModel { * type or is wraps another table model that is an instance of this type. If the given * model is not such an instance, then null is returned. * + * @param m the model * @return the variable column model */ public static VariableColumnTableModel from(TableModel m) { @@ -45,6 +46,7 @@ public interface VariableColumnTableModel extends TableModel { * Returns a value that is unique for a given table column. This is different than getting * the display name, which may be shared by different columns. * @param column the index (in the model space) of the column for which to get the identifier + * @return the identifier */ public String getUniqueIdentifier(int column); @@ -56,7 +58,9 @@ public interface VariableColumnTableModel extends TableModel { * calling methods like {@link #getColumnName(int)}. * * @return Gets the count of the default columns for this model. + * @deprecated no longer needed */ + @Deprecated(forRemoval = true, since = "12.1") public int getDefaultColumnCount(); /** diff --git a/Ghidra/Framework/Generic/src/main/java/generic/algorithms/StringReducingLcs.java b/Ghidra/Framework/Generic/src/main/java/generic/algorithms/StringReducingLcs.java new file mode 100644 index 0000000000..f5b8a6bd63 --- /dev/null +++ b/Ghidra/Framework/Generic/src/main/java/generic/algorithms/StringReducingLcs.java @@ -0,0 +1,41 @@ +/* ### + * 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 generic.algorithms; + +/** + * A reducing LCS that works on Strings. + */ +public class StringReducingLcs extends ReducingLcs { + + public StringReducingLcs(String x, String y) { + super(x, y); + } + + @Override + protected String reduce(String input, int start, int end) { + return input.substring(start, end); + } + + @Override + protected int lengthOf(String s) { + return s.length(); + } + + @Override + protected Character valueOf(String s, int offset) { + return s.charAt(offset); + } +} diff --git a/Ghidra/Framework/Generic/src/main/java/generic/algorithms/WordDiffer.java b/Ghidra/Framework/Generic/src/main/java/generic/algorithms/WordDiffer.java new file mode 100644 index 0000000000..7712769b94 --- /dev/null +++ b/Ghidra/Framework/Generic/src/main/java/generic/algorithms/WordDiffer.java @@ -0,0 +1,270 @@ +/* ### + * 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 generic.algorithms; + +import java.util.*; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +/** + * Finds differences between two words (any two Strings). The results are available via + * {@link #getParts()}. + */ +public class WordDiffer { + + private List parts = List.of(); + + /** + * Diffs the text between the old and new word. The new word is the current version of two + * Strings, the old word is the previous version. + * + * @param oldWord the previous version of the text + * @param newWord the current version of the text + */ + public WordDiffer(String oldWord, String newWord) { + + LcsMatch lcs = getLcs(newWord, oldWord); + if (lcs == null) { + return; + } + + TreeMap wordsByOffset = buildWordOffsets(lcs); + + parts = createWordParts(newWord, wordsByOffset); + } + + /** + * Returns the 'new word' broken into Strings with offsets, with each part being the same text + * or different text. + * @return the parts; empty if the LCS could not be created + */ + public List getParts() { + return parts; + } + + /** + * The same as {@link #getParts()} except that this method will merge differences that are + * separated only by {@code maxSize} or less characters. This method allows clients to combine + * many smaller differences into larger differences that span similar characters. Merging parts + * can reduce visual clutter when displaying the differences, at the expense of accuracy. + * + * @param maxSize the maximum span of characters past which not to merge two differences + * @return the parts + */ + public List getMergedParts(int maxSize) { + + List newParts = new ArrayList<>(); + DifferentPart lastDiffPart = null; + for (int i = 0; i < parts.size(); i++) { + WordPart part = parts.get(i); + if (part instanceof DifferentPart diffPart) { + lastDiffPart = diffPart; + newParts.add(lastDiffPart); + continue; + } + + int length = part.length(); + if (length > maxSize) { + continue; + } + + if (lastDiffPart != null) { + WordPart nextPart = i + 1 < parts.size() ? parts.get(i + 1) : null; + WordPart mergedPart = lastDiffPart.merge(part, nextPart); + if (mergedPart != null) { + i++; + newParts.remove(lastDiffPart); + newParts.add(mergedPart); + lastDiffPart = null; + } + } + // add this non-diff part to the last merged diff part, if any exists + else { + if (newParts.isEmpty()) { + continue; + } + + DifferentPart previousDiffPart = (DifferentPart) newParts.getLast(); + WordPart nextPart = i + 1 < parts.size() ? parts.get(i + 1) : null; + WordPart mergedPart = previousDiffPart.merge(part, nextPart); + if (mergedPart != null) { + i++; + newParts.remove(previousDiffPart); + newParts.add(mergedPart); + } + } + } + return newParts; + } + + @Override + public String toString() { + return parts.stream().map(p -> p.toString()).collect(Collectors.joining()); + } + + /** + * Turns the LCS match into one or more words that do not match. This uses the common + * characters to build a mapping of the different words and their offsets into the new word + * originally passed to the WordDiffer. + */ + private TreeMap buildWordOffsets(LcsMatch match) { + // break each word into parts, splitting on each character + + TreeMap wordsByOffset = new TreeMap<>(); + + StringBuilder buffy = new StringBuilder(); + String word = match.newWord; + int wordIndex = 0; // index into the overall 'new word' + for (char c : match.lcs) { + + for (; wordIndex < word.length(); wordIndex++) { + + char wordChar = word.charAt(wordIndex); + if (wordChar == c) { + int offset = (wordIndex) - buffy.length(); + saveWord(buffy, offset, wordsByOffset); + wordIndex++; + break; + } + + buffy.append(wordChar); + } + } + + int offset = wordIndex - buffy.length(); + saveWord(buffy, offset, wordsByOffset); + + if (wordIndex < word.length()) { + // the LCS ended; get the rest of the original word + buffy.append(word.substring(wordIndex)); + saveWord(buffy, wordIndex, wordsByOffset); + } + + return wordsByOffset; + } + + private void saveWord(StringBuilder buffy, int charPosition, + TreeMap wordIndices) { + if (buffy.length() > 0) { + wordIndices.put(charPosition, buffy.toString()); + buffy.setLength(0); + } + } + + private LcsMatch getLcs(String x, String y) { + StringReducingLcs lcs = new StringReducingLcs(x, y); + List lcsList = lcs.getLcs(); + if (lcsList.isEmpty()) { + return null; + } + if (lcsList.size() < 3) { + return null; // what is the min size? + } + + LcsMatch match = new LcsMatch(x, y, lcsList); + return match; + } + + private List createWordParts(String newWord, TreeMap wordIndices) { + + List results = new ArrayList<>(); + + int lastWrittenIndex = 0; + Set> entrySet = wordIndices.entrySet(); + for (Entry entry : entrySet) { + Integer index = entry.getKey(); + + if (lastWrittenIndex < index) { + String text = newWord.substring(lastWrittenIndex, index); + results.add(new SamePart(text, lastWrittenIndex)); + } + + String word = entry.getValue(); + results.add(new DifferentPart(word, index)); + lastWrittenIndex = index + word.length(); + + } + + if (lastWrittenIndex < newWord.length()) { + String text = newWord.substring(lastWrittenIndex); + results.add(new SamePart(text, lastWrittenIndex)); + } + return results; + } + +//================================================================================================= +// Inner Classes +//================================================================================================= + + /** + * A String that is part of a larger String. This class also has the offset into the original + * String. + */ + public abstract class WordPart { + protected String text; + protected int index; + + WordPart(String text, int index) { + this.text = text; + this.index = index; + } + + public int getIndex() { + return index; + } + + public int length() { + return text.length(); + } + + public String getText() { + return text; + } + + @Override + public String toString() { + return text; + } + } + + public class SamePart extends WordPart { + SamePart(String text, int index) { + super(text, index); + } + } + + public class DifferentPart extends WordPart { + DifferentPart(String text, int index) { + super(text, index); + } + + public WordPart merge(WordPart oldPart, WordPart nextPart) { + if (!(nextPart instanceof DifferentPart)) { + return null; + } + + String updatedText = text + oldPart.text + nextPart.text; + return new DifferentPart(updatedText, index); + } + + @Override + public String toString() { + return " /" + text + "/ "; + } + } + + private record LcsMatch(String newWord, String oldWord, List lcs) {} +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/StringDiff.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/StringDiff.java index ec9bb7062a..ab64fc3d9c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/StringDiff.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/StringDiff.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -35,12 +35,12 @@ public class StringDiff { /** * String being inserted. This can be an insert or a complete replace (the positions will both - * be -1 in a replace; pos1 will be non-negative during an insert). + * be -1 in a replace; start will be non-negative during an insert). */ public String text; /** - * Construct a new StringDiff with pos1 and pos2 are initialized to -1 + * Construct a new StringDiff with start and end are initialized to -1 * * @param newText string * @return the new diff @@ -50,7 +50,7 @@ public class StringDiff { } /** - * Construct a new StringDiff that indicates text was deleted from pos1 to pos2 + * Construct a new StringDiff that indicates text was deleted from start and end * * @param start position 1 for the diff * @param end position 2 for the diff @@ -61,7 +61,7 @@ public class StringDiff { } /** - * Construct a new StringDiff that indicates that insertData was inserted at the given position + * Construct a new StringDiff that indicates that newText was inserted at the given position * * @param newText inserted string * @param start position where the text was inserted diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/StringDiffUtils.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/StringDiffUtils.java index 3edca3ac16..342594db98 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/StringDiffUtils.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/StringDiffUtils.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -219,9 +219,11 @@ class StringDiffUtils { } /** - * Applies the array of StringObjects to the string s to produce a new string. Warning - the - * diff objects cannot be applied to an arbitrary string, the Strings must be the original - * String used to compute the diffs. + * Applies the array of StringDiffs to the string s to produce a new string. + * + *

Warning: the diff objects cannot be applied to an arbitrary string, the Strings must be + * the original String used to compute the diffs. + * * @param s the original string * @param diffs the array of StringDiff object to apply * @return a new String resulting from applying the diffs to s. diff --git a/Ghidra/Framework/Utility/src/main/java/ghidra/framework/ApplicationVersion.java b/Ghidra/Framework/Utility/src/main/java/ghidra/framework/ApplicationVersion.java index bf53b68224..39ae739e38 100644 --- a/Ghidra/Framework/Utility/src/main/java/ghidra/framework/ApplicationVersion.java +++ b/Ghidra/Framework/Utility/src/main/java/ghidra/framework/ApplicationVersion.java @@ -221,7 +221,8 @@ public class ApplicationVersion implements Comparable { } catch (NumberFormatException e) { throw new IllegalArgumentException( - "Failed to convert " + versionPartName + " version to integer"); + "Failed to convert version to integer: '" + versionPartName + "' value '" + + versionPart + "'"); } } }