diff --git a/Ghidra/Features/Base/src/main/help/help/topics/Trees/GhidraTreeFilter.html b/Ghidra/Features/Base/src/main/help/help/topics/Trees/GhidraTreeFilter.html index b7a8651a04..8a69d02136 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/Trees/GhidraTreeFilter.html +++ b/Ghidra/Features/Base/src/main/help/help/topics/Trees/GhidraTreeFilter.html @@ -293,7 +293,7 @@
-

The button in lower right corner will bring up +

The button in lower right corner will bring up the Column Filter Dialog.



diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringColumnConstraint.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringColumnConstraint.java index 6e39d79169..9d5dc8cde6 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringColumnConstraint.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringColumnConstraint.java @@ -77,7 +77,7 @@ public abstract class StringColumnConstraint implements ColumnConstraint protected Pattern generateFindsPattern() { String regexString = UserSearchUtils.createPatternString(patternString, true); return Pattern.compile("(" + regexString + ")", - Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); + Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.DOTALL); } @Override diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringContainsColumnConstraint.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringContainsColumnConstraint.java index 801cdf8fe9..4e058d591f 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringContainsColumnConstraint.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringContainsColumnConstraint.java @@ -46,7 +46,6 @@ public class StringContainsColumnConstraint extends StringColumnConstraint { @Override protected Pattern generateMatchesPattern(String patternString) { return UserSearchUtils.createContainsPattern(patternString.trim(), true, - Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); + Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.DOTALL); } - } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringEndsWithColumnConstraint.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringEndsWithColumnConstraint.java index a2216f96fe..b36c02f61f 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringEndsWithColumnConstraint.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringEndsWithColumnConstraint.java @@ -45,6 +45,6 @@ public class StringEndsWithColumnConstraint extends StringColumnConstraint { @Override protected Pattern generateMatchesPattern(String patternString) { return UserSearchUtils.createEndsWithPattern(patternString, true, - Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); + Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.DOTALL); } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringMatchesColumnConstraint.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringMatchesColumnConstraint.java index aa4d04ea85..f91aa75172 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringMatchesColumnConstraint.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringMatchesColumnConstraint.java @@ -18,6 +18,8 @@ package docking.widgets.table.constraint; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; +import ghidra.util.UserSearchUtils; + /** * String column constraint for matching column values if they match a full regular expression pattern. */ @@ -26,7 +28,7 @@ public class StringMatchesColumnConstraint extends StringColumnConstraint { * Constructor * *

This class is for users to enter true regular expression which is why it creates - * a pattern directly without using the UserSearchUtils + * a pattern directly without using the {@link UserSearchUtils}. * * @param spec the string to use to create a "matcher" pattern. */ diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringNotMatchesColumnConstraint.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringNotMatchesColumnConstraint.java index eafc74cedb..4cae6043e4 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringNotMatchesColumnConstraint.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringNotMatchesColumnConstraint.java @@ -15,6 +15,8 @@ */ package docking.widgets.table.constraint; +import ghidra.util.UserSearchUtils; + /** * String column constraint for matching column values if they do not match a full regular * expression pattern. @@ -25,7 +27,7 @@ public class StringNotMatchesColumnConstraint extends StringMatchesColumnConstra * Constructor * *

This class is for users to enter true regular expression which is why it creates - * a pattern directly without using the UserSearchUtils + * a pattern directly without using the {@link UserSearchUtils} * * @param spec the string to use to create a "matcher" pattern. */ diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringStartsWithColumnConstraint.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringStartsWithColumnConstraint.java index 0feb03a8ad..5d6aa1ff05 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringStartsWithColumnConstraint.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constraint/StringStartsWithColumnConstraint.java @@ -46,6 +46,6 @@ public class StringStartsWithColumnConstraint extends StringColumnConstraint { @Override protected Pattern generateMatchesPattern(String patternString) { return UserSearchUtils.createStartsWithPattern(patternString, true, - Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); + Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.DOTALL); } } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constrainteditor/AutocompletingStringConstraintEditor.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constrainteditor/AutocompletingStringConstraintEditor.java index 4617f4ecbc..16ecd266be 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constrainteditor/AutocompletingStringConstraintEditor.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/table/constrainteditor/AutocompletingStringConstraintEditor.java @@ -227,7 +227,6 @@ public class AutocompletingStringConstraintEditor extends DataLoadingConstraintE matcher.appendReplacement(sb, replacement); } matcher.appendTail(sb); - return sb.toString(); } diff --git a/Ghidra/Framework/Docking/src/test/java/docking/widgets/table/columnfilter/ColumnTableFilterTest.java b/Ghidra/Framework/Docking/src/test/java/docking/widgets/table/columnfilter/ColumnTableFilterTest.java index a43419bc8c..bee2fdec36 100644 --- a/Ghidra/Framework/Docking/src/test/java/docking/widgets/table/columnfilter/ColumnTableFilterTest.java +++ b/Ghidra/Framework/Docking/src/test/java/docking/widgets/table/columnfilter/ColumnTableFilterTest.java @@ -19,8 +19,8 @@ import static org.junit.Assert.*; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.util.*; +import java.util.Date; +import java.util.List; import javax.swing.table.TableColumn; @@ -29,14 +29,9 @@ import org.junit.Test; import docking.test.AbstractDockingTest; import docking.widgets.table.*; -import docking.widgets.table.constraint.ColumnConstraint; -import docking.widgets.table.constraint.MappedColumnConstraint; import docking.widgets.table.constraint.dialog.*; -import docking.widgets.table.constraint.provider.*; import ghidra.framework.options.SaveState; -import ghidra.util.Msg; import ghidra.util.classfinder.ClassSearcher; -import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; /** @@ -85,6 +80,18 @@ public class ColumnTableFilterTest extends AbstractDockingTest { return new TableModelWrapper<>(testTableModel); } + private RowObjectFilterModel createTableModelWithNewlines() { + TestTableModel testTableModel = new TestTableModel(); + + testTableModel.addColumn("Name", + new String[] { "Hello World\n", "\nHello World", "Hello\nWorld", "Hello World" }); + + testTableModel.addColumn("Long ID", + new Long[] { 1000l, 2000l, 3000l }); + + return new TableModelWrapper<>(testTableModel); + } + private Date date(String dateString) { try { return DATE_FORMAT.parse(dateString); @@ -107,6 +114,27 @@ public class ColumnTableFilterTest extends AbstractDockingTest { assertEquals("Chuck", tableModel.getValueAt(0, col)); } + @Test + public void testStringStartsWithColumnFilter_WithNewline() { + + runSwing(() -> gTable.dispose()); + + tableModel = createTableModelWithNewlines(); + gTable = new GTable(tableModel); + filterModel = new ColumnFilterDialogModel<>(tableModel, gTable.getColumnModel(), null); + + addFirstFilter("Name", "Starts With", "Hello"); + + applyFilter(); + + assertEquals(3, tableModel.getRowCount()); + + int col = getColumn("Name"); + assertEquals("Hello World\n", tableModel.getValueAt(0, col)); + assertEquals("Hello\nWorld", tableModel.getValueAt(1, col)); + assertEquals("Hello World", tableModel.getValueAt(2, col)); + } + @Test public void testStringDoesNotStartsWithColumnFilter() { addFirstFilter("Name", "Does Not Start With", "C"); @@ -136,6 +164,27 @@ public class ColumnTableFilterTest extends AbstractDockingTest { assertEquals("Dave", tableModel.getValueAt(1, col)); } + @Test + public void testStringEndsWithColumnFilter_WithNewline() { + + runSwing(() -> gTable.dispose()); + + tableModel = createTableModelWithNewlines(); + gTable = new GTable(tableModel); + filterModel = new ColumnFilterDialogModel<>(tableModel, gTable.getColumnModel(), null); + + addFirstFilter("Name", "Ends With", "World"); + + applyFilter(); + + assertEquals(3, tableModel.getRowCount()); + + int col = getColumn("Name"); + assertEquals("\nHello World", tableModel.getValueAt(0, col)); + assertEquals("Hello\nWorld", tableModel.getValueAt(1, col)); + assertEquals("Hello World", tableModel.getValueAt(2, col)); + } + @Test public void testStringContainsColumnFilter() { addFirstFilter("Name", "Contains", "l"); @@ -149,6 +198,44 @@ public class ColumnTableFilterTest extends AbstractDockingTest { assertEquals("Ellen", tableModel.getValueAt(1, col)); } + @Test + public void testStringContainsColumnFilter_WithNewline() { + + runSwing(() -> gTable.dispose()); + + tableModel = createTableModelWithNewlines(); + gTable = new GTable(tableModel); + filterModel = new ColumnFilterDialogModel<>(tableModel, gTable.getColumnModel(), null); + + addFirstFilter("Name", "Contains", "Hello"); + + applyFilter(); + + assertEquals(4, tableModel.getRowCount()); + + int col = getColumn("Name"); + assertEquals("Hello World\n", tableModel.getValueAt(0, col)); + assertEquals("\nHello World", tableModel.getValueAt(1, col)); + assertEquals("Hello\nWorld", tableModel.getValueAt(2, col)); + assertEquals("Hello World", tableModel.getValueAt(3, col)); + } + + @Test + public void testStringNotContainsColumnFilter_WithNewline() { + + runSwing(() -> gTable.dispose()); + + tableModel = createTableModelWithNewlines(); + gTable = new GTable(tableModel); + filterModel = new ColumnFilterDialogModel<>(tableModel, gTable.getColumnModel(), null); + + addFirstFilter("Name", "Does Not Contain", "Hello"); + + applyFilter(); + + assertEquals(0, tableModel.getRowCount()); + } + @Test public void testStringMatchesColumnFilter() { addFirstFilter("Name", "Matches Regex", ".*l.*e.*"); @@ -162,6 +249,49 @@ public class ColumnTableFilterTest extends AbstractDockingTest { assertEquals("Ellen", tableModel.getValueAt(1, col)); } + @Test + public void testStringMatchesColumnFilter_WithNewline() { + + runSwing(() -> gTable.dispose()); + + tableModel = createTableModelWithNewlines(); + gTable = new GTable(tableModel); + filterModel = new ColumnFilterDialogModel<>(tableModel, gTable.getColumnModel(), null); + + addFirstFilter("Name", "Matches Regex", ".*Hello\\sWorld.*"); + + applyFilter(); + + assertEquals(2, tableModel.getRowCount()); + + // only matches when newline not at the beginning or end, since DOTALL mode is not used + int col = getColumn("Name"); + assertEquals("Hello\nWorld", tableModel.getValueAt(0, col)); + assertEquals("Hello World", tableModel.getValueAt(1, col)); + } + + @Test + public void testStringMatchesColumnFilter_WithNewline_WithDotallMode() { + + runSwing(() -> gTable.dispose()); + + tableModel = createTableModelWithNewlines(); + gTable = new GTable(tableModel); + filterModel = new ColumnFilterDialogModel<>(tableModel, gTable.getColumnModel(), null); + + addFirstFilter("Name", "Matches Regex", "(?s).*Hello\\sWorld.*"); + + applyFilter(); + + assertEquals(4, tableModel.getRowCount()); + + int col = getColumn("Name"); + assertEquals("Hello World\n", tableModel.getValueAt(0, col)); + assertEquals("\nHello World", tableModel.getValueAt(1, col)); + assertEquals("Hello\nWorld", tableModel.getValueAt(2, col)); + assertEquals("Hello World", tableModel.getValueAt(3, col)); + } + @Test public void testStringNotMatchesColumnFilter() { addFirstFilter("Name", "Does Not Match Regex", ".*l.*e.*"); @@ -683,20 +813,6 @@ public class ColumnTableFilterTest extends AbstractDockingTest { return null; } - @SuppressWarnings("unchecked") - private List> loadConstraints() { - List> list = new ArrayList<>(); - list.addAll(new NumberColumnConstraintProvider().getColumnConstraints()); - list.addAll(new StringColumnConstraintProvider().getColumnConstraints()); - Collection> columnConstraints = - new DateColumnConstraintProvider().getColumnConstraints(); - for (ColumnConstraint c : columnConstraints) { - list.add(new MappedColumnConstraint<>(new DateColumnTypeMapper(), - (ColumnConstraint) c)); - } - return list; - } - private void addConstraints(String columnName, String[] constraintNames, String[] constraintValues, boolean first) { diff --git a/Ghidra/Framework/Docking/src/test/java/docking/widgets/table/columnfilter/TestTableModel.java b/Ghidra/Framework/Docking/src/test/java/docking/widgets/table/columnfilter/TestTableModel.java index 3cb62dacc6..147e42f9f2 100644 --- a/Ghidra/Framework/Docking/src/test/java/docking/widgets/table/columnfilter/TestTableModel.java +++ b/Ghidra/Framework/Docking/src/test/java/docking/widgets/table/columnfilter/TestTableModel.java @@ -112,7 +112,7 @@ class TestTableModel implements RowObjectTableModel { @Override public List getModelData() { List rowObjects = new ArrayList<>(); - for (int i = 0; i < getColumnCount(); i++) { + for (int i = 0; i < rowCount; i++) { rowObjects.add(i); } return rowObjects;