diff options
| author | Dirk Fauth | 2023-03-03 14:40:34 +0000 |
|---|---|---|
| committer | Dirk Fauth | 2023-03-03 14:40:34 +0000 |
| commit | 4986d10f43c694be7b7bb342e9cab9a24260a9fa (patch) | |
| tree | 1cc49a30cfbc67ef861f4eb5b1a75f127939297d | |
| parent | 767ece5de4a2b3c25dc31cf49ea3370118cdaf38 (diff) | |
| download | org.eclipse.nebula.widgets.nattable-4986d10f43c694be7b7bb342e9cab9a24260a9fa.tar.gz org.eclipse.nebula.widgets.nattable-4986d10f43c694be7b7bb342e9cab9a24260a9fa.tar.xz org.eclipse.nebula.widgets.nattable-4986d10f43c694be7b7bb342e9cab9a24260a9fa.zip | |
Bug 581620 - Reapply combobox filter states on different collections
Signed-off-by: Dirk Fauth <dirk.fauth@googlemail.com>
Change-Id: Iadc6059ca0037819055dbbdcfdcdee2bf07e4c31
3 files changed, 311 insertions, 19 deletions
diff --git a/org.eclipse.nebula.widgets.nattable.core/.settings/.api_filters b/org.eclipse.nebula.widgets.nattable.core/.settings/.api_filters index afddbc7d..5fb9e5e4 100644 --- a/org.eclipse.nebula.widgets.nattable.core/.settings/.api_filters +++ b/org.eclipse.nebula.widgets.nattable.core/.settings/.api_filters @@ -15,6 +15,18 @@ <message_argument value="COMMA_REPLACEMENT"/> </message_arguments> </filter> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.nebula.widgets.nattable.filterrow.FilterRowDataProvider"/> + <message_argument value="EMPTY_REPLACEMENT"/> + </message_arguments> + </filter> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.nebula.widgets.nattable.filterrow.FilterRowDataProvider"/> + <message_argument value="NULL_REPLACEMENT"/> + </message_arguments> + </filter> </resource> <resource path="src/org/eclipse/nebula/widgets/nattable/filterrow/combobox/ComboBoxFilterRowConfiguration.java" type="org.eclipse.nebula.widgets.nattable.filterrow.combobox.ComboBoxFilterRowConfiguration"> <filter id="336658481"> diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/filterrow/FilterRowDataProvider.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/filterrow/FilterRowDataProvider.java index 6eb83828..7493d31a 100644 --- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/filterrow/FilterRowDataProvider.java +++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/filterrow/FilterRowDataProvider.java @@ -13,9 +13,11 @@ package org.eclipse.nebula.widgets.nattable.filterrow; import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Properties; @@ -23,6 +25,8 @@ import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes; import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; import org.eclipse.nebula.widgets.nattable.data.IDataProvider; import org.eclipse.nebula.widgets.nattable.data.convert.IDisplayConverter; +import org.eclipse.nebula.widgets.nattable.edit.EditConstants; +import org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboBoxDataProvider; import org.eclipse.nebula.widgets.nattable.filterrow.event.FilterAppliedEvent; import org.eclipse.nebula.widgets.nattable.layer.ILayer; import org.eclipse.nebula.widgets.nattable.layer.cell.LayerCell; @@ -66,6 +70,26 @@ public class FilterRowDataProvider<T> implements IDataProvider, IPersistable { public static final String COMMA_REPLACEMENT = "°#°"; //$NON-NLS-1$ /** + * Replacement for the null value that is used for persisting collection + * values in case of combobox filters. Needed for the inverted persistence + * in case there are null values in the collection that need to be + * persisted. + * + * @since 2.1 + */ + public static final String NULL_REPLACEMENT = "°null°"; //$NON-NLS-1$ + + /** + * Replacement for an empty String value that is used for persisting + * collection values in case of combobox filters. Needed for the inverted + * persistence in case there are empty String values in the collection that + * need to be persisted. + * + * @since 2.1 + */ + public static final String EMPTY_REPLACEMENT = "°empty°"; //$NON-NLS-1$ + + /** * The prefix String that will be used to mark that the following filter * value in the persisted state is a collection. */ @@ -105,6 +129,29 @@ public class FilterRowDataProvider<T> implements IDataProvider, IPersistable { private Map<Integer, Object> filterIndexToObjectMap = new HashMap<>(); /** + * Flag to configure how filter collections are persisted. By default the + * values in the collection are persisted as is. In case of Excel like + * filters, it can be more feasible to store which values are NOT selected, + * to be able to load the filter even for different values in the filter + * list. + * + * @see #filterRowComboBoxDataProvider + * + * @since 2.1 + */ + private boolean invertCollectionPersistence = false; + + /** + * The FilterRowComboBoxDataProvider that is needed to be able to support + * inverted persistence of filter collections. + * + * @see FilterRowDataProvider#invertCollectionPersistence + * + * @since 2.1 + */ + private FilterRowComboBoxDataProvider<T> filterRowComboBoxDataProvider; + + /** * * @param filterStrategy * The {@link IFilterStrategy} to which the set filter value @@ -285,16 +332,43 @@ public class FilterRowDataProvider<T> implements IDataProvider, IPersistable { StringBuilder builder = new StringBuilder(collectionSpec); builder.append("["); //$NON-NLS-1$ Collection<?> filterCollection = (Collection<?>) filterValue; - for (Iterator<?> iterator = filterCollection.iterator(); iterator.hasNext();) { - Object filterObject = iterator.next(); - String displayValue = (String) converter.canonicalToDisplayValue( - new LayerCell(null, columnIndex, 0), - this.configRegistry, - filterObject); - displayValue = displayValue.replace(IPersistable.VALUE_SEPARATOR, COMMA_REPLACEMENT); - builder.append(displayValue); - if (iterator.hasNext()) { - builder.append(IPersistable.VALUE_SEPARATOR); + + if (!this.invertCollectionPersistence) { + for (Iterator<?> iterator = filterCollection.iterator(); iterator.hasNext();) { + Object filterObject = iterator.next(); + String displayValue = (String) converter.canonicalToDisplayValue( + new LayerCell(null, columnIndex, 0), + this.configRegistry, + filterObject); + displayValue = displayValue.replace(IPersistable.VALUE_SEPARATOR, COMMA_REPLACEMENT); + builder.append(displayValue); + if (iterator.hasNext()) { + builder.append(IPersistable.VALUE_SEPARATOR); + } + } + } else { + List<?> allValues = new ArrayList<>(this.filterRowComboBoxDataProvider.getAllValues(columnIndex)); + allValues.removeAll(filterCollection); + + for (Iterator<?> iterator = allValues.iterator(); iterator.hasNext();) { + Object filterObject = iterator.next(); + if (filterObject == null) { + builder.append(NULL_REPLACEMENT); + } else { + String displayValue = (String) converter.canonicalToDisplayValue( + new LayerCell(null, columnIndex, 0), + this.configRegistry, + filterObject); + displayValue = displayValue.replace(IPersistable.VALUE_SEPARATOR, COMMA_REPLACEMENT); + if (displayValue.isEmpty()) { + builder.append(EMPTY_REPLACEMENT); + } else { + builder.append(displayValue); + } + } + if (iterator.hasNext()) { + builder.append(IPersistable.VALUE_SEPARATOR); + } } } @@ -345,13 +419,31 @@ public class FilterRowDataProvider<T> implements IDataProvider, IPersistable { // also get rid of the collection marks filterText = filterText.substring(indexEndCollSpec + 2, filterText.length() - 1); - String[] filterSplit = filterText.split(IPersistable.VALUE_SEPARATOR); - for (String filterString : filterSplit) { - filterString = filterString.replace(COMMA_REPLACEMENT, IPersistable.VALUE_SEPARATOR); - filterCollection.add(converter.displayToCanonicalValue( - new LayerCell(null, columnIndex, 0), - this.configRegistry, - filterString)); + if (!filterText.isEmpty()) { + String[] filterSplit = filterText.split(IPersistable.VALUE_SEPARATOR); + for (String filterString : filterSplit) { + filterString = filterString.replace(COMMA_REPLACEMENT, IPersistable.VALUE_SEPARATOR); + if (NULL_REPLACEMENT.equals(filterString)) { + filterCollection.add(null); + } else if (EMPTY_REPLACEMENT.equals(filterString)) { + filterCollection.add(""); //$NON-NLS-1$ + } else { + filterCollection.add(converter.displayToCanonicalValue( + new LayerCell(null, columnIndex, 0), + this.configRegistry, + filterString)); + } + } + } + + if (this.invertCollectionPersistence) { + if (filterCollection.isEmpty()) { + return EditConstants.SELECT_ALL_ITEMS_VALUE; + } + + List<?> allValues = new ArrayList<>(this.filterRowComboBoxDataProvider.getAllValues(columnIndex)); + allValues.removeAll(filterCollection); + return allValues; } return filterCollection; @@ -383,4 +475,35 @@ public class FilterRowDataProvider<T> implements IDataProvider, IPersistable { return this.filterStrategy; } + /** + * + * @return <code>true</code> if filter collections are persisted in an + * inverted way, which means the values that are <b>NOT</b> selected + * in the combo are persisted. By default this configuration is set + * to <code>false</code> which means values in the collection are + * persisted as is. + * + * @since 2.1 + */ + public boolean isInvertCollectionPersistence() { + return this.invertCollectionPersistence; + } + + /** + * + * @param invertCollectionPersistence + * <code>true</code> if filter collections should be persisted in + * an inverted way, which means the values that are <b>NOT</b> + * selected in the combo are persisted. + * + * @since 2.1 + */ + public void setInvertCollectionPersistence(boolean invertCollectionPersistence, FilterRowComboBoxDataProvider<T> comboBoxDataProvider) { + if (invertCollectionPersistence && comboBoxDataProvider == null) { + throw new IllegalArgumentException("Can only invert the collection persistence if the FilterRowComboBoxDataProvider is provided"); //$NON-NLS-1$ + } + this.invertCollectionPersistence = invertCollectionPersistence; + this.filterRowComboBoxDataProvider = comboBoxDataProvider; + } + } diff --git a/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/FilterRowDataProviderTest.java b/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/FilterRowDataProviderTest.java index bc82a8d1..0dcc01d8 100644 --- a/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/FilterRowDataProviderTest.java +++ b/org.eclipse.nebula.widgets.nattable.extension.glazedlists.test/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/filterrow/FilterRowDataProviderTest.java @@ -25,25 +25,30 @@ import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes; import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry; import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration; import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; +import org.eclipse.nebula.widgets.nattable.data.ListDataProvider; import org.eclipse.nebula.widgets.nattable.data.ReflectiveColumnPropertyAccessor; import org.eclipse.nebula.widgets.nattable.data.convert.ContextualDisplayConverter; 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.edit.EditConstants; import org.eclipse.nebula.widgets.nattable.extension.glazedlists.fixture.DataLayerFixture; import org.eclipse.nebula.widgets.nattable.extension.glazedlists.fixture.LayerListenerFixture; 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.combobox.FilterRowComboBoxDataProvider; import org.eclipse.nebula.widgets.nattable.filterrow.config.DefaultFilterRowConfiguration; import org.eclipse.nebula.widgets.nattable.filterrow.config.FilterRowConfigAttributes; import org.eclipse.nebula.widgets.nattable.filterrow.event.FilterAppliedEvent; +import org.eclipse.nebula.widgets.nattable.layer.DataLayer; import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; import org.eclipse.nebula.widgets.nattable.persistence.IPersistable; import org.eclipse.nebula.widgets.nattable.style.DisplayMode; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.FilterList; import ca.odell.glazedlists.GlazedLists; @@ -51,9 +56,13 @@ public class FilterRowDataProviderTest { private FilterRowDataProvider<RowDataFixture> dataProvider; private DataLayerFixture columnHeaderLayer; + private EventList<RowDataFixture> baseList; private FilterList<RowDataFixture> filterList; private ConfigRegistry configRegistry; + private ReflectiveColumnPropertyAccessor<RowDataFixture> columnAccessor = + new ReflectiveColumnPropertyAccessor<>(RowDataListFixture.getPropertyNames()); + @BeforeEach public void setup() { this.columnHeaderLayer = new DataLayerFixture(10, 2, 100, 50); @@ -62,12 +71,13 @@ public class FilterRowDataProviderTest { new DefaultNatTableStyleConfiguration().configureRegistry(this.configRegistry); new DefaultFilterRowConfiguration().configureRegistry(this.configRegistry); - this.filterList = new FilterList<>(GlazedLists.eventList(RowDataListFixture.getList())); + this.baseList = GlazedLists.eventList(RowDataListFixture.getList()); + this.filterList = new FilterList<>(this.baseList); this.dataProvider = new FilterRowDataProvider<>( new DefaultGlazedListsFilterStrategy<>( this.filterList, - new ReflectiveColumnPropertyAccessor<RowDataFixture>(RowDataListFixture.getPropertyNames()), + this.columnAccessor, this.configRegistry), this.columnHeaderLayer, this.columnHeaderLayer.getDataProvider(), @@ -412,4 +422,151 @@ public class FilterRowDataProviderTest { assertEquals("foo", this.dataProvider.getDataValue(1, 1)); assertEquals("testValue", this.dataProvider.getDataValue(2, 1)); } + + @Test + public void shouldInvertFilterCollectionPersistence() { + // enable inverted combobox filter persistence + FilterRowComboBoxDataProvider<RowDataFixture> cbdp = + new FilterRowComboBoxDataProvider<>( + new DataLayer(new ListDataProvider<>(this.filterList, this.columnAccessor)), + this.baseList, + this.columnAccessor); + this.dataProvider.setInvertCollectionPersistence(true, cbdp); + + // set filter to filter out AAA and aaa + this.dataProvider.setDataValue(2, 1, new ArrayList<>(Arrays.asList("A-", "AA", "B", "B-", "BB", "C", "a", "aa"))); + + // save state + Properties properties = new Properties(); + this.dataProvider.saveState("prefix", properties); + String persistedProperty = properties.getProperty("prefix" + FilterRowDataLayer.PERSISTENCE_KEY_FILTER_ROW_TOKENS); + + String expectedPersistedCollection = "2:" + FilterRowDataProvider.FILTER_COLLECTION_PREFIX + ArrayList.class.getName() + ")[" + + "AAA" + IPersistable.VALUE_SEPARATOR + + "aaa" + + "]"; + + assertEquals(expectedPersistedCollection + "|", persistedProperty); + + // reset state + this.dataProvider.clearAllFilters(); + + assertNull(this.dataProvider.getDataValue(2, 1)); + + // load state + this.dataProvider.loadState("prefix", properties); + + assertEquals(new ArrayList<>(Arrays.asList("A-", "AA", "B", "B-", "BB", "C", "a", "aa")), this.dataProvider.getDataValue(2, 1)); + } + + @Test + public void shouldInvertAllSelectedFilterCollectionPersistence() { + // enable inverted combobox filter persistence + FilterRowComboBoxDataProvider<RowDataFixture> cbdp = + new FilterRowComboBoxDataProvider<>( + new DataLayer(new ListDataProvider<>(this.filterList, this.columnAccessor)), + this.baseList, + this.columnAccessor); + this.dataProvider.setInvertCollectionPersistence(true, cbdp); + + // set filter to select all, which means nothing is filtered + this.dataProvider.setDataValue(2, 1, new ArrayList<>(Arrays.asList("A-", "AA", "AAA", "B", "B-", "BB", "C", "a", "aa", "aaa"))); + + // save state + Properties properties = new Properties(); + this.dataProvider.saveState("prefix", properties); + String persistedProperty = properties.getProperty("prefix" + FilterRowDataLayer.PERSISTENCE_KEY_FILTER_ROW_TOKENS); + + String expectedPersistedCollection = "2:" + FilterRowDataProvider.FILTER_COLLECTION_PREFIX + ArrayList.class.getName() + ")[]"; + + assertEquals(expectedPersistedCollection + "|", persistedProperty); + + // reset state + this.dataProvider.clearAllFilters(); + + assertNull(this.dataProvider.getDataValue(2, 1)); + + // load state + this.dataProvider.loadState("prefix", properties); + + assertEquals(EditConstants.SELECT_ALL_ITEMS_VALUE, this.dataProvider.getDataValue(2, 1)); + } + + @Test + public void shouldInvertFilterCollectionNullValuePersistence() { + // enable inverted combobox filter persistence + FilterRowComboBoxDataProvider<RowDataFixture> cbdp = + new FilterRowComboBoxDataProvider<>( + new DataLayer(new ListDataProvider<>(this.filterList, this.columnAccessor)), + this.baseList, + this.columnAccessor); + this.dataProvider.setInvertCollectionPersistence(true, cbdp); + + this.filterList.get(0).setRating(null); + + assertEquals(Arrays.asList(null, "A-", "AA", "AAA", "B", "B-", "BB", "C", "aa", "aaa"), cbdp.getAllValues(2)); + + // set filter to filter out null values + this.dataProvider.setDataValue(2, 1, new ArrayList<>(Arrays.asList("A-", "AA", "AAA", "B", "B-", "BB", "C", "aa", "aaa"))); + + // save state + Properties properties = new Properties(); + this.dataProvider.saveState("prefix", properties); + String persistedProperty = properties.getProperty("prefix" + FilterRowDataLayer.PERSISTENCE_KEY_FILTER_ROW_TOKENS); + + String expectedPersistedCollection = "2:" + FilterRowDataProvider.FILTER_COLLECTION_PREFIX + ArrayList.class.getName() + ")[" + + FilterRowDataProvider.NULL_REPLACEMENT + + "]"; + + assertEquals(expectedPersistedCollection + "|", persistedProperty); + + // reset state + this.dataProvider.clearAllFilters(); + + assertNull(this.dataProvider.getDataValue(2, 1)); + + // load state + this.dataProvider.loadState("prefix", properties); + + assertEquals(new ArrayList<>(Arrays.asList("A-", "AA", "AAA", "B", "B-", "BB", "C", "aa", "aaa")), this.dataProvider.getDataValue(2, 1)); + } + + @Test + public void shouldInvertFilterCollectionEmptyValuePersistence() { + // enable inverted combobox filter persistence + FilterRowComboBoxDataProvider<RowDataFixture> cbdp = + new FilterRowComboBoxDataProvider<>( + new DataLayer(new ListDataProvider<>(this.filterList, this.columnAccessor)), + this.baseList, + this.columnAccessor); + this.dataProvider.setInvertCollectionPersistence(true, cbdp); + + this.filterList.get(0).setRating(""); + + assertEquals(Arrays.asList("", "A-", "AA", "AAA", "B", "B-", "BB", "C", "aa", "aaa"), cbdp.getAllValues(2)); + + // set filter to filter out null values + this.dataProvider.setDataValue(2, 1, new ArrayList<>(Arrays.asList("A-", "AA", "AAA", "B", "B-", "BB", "C", "aa", "aaa"))); + + // save state + Properties properties = new Properties(); + this.dataProvider.saveState("prefix", properties); + String persistedProperty = properties.getProperty("prefix" + FilterRowDataLayer.PERSISTENCE_KEY_FILTER_ROW_TOKENS); + + String expectedPersistedCollection = "2:" + FilterRowDataProvider.FILTER_COLLECTION_PREFIX + ArrayList.class.getName() + ")[" + + FilterRowDataProvider.EMPTY_REPLACEMENT + + "]"; + + assertEquals(expectedPersistedCollection + "|", persistedProperty); + + // reset state + this.dataProvider.clearAllFilters(); + + assertNull(this.dataProvider.getDataValue(2, 1)); + + // load state + this.dataProvider.loadState("prefix", properties); + + assertEquals(new ArrayList<>(Arrays.asList("A-", "AA", "AAA", "B", "B-", "BB", "C", "aa", "aaa")), this.dataProvider.getDataValue(2, 1)); + } }
\ No newline at end of file |
