diff options
| author | Dirk Fauth | 2022-11-10 10:55:34 +0000 |
|---|---|---|
| committer | Dirk Fauth | 2022-11-10 10:55:34 +0000 |
| commit | 05475afee9d892a0e1bbf62af1988b9bd62b654c (patch) | |
| tree | 9c518939e2b0ec1d0fc706677997a8bf6b07a0dd | |
| parent | 40eb716758467093ca42409d2fbf5293c4747266 (diff) | |
| download | org.eclipse.nebula.widgets.nattable-05475afee9d892a0e1bbf62af1988b9bd62b654c.tar.gz org.eclipse.nebula.widgets.nattable-05475afee9d892a0e1bbf62af1988b9bd62b654c.tar.xz org.eclipse.nebula.widgets.nattable-05475afee9d892a0e1bbf62af1988b9bd62b654c.zip | |
Bug 581027 - Not possible to specify if filters are AND or OR combined
Signed-off-by: Dirk Fauth <dirk.fauth@googlemail.com>
Change-Id: Ic30013ec1784b0a5c7a3fbf29bb87fa45f05e6b0
6 files changed, 578 insertions, 111 deletions
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/filterrow/combobox/FilterRowComboBoxDataProvider.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/filterrow/combobox/FilterRowComboBoxDataProvider.java index 6c812a18..c70fad92 100644 --- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/filterrow/combobox/FilterRowComboBoxDataProvider.java +++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/filterrow/combobox/FilterRowComboBoxDataProvider.java @@ -241,6 +241,10 @@ public class FilterRowComboBoxDataProvider<T> implements IComboBoxDataProvider, .unordered() .parallel() .map(x -> this.columnAccessor.getDataValue(x, columnIndex)) + // TODO 2.1 make the distinct of null and "" configurable, and + // enable it by default + // .map(x -> (x instanceof String && ((String) x).isEmpty()) ? + // null : x) .distinct() .collect(Collectors.toList()); diff --git a/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_600_GlazedLists/_603_Filter/_6037_MixedFilterRowExample.java b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_600_GlazedLists/_603_Filter/_6037_MixedFilterRowExample.java index 5c7ded65..5bac2218 100644 --- a/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_600_GlazedLists/_603_Filter/_6037_MixedFilterRowExample.java +++ b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_600_GlazedLists/_603_Filter/_6037_MixedFilterRowExample.java @@ -51,6 +51,7 @@ import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes; import org.eclipse.nebula.widgets.nattable.edit.editor.ComboBoxCellEditor; import org.eclipse.nebula.widgets.nattable.edit.editor.ICellEditor; import org.eclipse.nebula.widgets.nattable.edit.editor.IComboBoxDataProvider; +import org.eclipse.nebula.widgets.nattable.edit.editor.TextCellEditor; import org.eclipse.nebula.widgets.nattable.examples.AbstractNatExample; import org.eclipse.nebula.widgets.nattable.examples.runner.StandaloneNatExampleRunner; import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer; @@ -252,7 +253,7 @@ public class _6037_MixedFilterRowExample extends AbstractNatExample { filterRowHeaderLayer.addConfiguration( new ComboBoxFilterRowConfiguration( filterEditor, - new ComboBoxFilterIconPainter(comboBoxDataProvider, GUIHelper.getImage("filter"), null))); + new ComboBoxFilterIconPainter(comboBoxDataProvider))); // add the specialized configuration to the // ComboBoxFilterRowHeaderComposite @@ -753,7 +754,7 @@ public class _6037_MixedFilterRowExample extends AbstractNatExample { configRegistry.registerConfigAttribute( EditConfigAttributes.CELL_EDITOR, - new FilterRowTextCellEditor(), + new TextCellEditor(), DisplayMode.NORMAL, FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX + DataModelConstants.HOUSENUMBER_COLUMN_POSITION); @@ -796,6 +797,9 @@ public class _6037_MixedFilterRowExample extends AbstractNatExample { }, DisplayMode.NORMAL, GridRegion.FILTER_ROW); + + configRegistry.registerConfigAttribute( + FilterRowConfigAttributes.TEXT_DELIMITER, "[&\\|]"); //$NON-NLS-1$ } } diff --git a/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/DefaultGlazedListsFilterStrategyTest.java b/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/DefaultGlazedListsFilterStrategyTest.java index ea1c91bc..a4bc730e 100644 --- a/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/DefaultGlazedListsFilterStrategyTest.java +++ b/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/DefaultGlazedListsFilterStrategyTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018, 2020 Dirk Fauth. + * Copyright (c) 2018, 2022 Dirk Fauth. * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -18,13 +18,18 @@ import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry; import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration; import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor; import org.eclipse.nebula.widgets.nattable.data.ReflectiveColumnPropertyAccessor; +import org.eclipse.nebula.widgets.nattable.data.convert.DefaultDoubleDisplayConverter; import org.eclipse.nebula.widgets.nattable.dataset.fixture.data.RowDataFixture; import org.eclipse.nebula.widgets.nattable.dataset.fixture.data.RowDataListFixture; import org.eclipse.nebula.widgets.nattable.dataset.person.Person; import org.eclipse.nebula.widgets.nattable.dataset.person.PersonService; import org.eclipse.nebula.widgets.nattable.extension.glazedlists.fixture.DataLayerFixture; +import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowDataLayer; import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowDataProvider; +import org.eclipse.nebula.widgets.nattable.filterrow.TextMatchingMode; import org.eclipse.nebula.widgets.nattable.filterrow.config.DefaultFilterRowConfiguration; +import org.eclipse.nebula.widgets.nattable.filterrow.config.FilterRowConfigAttributes; +import org.eclipse.nebula.widgets.nattable.style.DisplayMode; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -46,7 +51,8 @@ public class DefaultGlazedListsFilterStrategyTest { "lastName", "gender", "married", - "birthday" }; + "birthday", + "money" }; @BeforeClass public static void init() { @@ -60,7 +66,7 @@ public class DefaultGlazedListsFilterStrategyTest { new DefaultNatTableStyleConfiguration().configureRegistry(configRegistry); new DefaultFilterRowConfiguration().configureRegistry(configRegistry); - columnHeaderLayer = new DataLayerFixture(5, 2, 100, 50); + columnHeaderLayer = new DataLayerFixture(6, 2, 100, 50); dataProvider = new FilterRowDataProvider<>( new DefaultGlazedListsFilterStrategy<>( filterList, @@ -142,7 +148,7 @@ public class DefaultGlazedListsFilterStrategyTest { new DefaultNatTableStyleConfiguration().configureRegistry(configRegistry); new DefaultFilterRowConfiguration().configureRegistry(configRegistry); - DataLayerFixture columnHeaderLayer = new DataLayerFixture(5, 2, 100, 50); + DataLayerFixture columnHeaderLayer = new DataLayerFixture(6, 2, 100, 50); FilterRowDataProvider<RowDataFixture> dataProvider = new FilterRowDataProvider<>( new DefaultGlazedListsFilterStrategy<>( filterList, @@ -180,7 +186,7 @@ public class DefaultGlazedListsFilterStrategyTest { @Test public void shouldReEvaluateWithChange() { FilterList<Person> persons = new FilterList<>(GlazedLists.eventList(PersonService.getFixedPersons())); - DataLayerFixture columnHeaderLayer = new DataLayerFixture(5, 2, 100, 50); + DataLayerFixture columnHeaderLayer = new DataLayerFixture(6, 2, 100, 50); FilterRowDataProvider<Person> dataProvider = new FilterRowDataProvider<>( new DefaultGlazedListsFilterStrategy<>( persons, @@ -211,4 +217,190 @@ public class DefaultGlazedListsFilterStrategyTest { assertEquals(8, persons.size()); } + @Test + public void shouldFilterThresholds() { + FilterList<Person> persons = new FilterList<>(GlazedLists.eventList(PersonService.getFixedPersons())); + DataLayerFixture columnHeaderLayer = new DataLayerFixture(6, 2, 100, 50); + + ConfigRegistry configRegistry = new ConfigRegistry(); + + new DefaultNatTableStyleConfiguration().configureRegistry(configRegistry); + new DefaultFilterRowConfiguration().configureRegistry(configRegistry); + + // register the FILTER_DISPLAY_CONVERTER and the REGULAR_EXPRESSION text + // matching mode to enable threshold matching + configRegistry.registerConfigAttribute( + FilterRowConfigAttributes.FILTER_DISPLAY_CONVERTER, + new DefaultDoubleDisplayConverter(), + DisplayMode.NORMAL, + FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX + "5"); + + configRegistry.registerConfigAttribute( + FilterRowConfigAttributes.TEXT_MATCHING_MODE, + TextMatchingMode.REGULAR_EXPRESSION, + DisplayMode.NORMAL, + FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX + "5"); + + // register a TEXT_DELIMITER to be able to combine filters + // a single character always results in a AND filter combination + configRegistry.registerConfigAttribute( + FilterRowConfigAttributes.TEXT_DELIMITER, "&"); + // + // configRegistry.registerConfigAttribute( + // FilterRowConfigAttributes.TEXT_DELIMITER, "[&|]"); + + FilterRowDataProvider<Person> dataProvider = new FilterRowDataProvider<>( + new DefaultGlazedListsFilterStrategy<>( + persons, + new ReflectiveColumnPropertyAccessor<Person>(personPropertyNames), + configRegistry), + columnHeaderLayer, + columnHeaderLayer.getDataProvider(), configRegistry); + + assertEquals(18, persons.size()); + + // set money of all Bart entries to 10 + dataProvider.setDataValue(0, 1, "Bart"); + assertEquals(3, persons.size()); + for (Person person : persons) { + person.setMoney(10d); + } + + // set money of all Lisa entries to 90 + dataProvider.setDataValue(0, 1, "Lisa"); + assertEquals(2, persons.size()); + for (Person person : persons) { + person.setMoney(90d); + } + + // set money of all Rod entries to 40 + dataProvider.setDataValue(0, 1, "Rod"); + assertEquals(2, persons.size()); + for (Person person : persons) { + person.setMoney(40d); + } + + // set money of all Tod entries to 60 + dataProvider.setDataValue(0, 1, "Tod"); + assertEquals(2, persons.size()); + for (Person person : persons) { + person.setMoney(60d); + } + + dataProvider.clearAllFilters(); + + assertEquals(18, persons.size()); + + // get all Persons with >= 100 + dataProvider.setDataValue(5, 1, ">=100"); + assertEquals(9, persons.size()); + + // get all Persons with > 30 & < 80 + // this means all Rods and Tods + dataProvider.setDataValue(5, 1, "> 30 & < 80"); + assertEquals(4, persons.size()); + } + + @Test + public void shouldFilterThresholdsWithANDOR() { + FilterList<Person> persons = new FilterList<>(GlazedLists.eventList(PersonService.getFixedPersons())); + DataLayerFixture columnHeaderLayer = new DataLayerFixture(6, 2, 100, 50); + + ConfigRegistry configRegistry = new ConfigRegistry(); + + new DefaultNatTableStyleConfiguration().configureRegistry(configRegistry); + new DefaultFilterRowConfiguration().configureRegistry(configRegistry); + + // register the FILTER_DISPLAY_CONVERTER and the REGULAR_EXPRESSION text + // matching mode to enable threshold matching + configRegistry.registerConfigAttribute( + FilterRowConfigAttributes.FILTER_DISPLAY_CONVERTER, + new DefaultDoubleDisplayConverter(), + DisplayMode.NORMAL, + FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX + "5"); + + configRegistry.registerConfigAttribute( + FilterRowConfigAttributes.TEXT_MATCHING_MODE, + TextMatchingMode.REGULAR_EXPRESSION, + DisplayMode.NORMAL, + FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX + "5"); + + // register a TEXT_DELIMITER to be able to combine filters + // use the regular expression to use & for AND and | for OR + configRegistry.registerConfigAttribute( + FilterRowConfigAttributes.TEXT_DELIMITER, "[&\\|]"); + + FilterRowDataProvider<Person> dataProvider = new FilterRowDataProvider<>( + new DefaultGlazedListsFilterStrategy<>( + persons, + new ReflectiveColumnPropertyAccessor<Person>(personPropertyNames), + configRegistry), + columnHeaderLayer, + columnHeaderLayer.getDataProvider(), configRegistry); + + assertEquals(18, persons.size()); + + // set money of all Bart entries to 10 + dataProvider.setDataValue(0, 1, "Bart"); + assertEquals(3, persons.size()); + for (Person person : persons) { + person.setMoney(10d); + } + + // set money of all Lisa entries to 90 + dataProvider.setDataValue(0, 1, "Lisa"); + assertEquals(2, persons.size()); + for (Person person : persons) { + person.setMoney(90d); + } + + // set money of all Marge entries to 200 + dataProvider.setDataValue(0, 1, "Marge"); + assertEquals(2, persons.size()); + for (Person person : persons) { + person.setMoney(200d); + } + + // set money of all Rod entries to 40 + dataProvider.setDataValue(0, 1, "Rod"); + assertEquals(2, persons.size()); + for (Person person : persons) { + person.setMoney(40d); + } + + // set money of all Tod entries to 60 + dataProvider.setDataValue(0, 1, "Tod"); + assertEquals(2, persons.size()); + for (Person person : persons) { + person.setMoney(60d); + } + + dataProvider.clearAllFilters(); + + assertEquals(18, persons.size()); + + // get all Persons with >= 100 + dataProvider.setDataValue(5, 1, ">=100"); + assertEquals(9, persons.size()); + + // get all Persons with > 30 & < 80 + // this means all Rods and Tods + dataProvider.setDataValue(5, 1, "> 30 & < 80"); + assertEquals(4, persons.size()); + + // get all Persons with > 30 | < 80 + // should actually return all entries + dataProvider.setDataValue(5, 1, "> 30 | < 80"); + assertEquals(18, persons.size()); + + // get all Persons with < 30 | > 100 + // should return all Bart and all Marge + dataProvider.setDataValue(5, 1, "< 30 | > 100"); + assertEquals(5, persons.size()); + + // now also filter Marge to only get the Bart entries + // test to verify that combined filters are working + dataProvider.setDataValue(0, 1, "Bart"); + assertEquals(3, persons.size()); + } } diff --git a/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/FilterRowUtilsTest.java b/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/FilterRowUtilsTest.java index d3cffcda..ee8716c3 100644 --- a/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/FilterRowUtilsTest.java +++ b/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/FilterRowUtilsTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012, 2020 Original authors and others. + * Copyright (c) 2012, 2022 Original authors and others. * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -13,6 +13,8 @@ package org.eclipse.nebula.widgets.nattable.extension.glazedlists.filterrow; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import java.util.List; @@ -28,11 +30,15 @@ public class FilterRowUtilsTest { private static final String NULL_DELIMITER = null; private static final String COMMA_DELIMITER = ","; + private static final String AND_OR_DELIMITER = "[&\\|]"; @Test public void parseWithoutThresholdSymbols() throws Exception { - ParseResult result = FilterRowUtils.parse("100", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); + ParseResult result = FilterRowUtils.parse( + "100", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); assertEquals(MatchType.NONE, result.getMatchOperation()); assertEquals("100", result.getValueToMatch()); @@ -45,90 +51,120 @@ public class FilterRowUtilsTest { // e.g. spaces in the custom regular expression will lead to a wrong parsed // valueToMatch // -> see the parseMultipleStringsWithSpaces() test below - public void parseWithInvalidSymbols() throws Exception { - ParseResult result = FilterRowUtils.parse("# 100", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); + public void parseWithInvalidSymbols() { + ParseResult result = FilterRowUtils.parse( + "# 100", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); assertEquals(MatchType.NONE, result.getMatchOperation()); assertEquals("100", result.getValueToMatch()); } @Test - public void parseGreaterThanSymbol() throws Exception { - ParseResult result = FilterRowUtils.parse(" > 100 ", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); + public void parseGreaterThanSymbol() { + ParseResult result = FilterRowUtils.parse( + " > 100 ", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); assertEquals(MatchType.GREATER_THAN, result.getMatchOperation()); assertEquals("100", result.getValueToMatch()); } @Test - public void parseGreaterThanSymbolWithoutSpace() throws Exception { - ParseResult result = FilterRowUtils.parse(">100", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); + public void parseGreaterThanSymbolWithoutSpace() { + ParseResult result = FilterRowUtils.parse( + ">100", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); assertEquals(MatchType.GREATER_THAN, result.getMatchOperation()); assertEquals("100", result.getValueToMatch()); } @Test - public void parseLessThanSymbol() throws Exception { - ParseResult result = FilterRowUtils.parse("< 100", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); + public void parseLessThanSymbol() { + ParseResult result = FilterRowUtils.parse( + "< 100", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); assertEquals(MatchType.LESS_THAN, result.getMatchOperation()); assertEquals("100", result.getValueToMatch()); } @Test - public void parseLessThanSymbolWithoutSpace() throws Exception { - ParseResult result = FilterRowUtils.parse("<100", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); + public void parseLessThanSymbolWithoutSpace() { + ParseResult result = FilterRowUtils.parse( + "<100", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); assertEquals(MatchType.LESS_THAN, result.getMatchOperation()); assertEquals("100", result.getValueToMatch()); } @Test - public void parseEqualSymbol() throws Exception { - ParseResult result = FilterRowUtils.parse("=100", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); + public void parseEqualSymbol() { + ParseResult result = FilterRowUtils.parse( + "=100", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); assertEquals(MatchType.EQUAL, result.getMatchOperation()); assertEquals("100", result.getValueToMatch()); } @Test - public void parseEqualSymbolWithSpace() throws Exception { - ParseResult result = FilterRowUtils.parse("= 100", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); + public void parseEqualSymbolWithSpace() { + ParseResult result = FilterRowUtils.parse( + "= 100", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); assertEquals(MatchType.EQUAL, result.getMatchOperation()); assertEquals("100", result.getValueToMatch()); } @Test - public void parseNotEqualSymbol() throws Exception { - ParseResult result = FilterRowUtils.parse("<>100", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); + public void parseNotEqualSymbol() { + ParseResult result = FilterRowUtils.parse( + "<>100", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); assertEquals(MatchType.NOT_EQUAL, result.getMatchOperation()); assertEquals("100", result.getValueToMatch()); } @Test - public void parseNotEqualSymbolWithSpace() throws Exception { - ParseResult result = FilterRowUtils.parse(" <> 100", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); + public void parseNotEqualSymbolWithSpace() { + ParseResult result = FilterRowUtils.parse( + " <> 100", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); assertEquals(MatchType.NOT_EQUAL, result.getMatchOperation()); assertEquals("100", result.getValueToMatch()); } @Test - public void parseGreaterThanOrEqualSymbol() throws Exception { - ParseResult result = FilterRowUtils.parse(">= 100", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); + public void parseGreaterThanOrEqualSymbol() { + ParseResult result = FilterRowUtils.parse( + ">= 100", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); assertEquals(MatchType.GREATER_THAN_OR_EQUAL, result.getMatchOperation()); @@ -136,28 +172,36 @@ public class FilterRowUtilsTest { } @Test - public void parseGreaterThanOrEqualSymbolWithSpace() throws Exception { - ParseResult result = FilterRowUtils.parse(" >= 100", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); - - assertEquals(MatchType.GREATER_THAN_OR_EQUAL, - result.getMatchOperation()); + public void parseGreaterThanOrEqualSymbolWithSpace() { + ParseResult result = FilterRowUtils.parse( + " >= 100", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); + + assertEquals(MatchType.GREATER_THAN_OR_EQUAL, result.getMatchOperation()); assertEquals("100", result.getValueToMatch()); } @Test - public void parseLessThanOrEqualSymbol() throws Exception { - ParseResult result = FilterRowUtils.parse("<=100", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); + public void parseLessThanOrEqualSymbol() { + ParseResult result = FilterRowUtils.parse( + "<=100", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); assertEquals(MatchType.LESS_THAN_OR_EQUAL, result.getMatchOperation()); assertEquals("100", result.getValueToMatch()); } @Test - public void parseLessThanOrEqualSymbolWithSpace() throws Exception { - ParseResult result = FilterRowUtils.parse("<= 100", NULL_DELIMITER, - TextMatchingMode.REGULAR_EXPRESSION).get(0); + public void parseLessThanOrEqualSymbolWithSpace() { + ParseResult result = FilterRowUtils.parse( + "<= 100", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION) + .get(0); assertEquals(MatchType.LESS_THAN_OR_EQUAL, result.getMatchOperation()); assertEquals("100", result.getValueToMatch()); @@ -165,53 +209,50 @@ public class FilterRowUtilsTest { @Test @SuppressWarnings({ "unchecked", "rawtypes" }) - public void shouldMapBetweenNatTableAndGlazedLists() throws Exception { + public void shouldMapBetweenNatTableAndGlazedLists() { ThresholdMatcherEditor fixture = new ThresholdMatcherEditor(); FilterRowUtils.setMatchOperation(fixture, MatchType.EQUAL); assertEquals(ThresholdMatcherEditor.EQUAL, fixture.getMatchOperation()); FilterRowUtils.setMatchOperation(fixture, MatchType.NOT_EQUAL); - assertEquals(ThresholdMatcherEditor.NOT_EQUAL, - fixture.getMatchOperation()); + assertEquals(ThresholdMatcherEditor.NOT_EQUAL, fixture.getMatchOperation()); FilterRowUtils.setMatchOperation(fixture, MatchType.GREATER_THAN); - assertEquals(ThresholdMatcherEditor.GREATER_THAN, - fixture.getMatchOperation()); + assertEquals(ThresholdMatcherEditor.GREATER_THAN, fixture.getMatchOperation()); - FilterRowUtils.setMatchOperation(fixture, - MatchType.GREATER_THAN_OR_EQUAL); - assertEquals(ThresholdMatcherEditor.GREATER_THAN_OR_EQUAL, - fixture.getMatchOperation()); + FilterRowUtils.setMatchOperation(fixture, MatchType.GREATER_THAN_OR_EQUAL); + assertEquals(ThresholdMatcherEditor.GREATER_THAN_OR_EQUAL, fixture.getMatchOperation()); FilterRowUtils.setMatchOperation(fixture, MatchType.LESS_THAN); - assertEquals(ThresholdMatcherEditor.LESS_THAN, - fixture.getMatchOperation()); + assertEquals(ThresholdMatcherEditor.LESS_THAN, fixture.getMatchOperation()); FilterRowUtils.setMatchOperation(fixture, MatchType.LESS_THAN_OR_EQUAL); - assertEquals(ThresholdMatcherEditor.LESS_THAN_OR_EQUAL, - fixture.getMatchOperation()); + assertEquals(ThresholdMatcherEditor.LESS_THAN_OR_EQUAL, fixture.getMatchOperation()); } @Test public void parseMultiple() { - List<ParseResult> results = FilterRowUtils.parse("100, <=200", - COMMA_DELIMITER, TextMatchingMode.REGULAR_EXPRESSION); + List<ParseResult> results = FilterRowUtils.parse( + "100, <=200", + COMMA_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION); assertEquals(2, results.size()); assertEquals(MatchType.NONE, results.get(0).getMatchOperation()); assertEquals("100", results.get(0).getValueToMatch()); - assertEquals(MatchType.LESS_THAN_OR_EQUAL, results.get(1) - .getMatchOperation()); + assertEquals(MatchType.LESS_THAN_OR_EQUAL, results.get(1).getMatchOperation()); assertEquals("200", results.get(1).getValueToMatch()); } @Test public void parseMultipleStrings() { - List<ParseResult> results = FilterRowUtils.parse("(Bart|Lisa)", - NULL_DELIMITER, TextMatchingMode.REGULAR_EXPRESSION); + List<ParseResult> results = FilterRowUtils.parse( + "(Bart|Lisa)", + NULL_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION); assertEquals(1, results.size()); @@ -220,16 +261,168 @@ public class FilterRowUtilsTest { } @Test + public void parseMultipleStringsWithDelimiter() { + List<ParseResult> results = FilterRowUtils.parse( + "Bart|Lisa", + "\\|", + TextMatchingMode.REGULAR_EXPRESSION); + + assertEquals(2, results.size()); + + assertEquals(MatchType.NONE, results.get(0).getMatchOperation()); + assertEquals("Bart", results.get(0).getValueToMatch()); + assertEquals(MatchType.NONE, results.get(1).getMatchOperation()); + assertEquals("Lisa", results.get(1).getValueToMatch()); + } + + @Test + public void parseMultipleStringsWithDelimiterRegex() { + List<ParseResult> results = FilterRowUtils.parse( + "Bart|Lisa&Maggie", + "[\\|&]", + TextMatchingMode.REGULAR_EXPRESSION); + + assertEquals(3, results.size()); + + assertEquals(MatchType.NONE, results.get(0).getMatchOperation()); + assertEquals("Bart", results.get(0).getValueToMatch()); + assertEquals(MatchType.NONE, results.get(1).getMatchOperation()); + assertEquals("Lisa", results.get(1).getValueToMatch()); + assertEquals(MatchType.NONE, results.get(2).getMatchOperation()); + assertEquals("Maggie", results.get(2).getValueToMatch()); + } + + @Test + public void parseMultipleExpressionsWithDelimiterRegexAnd() { + List<ParseResult> results = FilterRowUtils.parse( + ">= 100 & <= 200", + AND_OR_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION); + + assertEquals(2, results.size()); + + assertEquals(MatchType.GREATER_THAN_OR_EQUAL, results.get(0).getMatchOperation()); + assertEquals("100", results.get(0).getValueToMatch()); + assertEquals(MatchType.LESS_THAN_OR_EQUAL, results.get(1).getMatchOperation()); + assertEquals("200", results.get(1).getValueToMatch()); + } + + @Test + public void parseMultipleExpressionsWithDelimiterRegexOr() { + List<ParseResult> results = FilterRowUtils.parse( + "< 100 | > 200", + AND_OR_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION); + + assertEquals(2, results.size()); + + assertEquals(MatchType.LESS_THAN, results.get(0).getMatchOperation()); + assertEquals("100", results.get(0).getValueToMatch()); + assertEquals(MatchType.GREATER_THAN, results.get(1).getMatchOperation()); + assertEquals("200", results.get(1).getValueToMatch()); + } + + @Test public void parseMultipleStringsWithSpaces() { List<ParseResult> results = FilterRowUtils.parse( - "(Bart Simpson|Lisa Simpson)", NULL_DELIMITER, + "(Bart Simpson|Lisa Simpson)", + NULL_DELIMITER, TextMatchingMode.REGULAR_EXPRESSION); assertEquals(1, results.size()); assertEquals(MatchType.NONE, results.get(0).getMatchOperation()); - assertEquals("(Bart Simpson|Lisa Simpson)", results.get(0) - .getValueToMatch()); + assertEquals("(Bart Simpson|Lisa Simpson)", results.get(0).getValueToMatch()); } + @Test + public void parseMultipleStringsWithSpacesWithDelimiter() { + List<ParseResult> results = FilterRowUtils.parse( + "Bart Simpson|Lisa Simpson", + AND_OR_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION); + + assertEquals(2, results.size()); + + assertEquals(MatchType.NONE, results.get(0).getMatchOperation()); + assertEquals("Bart Simpson", results.get(0).getValueToMatch()); + assertEquals(MatchType.NONE, results.get(1).getMatchOperation()); + assertEquals("Lisa Simpson", results.get(1).getValueToMatch()); + } + + @Test + public void parseMultipleStringsWithSpacesWithDelimiterNoSplit() { + List<ParseResult> results = FilterRowUtils.parse( + "(Bart Simpson|Lisa Simpson)", + AND_OR_DELIMITER, + TextMatchingMode.REGULAR_EXPRESSION); + + assertEquals(1, results.size()); + + assertEquals(MatchType.NONE, results.get(0).getMatchOperation()); + assertEquals("(Bart Simpson|Lisa Simpson)", results.get(0).getValueToMatch()); + } + + @Test + public void shouldMatch() { + assertNull("Single character matches", getSeparatorCharacters(";")); + assertNull("Missing end bracket matches", getSeparatorCharacters("[&;")); + assertNull("Missing start bracket matches", getSeparatorCharacters("&;]")); + assertNull("Missing brackets matches", getSeparatorCharacters("&;")); + assertNull("More than 2 characters matches", getSeparatorCharacters("&;\\|")); + assertNull("Null matches", getSeparatorCharacters(null)); + assertNull("Empty matches", getSeparatorCharacters("")); + assertNull("Text matches", getSeparatorCharacters("NatTable")); + + String[] delimiterChars = getSeparatorCharacters("[&;]"); + assertNotNull("Two characters doesn't match", delimiterChars); + assertEquals("&", delimiterChars[0]); + assertEquals(";", delimiterChars[1]); + + delimiterChars = getSeparatorCharacters(AND_OR_DELIMITER); + assertNotNull("Second character masked doesn't match", delimiterChars); + assertEquals("&", delimiterChars[0]); + assertEquals("|", delimiterChars[1]); + + delimiterChars = getSeparatorCharacters("[\\|&]"); + assertNotNull("First character masked doesn't match", delimiterChars); + assertEquals("|", delimiterChars[0]); + assertEquals("&", delimiterChars[1]); + + delimiterChars = getSeparatorCharacters("[\\|\\\\]"); + assertNotNull("Both characters masked don't match", delimiterChars); + assertEquals("|", delimiterChars[0]); + assertEquals("\\", delimiterChars[1]); + + } + + // TODO 2.1 move this method to FilterRowUtils + private String[] getSeparatorCharacters(String delimiter) { + // start with [ and end with ] + // (.){2} => e.g. ab + // (.)\\\\(.) => a\b + // \\\\(.){2} => \ab + // \\\\(.)\\\\(.) => \a\b + String twoCharacterRegex = "\\[(((.){2})|((.)\\\\(.))|(\\\\(.){2})|(\\\\(.)\\\\(.)))\\]"; + + if (delimiter != null && delimiter.matches(twoCharacterRegex)) { + String inspect = delimiter.substring(1, delimiter.length() - 1); + + // special handling if the backslash is used as delimiter for AND or + // OR + inspect = inspect.replace("\\\\", "backslash"); + + // now replace all backslashed + inspect = inspect.replaceAll("\\\\", ""); + + // convert back the "backslash" to "\" + inspect = inspect.replace("backslash", "\\"); + if (inspect.length() == 2) { + String[] result = new String[] { inspect.substring(0, 1), inspect.substring(1, 2) }; + return result; + } + } + + return null; + } } diff --git a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/DefaultGlazedListsFilterStrategy.java b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/DefaultGlazedListsFilterStrategy.java index e2e08977..7427bdf2 100644 --- a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/DefaultGlazedListsFilterStrategy.java +++ b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/DefaultGlazedListsFilterStrategy.java @@ -181,6 +181,7 @@ public class DefaultGlazedListsFilterStrategy<T> implements IFilterStrategy<T> { List<ParseResult> parseResults = FilterRowUtils.parse(filterText, textDelimiter, textMatchingMode); EventList<MatcherEditor<T>> stringMatcherEditors = new BasicEventList<>(); + EventList<MatcherEditor<T>> thresholdMatcherEditors = new BasicEventList<>(); for (ParseResult parseResult : parseResults) { try { MatchType matchOperation = parseResult.getMatchOperation(); @@ -193,7 +194,7 @@ public class DefaultGlazedListsFilterStrategy<T> implements IFilterStrategy<T> { } else { Object threshold = displayConverter.displayToCanonicalValue(parseResult.getValueToMatch()); - matcherEditors.add(getThresholdMatcherEditor( + thresholdMatcherEditors.add(getThresholdMatcherEditor( columnIndex, threshold, comparator, @@ -205,11 +206,27 @@ public class DefaultGlazedListsFilterStrategy<T> implements IFilterStrategy<T> { } } + String[] separator = getSeparatorCharacters(textDelimiter); + if (!stringMatcherEditors.isEmpty()) { CompositeMatcherEditor<T> stringCompositeMatcherEditor = new CompositeMatcherEditor<>(stringMatcherEditors); - stringCompositeMatcherEditor.setMode(CompositeMatcherEditor.OR); + if (separator == null || filterText.contains(separator[1])) { + stringCompositeMatcherEditor.setMode(CompositeMatcherEditor.OR); + } else { + stringCompositeMatcherEditor.setMode(CompositeMatcherEditor.AND); + } matcherEditors.add(stringCompositeMatcherEditor); } + + if (!thresholdMatcherEditors.isEmpty()) { + CompositeMatcherEditor<T> thresholdCompositeMatcherEditor = new CompositeMatcherEditor<>(thresholdMatcherEditors); + if (separator == null || filterText.contains(separator[0])) { + thresholdCompositeMatcherEditor.setMode(CompositeMatcherEditor.AND); + } else { + thresholdCompositeMatcherEditor.setMode(CompositeMatcherEditor.OR); + } + matcherEditors.add(thresholdCompositeMatcherEditor); + } } // wait until all listeners had the chance to handle the clear event @@ -257,6 +274,63 @@ public class DefaultGlazedListsFilterStrategy<T> implements IFilterStrategy<T> { } } + // TODO 2.1 move constants and getSeparatorCharacters() to FilterRowUtils + + private static final String TWO_CHARACTER_REGEX = "\\[(((.){2})|((.)\\\\(.))|(\\\\(.){2})|(\\\\(.)\\\\(.)))\\]"; //$NON-NLS-1$ + private static final String MASKED_BACKSLASH = "\\\\"; //$NON-NLS-1$ + private static final String BACKSLASH_REPLACEMENT = "backslash"; //$NON-NLS-1$ + + /** + * This method tries to extract the AND and the OR character that should be + * used as delimiter, so that a user is able to specify the operation for + * combined filter criteria. If it does not start with [ and ends with ] and + * does not match one of the following regular expressions, this method + * returns <code>null</code> which causes the default behavior, e.g. OR for + * String matchers, AND for threshold matchers. + * <ul> + * <li>(.){2}</li> + * <li>(.)\\\\(.)</li> + * <li>\\\\(.){2}</li> + * <li>\\\\(.)\\\\(.)</li> + * </ul> + * + * @param delimiter + * The delimiter that is configured via + * {@link FilterRowConfigAttributes#TEXT_DELIMITER}. Can be + * <code>null</code>. + * @return String array with the configured AND and the configured OR + * character, or <code>null</code> if the delimiter is not a two + * character regular expression. The first element in the array is + * the AND character, the second element is the OR character. + */ + private String[] getSeparatorCharacters(String delimiter) { + // start with [ and end with ] + // (.){2} => e.g. ab + // (.)\\\\(.) => a\b + // \\\\(.){2} => \ab + // \\\\(.)\\\\(.) => \a\b + + if (delimiter != null && delimiter.matches(TWO_CHARACTER_REGEX)) { + String inspect = delimiter.substring(1, delimiter.length() - 1); + + // special handling if the backslash is used as delimiter for AND or + // OR + inspect = inspect.replace(MASKED_BACKSLASH, BACKSLASH_REPLACEMENT); + + // now replace all backslashed + inspect = inspect.replaceAll(MASKED_BACKSLASH, ""); //$NON-NLS-1$ + + // convert back the "backslash" to "\" + inspect = inspect.replace(BACKSLASH_REPLACEMENT, "\\"); //$NON-NLS-1$ + if (inspect.length() == 2) { + String[] result = new String[] { inspect.substring(0, 1), inspect.substring(1, 2) }; + return result; + } + } + + return null; + } + /** * Retrieves the {@link IDisplayConverter} that should be used for * converting the body content to string for text match filter operations. @@ -447,7 +521,7 @@ public class DefaultGlazedListsFilterStrategy<T> implements IFilterStrategy<T> { } /** - * This allows to determinate if two matcher editors are equals. + * This allows to determinate if two matcher editors are equal. * * @param first * The first matcher editor to compare. @@ -471,20 +545,25 @@ public class DefaultGlazedListsFilterStrategy<T> implements IFilterStrategy<T> { CompositeMatcherEditor<T> firstComp = (CompositeMatcherEditor<T>) first; CompositeMatcherEditor<T> secondComp = (CompositeMatcherEditor<T>) second; - result = firstComp.getMatcherEditors().size() == secondComp.getMatcherEditors().size(); - - // Check that all sub matcher editors of first composite matcher - // editors are available in the sub matchers of second composite - // matcher editor - final Iterator<MatcherEditor<T>> matcherEditors = firstComp.getMatcherEditors().iterator(); - while (matcherEditors.hasNext() && result) { - MatcherEditor<T> e = matcherEditors.next(); - final Iterator<MatcherEditor<T>> iterator = secondComp.getMatcherEditors().iterator(); - boolean found = false; - while (iterator.hasNext() && !found) { - found = matcherEditorEqual(iterator.next(), e); + // check if both CompositeMatcherEditor have the same size and + // the same mode + result = (firstComp.getMatcherEditors().size() == secondComp.getMatcherEditors().size()) + && (firstComp.getMode() == secondComp.getMode()); + + if (result) { + // Check that all sub matcher editors of first composite + // matcher editors are available in the sub matchers of + // second composite matcher editor + final Iterator<MatcherEditor<T>> matcherEditors = firstComp.getMatcherEditors().iterator(); + while (matcherEditors.hasNext() && result) { + MatcherEditor<T> e = matcherEditors.next(); + final Iterator<MatcherEditor<T>> iterator = secondComp.getMatcherEditors().iterator(); + boolean found = false; + while (iterator.hasNext() && !found) { + found = matcherEditorEqual(iterator.next(), e); + } + result = found; } - result = found; } } else if (first instanceof TextMatcherEditor) { diff --git a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/FilterRowUtils.java b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/FilterRowUtils.java index 78cb84fe..7b9348eb 100644 --- a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/FilterRowUtils.java +++ b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/FilterRowUtils.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012, 2020 Original authors and others. + * Copyright (c) 2012, 2022 Original authors and others. * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -17,7 +17,6 @@ import static org.eclipse.nebula.widgets.nattable.util.ObjectUtils.isNotEmpty; import java.util.ArrayList; import java.util.List; import java.util.Scanner; -import java.util.StringTokenizer; import java.util.regex.Pattern; import org.eclipse.nebula.widgets.nattable.filterrow.ParseResult; @@ -39,10 +38,13 @@ public final class FilterRowUtils { List<ParseResult> parseResults = new ArrayList<>(); - if (textDelimiter != null) { - StringTokenizer tok = new StringTokenizer(string, textDelimiter); - while (tok.hasMoreTokens()) { - parse(tok.nextToken(), textMatchingMode, parseResults); + // avoid splitting if string is in brackets + // needed to be able to use "|" as OR delimiter but still use it in + // regular expressions, e.g. combobox filters with checkboxes + if (textDelimiter != null && !(string.startsWith("(") && string.endsWith(")"))) { //$NON-NLS-1$ //$NON-NLS-2$ + String[] splitted = string.split(textDelimiter); + for (String split : splitted) { + parse(split, textMatchingMode, parseResults); } } else { parse(string, textMatchingMode, parseResults); @@ -88,7 +90,7 @@ public final class FilterRowUtils { parseResult.setValueToMatch(scanner.next()); } } else { - parseResult.setValueToMatch(string); + parseResult.setValueToMatch(string.trim()); } scanner.close(); return parseResult; @@ -119,29 +121,22 @@ public final class FilterRowUtils { MatchType matchType) { switch (matchType) { case GREATER_THAN: - thresholdMatcherEditor - .setMatchOperation(ThresholdMatcherEditor.GREATER_THAN); + thresholdMatcherEditor.setMatchOperation(ThresholdMatcherEditor.GREATER_THAN); break; case GREATER_THAN_OR_EQUAL: - thresholdMatcherEditor - .setMatchOperation(ThresholdMatcherEditor.GREATER_THAN_OR_EQUAL); + thresholdMatcherEditor.setMatchOperation(ThresholdMatcherEditor.GREATER_THAN_OR_EQUAL); break; case LESS_THAN: - thresholdMatcherEditor - .setMatchOperation(ThresholdMatcherEditor.LESS_THAN); + thresholdMatcherEditor.setMatchOperation(ThresholdMatcherEditor.LESS_THAN); break; case LESS_THAN_OR_EQUAL: - thresholdMatcherEditor - .setMatchOperation(ThresholdMatcherEditor.LESS_THAN_OR_EQUAL); + thresholdMatcherEditor.setMatchOperation(ThresholdMatcherEditor.LESS_THAN_OR_EQUAL); break; case NOT_EQUAL: - thresholdMatcherEditor - .setMatchOperation(ThresholdMatcherEditor.NOT_EQUAL); + thresholdMatcherEditor.setMatchOperation(ThresholdMatcherEditor.NOT_EQUAL); break; default: - thresholdMatcherEditor - .setMatchOperation(ThresholdMatcherEditor.EQUAL); + thresholdMatcherEditor.setMatchOperation(ThresholdMatcherEditor.EQUAL); } } - -} +}
\ No newline at end of file |
