From 2b1f0353a5d26656aae748fc905baa3e07a472a0 Mon Sep 17 00:00:00 2001 From: Steffen Schaefer Date: Mon, 24 Oct 2016 12:06:38 +0200 Subject: [PATCH] Refactored AbstractFilterablePanel and CustomizableFilterablePanel to consistently use com.sap.sse.common.filter.Filter for the keyword filter and the extendible set of filters in CustomizableFilterablePanel --- .../AbstractTrackedRacesListComposite.java | 6 +- ...egattaLogTrackingDeviceMappingsDialog.java | 21 ++-- .../panels/AbstractFilterablePanel.java | 114 +++++++++++------- .../panels/CustomizableFilterablePanel.java | 44 ++++--- 4 files changed, 106 insertions(+), 79 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractTrackedRacesListComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractTrackedRacesListComposite.java index 320c5630568..06eacf8ad37 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractTrackedRacesListComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractTrackedRacesListComposite.java @@ -46,6 +46,7 @@ import com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn; import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; import com.sap.sailing.gwt.ui.shared.RegattaDTO; import com.sap.sse.common.Util; +import com.sap.sse.common.filter.Filter; import com.sap.sse.common.util.NaturalComparator; import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.async.MarkedAsyncCallback; @@ -53,7 +54,6 @@ import com.sap.sse.gwt.client.celltable.EntityIdentityComparator; import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel; import com.sap.sse.gwt.client.celltable.RefreshableSingleSelectionModel; import com.sap.sse.gwt.client.panels.CustomizableFilterablePanel; -import com.sap.sse.gwt.client.panels.CustomizableFilterablePanel.Filter; import com.sap.sse.gwt.client.shared.components.Component; import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; @@ -172,6 +172,10 @@ public abstract class AbstractTrackedRacesListComposite extends SimplePanel public boolean matches(RaceDTO t) { return listBoxRegattas.getSelectedIndex() == 0 /* All */ || Util.equalsWithNull(listBoxRegattas.getSelectedValue(), t.getRegattaName()); } + @Override + public String getName() { + return "TrackedRacesByRegattaFilter"; + } }); Label lblFilterRacesByName = new Label(stringMessages.filterByNameOrBoatClass() + ":"); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogTrackingDeviceMappingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogTrackingDeviceMappingsDialog.java index 8c18dcc2079..180ba4e631b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogTrackingDeviceMappingsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogTrackingDeviceMappingsDialog.java @@ -41,7 +41,7 @@ import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; import com.sap.sailing.gwt.ui.shared.DeviceMappingDTO; import com.sap.sailing.gwt.ui.shared.MarkDTO; import com.sap.sailing.gwt.ui.shared.TypedDeviceMappingDTO; -import com.sap.sse.common.filter.AbstractListFilter; +import com.sap.sse.common.filter.AbstractKeywordFilter; import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.dialog.DataEntryDialog; import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel; @@ -181,24 +181,17 @@ public class RegattaLogTrackingDeviceMappingsDialog extends DataEntryDialog getFilterer() { - final AbstractListFilter baseFilterer = super.getFilterer(); - return new AbstractListFilter() { + protected AbstractKeywordFilter getFilterer() { + final AbstractKeywordFilter baseFilterer = super.getFilterer(); + return new AbstractKeywordFilter() { @Override public Iterable getStrings(DeviceMappingDTO t) { return baseFilterer.getStrings(t); } - + @Override - public Iterable applyFilter(Iterable keywords, - Iterable all) { - final List result = new ArrayList<>(); - for (final DeviceMappingDTO dm : super.applyFilter(keywords, all)) { - if (showPingMappingsCb.getValue() || !"PING".equals(dm.deviceIdentifier.deviceType)) { - result.add(dm); - } - } - return result; + public boolean matches(DeviceMappingDTO dm) { + return super.matches(dm) && showPingMappingsCb.getValue() || !"PING".equals(dm.deviceIdentifier.deviceType); } }; } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/AbstractFilterablePanel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/AbstractFilterablePanel.java index 712f33e4a7d..0170f0faaa6 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/AbstractFilterablePanel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/AbstractFilterablePanel.java @@ -1,6 +1,8 @@ package com.sap.sse.gwt.client.panels; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; @@ -12,25 +14,28 @@ import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.view.client.ListDataProvider; import com.sap.sse.common.Util; +import com.sap.sse.common.filter.AbstractKeywordFilter; import com.sap.sse.common.filter.AbstractListFilter; +import com.sap.sse.common.filter.Filter; import com.sap.sse.gwt.client.celltable.EntityIdentityComparator; import com.sap.sse.gwt.client.celltable.RefreshableMultiSelectionModel; import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel; import com.sap.sse.gwt.client.celltable.RefreshableSingleSelectionModel; /** - * This Panel contains a text box. Text entered into the text box filters the {@link CellTable} passed to - * the constructor by adjusting the cell table's {@link ListDataProvider}'s contents using the {@link - * applyFilter(String, List)} of the {@link AbstractListFilter} and then sorting the table again according the the - * sorting criteria currently active (the sorting is the only reason why the {@link CellTable} actually needs to be - * known to an instance of this class). To be initiated the method {@link #getSearchableStrings(Object)} has to be - * defined, which gets those Strings from a T that should be considered when filtering, e.g. name or - * boatClass. The cell table can be sorted independently from the text box (e.g., after adding new objects) by calling - * the method {@link #updateAll(Iterable)} which then runs the filter over the new selection.

+ * This Panel contains a text box. Text entered into the text box filters the {@link CellTable} passed to the + * constructor by adjusting the cell table's {@link ListDataProvider}'s contents using the {@link applyFilter(String, + * List)} of the {@link AbstractListFilter} and then sorting the table again according the the sorting criteria + * currently active (the sorting is the only reason why the {@link CellTable} actually needs to be known to an instance + * of this class). To be initiated the method {@link #getSearchableStrings(Object)} has to be defined, which gets those + * Strings from a T that should be considered when filtering, e.g. name or boatClass. The cell table can be + * sorted independently from the text box (e.g., after adding new objects) by calling the method + * {@link #updateAll(Iterable)} which then runs the filter over the new selection. + *

* - * Note that this panel does not contain the table that it filters. With this, this class's clients are free - * to position the table wherever they want, not necessarily related to the text box provided by this panel in any - * specific way. + * Note that this panel does not contain the table that it filters. With this, this class's clients are free to + * position the table wherever they want, not necessarily related to the text box provided by this panel in any specific + * way. * * @param * @author Nicolas Klose, Axel Uhl @@ -41,27 +46,28 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { protected AbstractCellTable display; protected final ListDataProvider filtered; protected final TextBox textBox; - - protected final AbstractListFilter filterer = new AbstractListFilter() { + + protected final AbstractKeywordFilter filterer = new AbstractKeywordFilter() { @Override public Iterable getStrings(T t) { return getSearchableStrings(t); } }; - + /** * @param all * the sequence of all objects that may be displayed in the table and from which the filter may choose. * This panel keeps a copy, so modifications to the all object do not reflect in the table * contents. Use {@link #updateAll(Iterable)} instead to update the sequence of available objects. * @param drawTextBox - * if {@code true}, the default text box will be shown that can be used to provide the filter text; if {@code false}, - * the box may optionally be added later using the {@link #addDefaultTextBox()} method. This way, subclasses may choose - * to add other filter elements before the default text filter box. Filtering will still use the text box contents, - * even if the box is ultimately not shown; but under normal circumstances the text box will be empty in this case, - * not making filtering any stricter. + * if {@code true}, the default text box will be shown that can be used to provide the filter text; if + * {@code false}, the box may optionally be added later using the {@link #addDefaultTextBox()} method. + * This way, subclasses may choose to add other filter elements before the default text filter box. + * Filtering will still use the text box contents, even if the box is ultimately not shown; but under + * normal circumstances the text box will be empty in this case, not making filtering any stricter. */ - public AbstractFilterablePanel(Iterable all, AbstractCellTable display, final ListDataProvider filtered, boolean drawTextBox) { + public AbstractFilterablePanel(Iterable all, AbstractCellTable display, final ListDataProvider filtered, + boolean drawTextBox) { setSpacing(5); this.all = new ListDataProvider<>(); this.display = display; @@ -74,11 +80,11 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { addDefaultTextBox(); } } - + public AbstractFilterablePanel(Iterable all, AbstractCellTable display, final ListDataProvider filtered) { this(all, display, filtered, /* show default filter text box */ true); } - + private void setAll(Iterable all) { this.all.getList().clear(); if (all != null) { @@ -99,14 +105,13 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { public abstract Iterable getSearchableStrings(T t); /** - * Updates the set of all objects to be shown in the table and applies the search filter to update the - * table view. + * Updates the set of all objects to be shown in the table and applies the search filter to update the table view. */ - public void updateAll(Iterable all){ + public void updateAll(Iterable all) { setAll(all); filter(); } - + /** * Adds an object and applies the search filter. */ @@ -122,14 +127,15 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { all.getList().add(index, object); filter(); } - + /** - * Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element. + * Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not + * contain the element. */ public int indexOf(T object) { return all.getList().indexOf(object); } - + /** * Removes an object and applies the search filter. */ @@ -137,12 +143,12 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { all.getList().remove(object); filter(); } - + public void addAll(Iterable objects) { Util.addAll(objects, all.getList()); filter(); } - + /** * Would better be called {@code clear()}, but {@link #clear} is already the method inherited from {@link Panel}... * This method removes all entries from this filterable panel. The effect is the same as invoking @@ -152,12 +158,12 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { all.getList().clear(); filter(); } - + public void removeAll(Iterable objects) { Util.removeAll(objects, all.getList()); filter(); } - + public void filter() { filtered.getList().clear(); retainElementsInFilteredThatPassFilter(); @@ -166,21 +172,27 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { } protected void retainElementsInFilteredThatPassFilter() { - Util.addAll(getFilterer().applyFilter(Arrays.asList(getTextBox().getText().split(" ")), all.getList()), - filtered.getList()); + List filteredElements = new ArrayList<>(); + for (T t : all.getList()) { + if (getFilter().matches(t)) { + filteredElements.add(t); + } + } + Util.addAll(filteredElements, filtered.getList()); } - + protected void sort() { if (display != null) { ColumnSortEvent.fire(display, display.getColumnSortList()); } } - + public void addDefaultTextBox() { add(getTextBox()); getTextBox().addKeyUpHandler(new KeyUpHandler() { @Override public void onKeyUp(KeyUpEvent event) { + getFilterer().setKeywords(Arrays.asList(getTextBox().getText().split(" "))); filter(); } }); @@ -189,15 +201,15 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { public TextBox getTextBox() { return textBox; } - + public Iterable getAll() { return all.getList(); } - + /** * Registers a {@link RefreshableSelectionModel} on the all data structure. So the selection can be maintained when - * the {@link CellTable} is filtered. You can use the {@link RefreshableSelectionModel} returned by this method - * e.g. for a {@link CellTable} using {@link CellTable#setSelectionModel(com.google.gwt.view.client.SelectionModel)} + * the {@link CellTable} is filtered. You can use the {@link RefreshableSelectionModel} returned by this method e.g. + * for a {@link CellTable} using {@link CellTable#setSelectionModel(com.google.gwt.view.client.SelectionModel)} * * @param comp * {@link EntityIdentityComparator Comperator} to create the {@link RefreshableSelectionModel selection @@ -226,19 +238,33 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { public ListDataProvider getAllListDataProvider() { return all; } - + /** * This method can be used to set the CellTable, when the {@link CellTable} on which this Panel should work is * created after this Panel. When the table isn't set correctly the order of elements, could be wrong after * filtering the data. * - * @param table {@link AbstractCellTable} on which this panel works. + * @param table + * {@link AbstractCellTable} on which this panel works. */ public void setTable(AbstractCellTable table) { display = table; } - protected AbstractListFilter getFilterer() { + /** + * @return the AbstractKeywordFilter to use for keyword-based filtering + */ + protected AbstractKeywordFilter getFilterer() { return filterer; } + + /** + * Returns the {@link Filter} to be used for filtering the result list. By default, this is just the keyword filter + * returned by {@link #getFilterer()}. + * + * May be overwritten by subclasses to e.g. define a composite filter or completely exchange the filter. + */ + protected Filter getFilter() { + return getFilterer(); + } } \ No newline at end of file diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/CustomizableFilterablePanel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/CustomizableFilterablePanel.java index 75e60de38a2..29acd10c6e6 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/CustomizableFilterablePanel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/CustomizableFilterablePanel.java @@ -1,7 +1,6 @@ package com.sap.sse.gwt.client.panels; import java.util.HashSet; -import java.util.Iterator; import java.util.Set; import com.google.gwt.user.cellview.client.AbstractCellTable; @@ -9,7 +8,7 @@ import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.view.client.ListDataProvider; -import com.sap.sse.common.Util; +import com.sap.sse.common.filter.Filter; /** * Customizable filter panel which allows to create a flexible filter panel with various number of filter inputs (e.g. @@ -20,11 +19,10 @@ import com.sap.sse.common.Util; * */ public abstract class CustomizableFilterablePanel extends AbstractFilterablePanel { - public static interface Filter { - boolean matches(T t); - } - private Set> filters; + private final Set> filters; + + private final Filter compositeFilter = new CompositeFilter(); public CustomizableFilterablePanel(Iterable all, AbstractCellTable display, ListDataProvider filtered) { super(all, display, filtered, /* show default filter text box */ false); @@ -39,21 +37,27 @@ public abstract class CustomizableFilterablePanel extends AbstractFilterableP } @Override - protected void retainElementsInFilteredThatPassFilter() { - super.retainElementsInFilteredThatPassFilter(); - for (final Iterator i = filtered.getList().iterator(); i.hasNext(); ) { - final T t = i.next(); - for (final Filter filter : filters) { - if (!filter.matches(t)) { - i.remove(); - break; - } - } - } + protected Filter getFilter() { + return compositeFilter; } - public void clearFilter() { - filtered.getList().clear(); - Util.addAll(all.getList(), filtered.getList()); + public boolean matches(T t) { + boolean matches = super.getFilter().matches(t); + for (final Filter filter : filters) { + matches &= filter.matches(t); + } + return matches; + } + + private class CompositeFilter implements Filter { + @Override + public boolean matches(T object) { + return CustomizableFilterablePanel.this.matches(object); + } + + @Override + public String getName() { + return getClass().getSimpleName(); + } } }