blob: 47f5f7243f0635f4a376aab232ecd63a8ff594ee [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019, 2020 Dirk Fauth.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.nebula.widgets.nattable.extension.glazedlists.filterrow;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.eclipse.nebula.widgets.nattable.command.DisposeResourcesCommand;
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
import org.eclipse.nebula.widgets.nattable.data.ExtendedReflectiveColumnPropertyAccessor;
import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
import org.eclipse.nebula.widgets.nattable.dataset.person.Address;
import org.eclipse.nebula.widgets.nattable.dataset.person.ExtendedPersonWithAddress;
import org.eclipse.nebula.widgets.nattable.dataset.person.Person;
import org.eclipse.nebula.widgets.nattable.dataset.person.Person.Gender;
import org.eclipse.nebula.widgets.nattable.dataset.person.PersonService;
import org.eclipse.nebula.widgets.nattable.edit.command.UpdateDataCommand;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.fixture.NatTableFixture;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.GroupByConfigLabelModifier;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.GroupByDataLayer;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.GroupByHeaderLayer;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.GroupByModel;
import org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboUpdateEvent;
import org.eclipse.nebula.widgets.nattable.filterrow.combobox.IFilterRowComboUpdateListener;
import org.eclipse.nebula.widgets.nattable.filterrow.command.ClearAllFiltersCommand;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultColumnHeaderDataLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer;
import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform;
import org.eclipse.nebula.widgets.nattable.layer.CompositeLayer;
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnLabelAccumulator;
import org.eclipse.nebula.widgets.nattable.reorder.ColumnReorderLayer;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
import org.eclipse.nebula.widgets.nattable.sort.SortDirectionEnum;
import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer;
import org.eclipse.nebula.widgets.nattable.sort.config.DefaultSortConfiguration;
import org.eclipse.nebula.widgets.nattable.tree.TreeLayer;
import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.FilterList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.TransformedList;
/**
* This test class is intended to verify the integration of the
* {@link ComboBoxFilterRowHeaderComposite}. Especially the updates of the
* filter combobox contents in case of structural changes. As those updates are
* done in background threads to not blocking the UI, the tests are complicated.
* To be sure that everything works as intended, the cases can be replayed with
* a UI via the _813_SortableGroupByWithComboBoxFilterExample.
*/
public class ComboBoxFilterRowHeaderCompositeIntegrationTest {
private static BodyLayerStack<ExtendedPersonWithAddress> bodyLayer;
private static ComboBoxFilterRowHeaderComposite<ExtendedPersonWithAddress> filterRowHeaderLayer;
private static NatTableFixture natTable;
private static GlazedListsSortModel<ExtendedPersonWithAddress> sortModel;
@BeforeClass
public static void setupClass() {
// create a new ConfigRegistry which will be needed for GlazedLists
// handling
ConfigRegistry configRegistry = new ConfigRegistry();
// property names of the ExtendedPersonWithAddress class
String[] propertyNames = { "firstName", "lastName", "gender",
"married", "address.street", "address.housenumber",
"address.postalCode", "address.city", "age", "birthday",
"money", "description", "favouriteFood", "favouriteDrinks" };
// mapping from property to label, needed for column header labels
Map<String, String> propertyToLabelMap = new HashMap<>();
propertyToLabelMap.put("firstName", "Firstname");
propertyToLabelMap.put("lastName", "Lastname");
propertyToLabelMap.put("gender", "Gender");
propertyToLabelMap.put("married", "Married");
propertyToLabelMap.put("address.street", "Street");
propertyToLabelMap.put("address.housenumber", "Housenumber");
propertyToLabelMap.put("address.postalCode", "Postalcode");
propertyToLabelMap.put("address.city", "City");
propertyToLabelMap.put("age", "Age");
propertyToLabelMap.put("birthday", "Birthday");
propertyToLabelMap.put("money", "Money");
propertyToLabelMap.put("description", "Description");
propertyToLabelMap.put("favouriteFood", "Food");
propertyToLabelMap.put("favouriteDrinks", "Drinks");
final IColumnPropertyAccessor<ExtendedPersonWithAddress> columnPropertyAccessor =
new ExtendedReflectiveColumnPropertyAccessor<>(propertyNames);
List<ExtendedPersonWithAddress> values = new ArrayList<>();
for (int i = 0; i < 300; i++) {
values.addAll(createValues(i * 30));
}
// to enable the group by summary feature, the GroupByDataLayer needs to
// know the ConfigRegistry
bodyLayer =
new BodyLayerStack<>(
values,
columnPropertyAccessor,
configRegistry);
bodyLayer.getBodyDataLayer().setConfigLabelAccumulator(new ColumnLabelAccumulator());
// build the column header layer
IDataProvider columnHeaderDataProvider =
new DefaultColumnHeaderDataProvider(propertyNames, propertyToLabelMap);
DataLayer columnHeaderDataLayer =
new DefaultColumnHeaderDataLayer(columnHeaderDataProvider);
ILayer columnHeaderLayer =
new ColumnHeaderLayer(columnHeaderDataLayer, bodyLayer, bodyLayer.getSelectionLayer());
// add sorting
sortModel = new GlazedListsSortModel<>(
bodyLayer.getSortedList(),
columnPropertyAccessor,
configRegistry,
columnHeaderDataLayer);
SortHeaderLayer<ExtendedPersonWithAddress> sortHeaderLayer = new SortHeaderLayer<>(
columnHeaderLayer,
sortModel,
false);
// connect sortModel to GroupByDataLayer to support sorting by group by
// summary values
bodyLayer.getBodyDataLayer().initializeTreeComparator(
sortHeaderLayer.getSortModel(),
bodyLayer.getTreeLayer(),
true);
filterRowHeaderLayer =
new ComboBoxFilterRowHeaderComposite<>(
bodyLayer.getFilterList(),
bodyLayer.getGlazedListsEventLayer(),
bodyLayer.getSortedList(),
columnPropertyAccessor,
sortHeaderLayer,
columnHeaderDataProvider,
configRegistry);
// build the row header layer
IDataProvider rowHeaderDataProvider =
new DefaultRowHeaderDataProvider(bodyLayer.getBodyDataProvider());
DataLayer rowHeaderDataLayer =
new DefaultRowHeaderDataLayer(rowHeaderDataProvider);
ILayer rowHeaderLayer =
new RowHeaderLayer(rowHeaderDataLayer, bodyLayer, bodyLayer.getSelectionLayer());
// build the corner layer
IDataProvider cornerDataProvider =
new DefaultCornerDataProvider(columnHeaderDataProvider, rowHeaderDataProvider);
DataLayer cornerDataLayer =
new DataLayer(cornerDataProvider);
ILayer cornerLayer =
new CornerLayer(cornerDataLayer, rowHeaderLayer, filterRowHeaderLayer);
// build the grid layer
GridLayer gridLayer = new GridLayer(bodyLayer, filterRowHeaderLayer, rowHeaderLayer, cornerLayer);
// set the group by header on top of the grid
CompositeLayer compositeGridLayer = new CompositeLayer(1, 2);
final GroupByHeaderLayer groupByHeaderLayer =
new GroupByHeaderLayer(bodyLayer.getGroupByModel(), gridLayer, columnHeaderDataProvider);
compositeGridLayer.setChildLayer(GroupByHeaderLayer.GROUP_BY_REGION, groupByHeaderLayer, 0, 0);
compositeGridLayer.setChildLayer("Grid", gridLayer, 0, 1);
// turn the auto configuration off as we want to add our header menu
// configuration
natTable = new NatTableFixture(compositeGridLayer, false);
natTable.setConfigRegistry(configRegistry);
natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
natTable.addConfiguration(new DefaultSortConfiguration());
natTable.configure();
}
@Before
public void setup() {
// test that nothing is filtered
assertEquals(9000, bodyLayer.getFilterList().size());
}
@After
public void tearDown() {
natTable.doCommand(new ClearAllFiltersCommand());
}
@AfterClass
public static void tearDownClass() {
natTable.doCommand(new DisposeResourcesCommand());
}
@Test
public void shouldFilterForSimpsons() {
// load the possible values first to simulate same behavior as in UI,
// otherwise exceptions might occur
filterRowHeaderLayer.comboBoxDataProvider.getValues(1, 0);
// filter
natTable.doCommand(new UpdateDataCommand(natTable, 2, 2, new ArrayList<>(Arrays.asList("Simpson"))));
assertEquals(4500, bodyLayer.getFilterList().size());
}
@Test
public void shouldFilterForMaleSimpsons() {
// load the possible values first to simulate same behavior as in UI,
// otherwise exceptions might occur
filterRowHeaderLayer.comboBoxDataProvider.getValues(0, 0);
filterRowHeaderLayer.comboBoxDataProvider.getValues(1, 0);
// filter
List<String> firstNames = new ArrayList<>(Arrays.asList("Homer", "Bart"));
List<String> lastNames = new ArrayList<>(Arrays.asList("Simpson"));
natTable.doCommand(new UpdateDataCommand(natTable, 1, 2, firstNames));
natTable.doCommand(new UpdateDataCommand(natTable, 2, 2, lastNames));
assertEquals(2400, bodyLayer.getFilterList().size());
}
@Test
public void shouldFilterOnLoadPersistedState() {
// load the possible values first to simulate same behavior as in UI,
// otherwise exceptions might occur
filterRowHeaderLayer.comboBoxDataProvider.getValues(0, 0);
// filter
List<String> firstNames = new ArrayList<>(Arrays.asList("Homer", "Bart"));
natTable.doCommand(new UpdateDataCommand(natTable, 1, 2, firstNames));
assertEquals(2400, bodyLayer.getFilterList().size());
// persist
Properties properties = new Properties();
natTable.saveState("filtered", properties);
natTable.doCommand(new ClearAllFiltersCommand());
// test that nothing is filtered
assertEquals(9000, bodyLayer.getFilterList().size());
// load saved state
natTable.loadState("filtered", properties);
assertEquals(2400, bodyLayer.getFilterList().size());
}
// TODO should not remove filter on sort
@Ignore
@Test
public void shouldKeepFilterOnSort() throws InterruptedException {
shouldFilterForMaleSimpsons();
// change a value to check that sorting was applied
ExtendedPersonWithAddress person = bodyLayer.filterList.get(bodyLayer.filterList.size() - 1);
person.getAddress().setCity("Al Bundy Street");
// sort by street (column 4)
// natTable.doCommand(new SortColumnCommand(natTable, 5));
// natTable.doCommand(new SortColumnCommand(natTable, 5));
sortModel.sort(4, SortDirectionEnum.ASC, false);
Thread.sleep(500);
Object street = natTable.getDataValueByPosition(5, 3);
assertEquals("Al Bundy Street", street);
}
@SuppressWarnings("rawtypes")
@Test
public void shouldKeepFilterOnEdit() throws InterruptedException {
shouldFilterForMaleSimpsons();
Object columnOneFilter = filterRowHeaderLayer.filterRowDataLayer.getDataValue(0, 0);
assertTrue(columnOneFilter instanceof List);
List filter = (List) columnOneFilter;
assertEquals(2, filter.size());
assertTrue(filter.contains("Homer"));
assertTrue(filter.contains("Bart"));
ComboUpdateListener listener = new ComboUpdateListener();
CountDownLatch countDown = new CountDownLatch(1);
listener.setCountDown(countDown);
filterRowHeaderLayer.comboBoxDataProvider.addCacheUpdateListener(listener);
// edit one entry
natTable.doCommand(new UpdateDataCommand(natTable, 1, 3, "Bort"));
boolean completed = countDown.await(2000, TimeUnit.MILLISECONDS);
assertTrue("Timeout - no event received", completed);
assertEquals(1, listener.getEventsCount());
FilterRowComboUpdateEvent evt = listener.getReceivedEvents().get(0);
assertEquals(0, evt.getColumnIndex());
assertEquals(1, evt.getAddedItems().size());
assertEquals("Bort", evt.getAddedItems().iterator().next());
assertEquals("Bort", natTable.getDataValueByPosition(1, 3));
columnOneFilter = filterRowHeaderLayer.filterRowDataLayer.getDataValue(0, 0);
assertTrue(columnOneFilter instanceof List);
assertEquals(3, filter.size());
assertTrue(filter.contains("Homer"));
assertTrue(filter.contains("Bart"));
assertTrue(filter.contains("Bort"));
listener.clearReceivedEvents();
// edit back
countDown = new CountDownLatch(1);
listener.setCountDown(countDown);
natTable.doCommand(new UpdateDataCommand(natTable, 1, 3, "Bart"));
completed = countDown.await(2000, TimeUnit.MILLISECONDS);
assertTrue("Timeout - no event received", completed);
assertEquals(1, listener.getEventsCount());
evt = listener.getReceivedEvents().get(0);
assertEquals(0, evt.getColumnIndex());
assertEquals(0, evt.getAddedItems().size());
assertEquals(1, evt.getRemovedItems().size());
assertEquals("Bort", evt.getRemovedItems().iterator().next());
assertEquals("Bart", natTable.getDataValueByPosition(1, 3));
columnOneFilter = filterRowHeaderLayer.filterRowDataLayer.getDataValue(0, 0);
assertTrue(columnOneFilter instanceof List);
assertEquals(2, filter.size());
assertTrue(filter.contains("Homer"));
assertTrue(filter.contains("Bart"));
}
@SuppressWarnings("rawtypes")
@Test
public void shouldKeepFilterOnStructuralChanges() throws InterruptedException {
shouldFilterForMaleSimpsons();
// add an entry
Person person = new Person(42, "Ralph", "Wiggum", Gender.MALE, false, new Date());
Address address = new Address();
address.setStreet("Some Street");
address.setHousenumber(42);
address.setPostalCode(12345);
address.setCity("In the clouds");
// we need to wait here as the listChanged handling is triggered with a
// delay of 100 ms to avoid too frequent calculations
ComboUpdateListener listener = new ComboUpdateListener();
CountDownLatch countDown = new CountDownLatch(2);
listener.setCountDown(countDown);
filterRowHeaderLayer.comboBoxDataProvider.addCacheUpdateListener(listener);
ExtendedPersonWithAddress entry = new ExtendedPersonWithAddress(person, address,
"0000", "The little Ralphy", 0,
new ArrayList<String>(), new ArrayList<String>());
bodyLayer.eventList.add(entry);
// long start = System.currentTimeMillis();
boolean completed = countDown.await(2000, TimeUnit.MILLISECONDS);
// long end = System.currentTimeMillis();
// System.out.println("duration " + (end - start));
assertTrue("Timeout - no event received", completed);
assertEquals(2, listener.getEventsCount());
FilterRowComboUpdateEvent evt = listener.getReceivedEvents().get(0);
assertEquals(0, evt.getColumnIndex());
assertEquals(1, evt.getAddedItems().size());
assertEquals("Ralph", evt.getAddedItems().iterator().next());
evt = listener.getReceivedEvents().get(1);
assertEquals(1, evt.getColumnIndex());
assertEquals(1, evt.getAddedItems().size());
assertEquals("Wiggum", evt.getAddedItems().iterator().next());
// test that still all filters are set
Object columnOneFilter = filterRowHeaderLayer.filterRowDataLayer.getDataValue(0, 0);
assertTrue(columnOneFilter instanceof List);
List filter = (List) columnOneFilter;
assertEquals(3, filter.size());
assertTrue(filter.contains("Homer"));
assertTrue(filter.contains("Bart"));
assertTrue(filter.contains("Ralph"));
Object columnTwoFilter = filterRowHeaderLayer.filterRowDataLayer.getDataValue(1, 0);
assertTrue(columnTwoFilter instanceof List);
filter = (List) columnTwoFilter;
assertEquals(2, filter.size());
assertTrue(filter.contains("Simpson"));
assertTrue(filter.contains("Wiggum"));
bodyLayer.eventList.remove(entry);
countDown = new CountDownLatch(2);
listener.setCountDown(countDown);
// start = System.currentTimeMillis();
completed = countDown.await(2000, TimeUnit.MILLISECONDS);
// end = System.currentTimeMillis();
// System.out.println("duration " + (end - start));
assertTrue("Timeout - no event received", completed);
filterRowHeaderLayer.comboBoxDataProvider.removeCacheUpdateListener(listener);
}
private static List<ExtendedPersonWithAddress> createValues(int startId) {
return createPersons(startId).stream()
.map(p -> new ExtendedPersonWithAddress(
p,
PersonService.createAddress(),
PersonService.generateSimplePassword(),
PersonService.createRandomLengthText(),
PersonService.createRandomMoneyAmount(),
null,
null))
.collect(Collectors.toList());
}
private static List<Person> createPersons(int startId) {
List<Person> result = new ArrayList<>();
result.add(new Person(startId + 1, "Homer", "Simpson", Gender.MALE, true, new Date()));
result.add(new Person(startId + 2, "Homer", "Simpson", Gender.MALE, true, new Date()));
result.add(new Person(startId + 3, "Marge", "Simpson", Gender.FEMALE, true, new Date()));
result.add(new Person(startId + 4, "Marge", "Simpson", Gender.FEMALE, true, new Date()));
result.add(new Person(startId + 5, "Marge", "Simpson", Gender.FEMALE, true, new Date(), null));
result.add(new Person(startId + 6, "Ned", null, Gender.MALE, true, new Date()));
result.add(new Person(startId + 7, "Maude", null, Gender.FEMALE, true, new Date()));
result.add(new Person(startId + 8, "Homer", "Simpson", Gender.MALE, true, new Date()));
result.add(new Person(startId + 9, "Homer", "Simpson", Gender.MALE, true, new Date()));
result.add(new Person(startId + 10, "Homer", "Simpson", Gender.MALE, true, new Date()));
result.add(new Person(startId + 11, "Bart", "Simpson", Gender.MALE, false, new Date()));
result.add(new Person(startId + 12, "Bart", "Simpson", Gender.MALE, false, new Date()));
result.add(new Person(startId + 13, "Bart", "Simpson", Gender.MALE, false, new Date()));
result.add(new Person(startId + 14, "Marge", "Simpson", Gender.FEMALE, true, new Date()));
result.add(new Person(startId + 15, "Marge", "Simpson", Gender.FEMALE, true, new Date()));
result.add(new Person(startId + 16, "Lisa", "Simpson", Gender.FEMALE, false, new Date()));
result.add(new Person(startId + 17, "Lisa", "Simpson", Gender.FEMALE, false, new Date()));
result.add(new Person(startId + 18, "Ned", "Flanders", Gender.MALE, true, new Date()));
result.add(new Person(startId + 19, "Ned", "Flanders", Gender.MALE, true, new Date()));
result.add(new Person(startId + 20, "Maude", "Flanders", Gender.FEMALE, true, new Date()));
result.add(new Person(startId + 21, "Maude", "Flanders", Gender.FEMALE, true, new Date()));
result.add(new Person(startId + 22, "Rod", "Flanders", Gender.MALE, false, new Date()));
result.add(new Person(startId + 23, "Rod", "Flanders", Gender.MALE, false, new Date()));
result.add(new Person(startId + 24, "Tod", "Flanders", Gender.MALE, false, new Date()));
result.add(new Person(startId + 25, "Tod", "Flanders", Gender.MALE, false, new Date()));
result.add(new Person(startId + 26, "Lenny", "Leonard", Gender.MALE, false, new Date()));
result.add(new Person(startId + 27, "Lenny", "Leonard", Gender.MALE, false, new Date()));
result.add(new Person(startId + 28, "Carl", "Carlson", Gender.MALE, false, new Date()));
result.add(new Person(startId + 29, "Carl", "Carlson", Gender.MALE, false, new Date()));
result.add(new Person(startId + 30, "Timothy", "Lovejoy", Gender.MALE, false, new Date()));
return result;
}
static class BodyLayerStack<T> extends AbstractLayerTransform {
private final EventList<T> eventList;
private final SortedList<T> sortedList;
private final FilterList<T> filterList;
private final IDataProvider bodyDataProvider;
private final GroupByDataLayer<T> bodyDataLayer;
private final SelectionLayer selectionLayer;
private final TreeLayer treeLayer;
private final GroupByModel groupByModel = new GroupByModel();
private final GlazedListsEventLayer<T> glazedListsEventLayer;
public BodyLayerStack(List<T> values,
IColumnPropertyAccessor<T> columnPropertyAccessor,
ConfigRegistry configRegistry) {
// wrapping of the list to show into GlazedLists
// see http://publicobject.com/glazedlists/ for further information
this.eventList = GlazedLists.eventList(values);
TransformedList<T, T> rowObjectsGlazedList = GlazedLists.threadSafeList(this.eventList);
// use the SortedList constructor with 'null' for the Comparator
// because the Comparator
// will be set by configuration
this.sortedList = new SortedList<>(rowObjectsGlazedList, null);
// wrap the SortedList with the FilterList
this.filterList = new FilterList<>(this.sortedList);
// Use the GroupByDataLayer instead of the default DataLayer
this.bodyDataLayer = new GroupByDataLayer<>(
getGroupByModel(),
this.filterList,
columnPropertyAccessor,
configRegistry);
// get the IDataProvider that was created by the GroupByDataLayer
this.bodyDataProvider = this.bodyDataLayer.getDataProvider();
// layer for event handling of GlazedLists and PropertyChanges
this.glazedListsEventLayer =
new GlazedListsEventLayer<>(this.bodyDataLayer, this.filterList);
this.glazedListsEventLayer.setTestMode(true);
ColumnReorderLayer columnReorderLayer =
new ColumnReorderLayer(this.glazedListsEventLayer);
ColumnHideShowLayer columnHideShowLayer =
new ColumnHideShowLayer(columnReorderLayer);
this.selectionLayer =
new SelectionLayer(columnHideShowLayer);
// add a tree layer to visualise the grouping
this.treeLayer = new TreeLayer(this.selectionLayer, this.bodyDataLayer.getTreeRowModel());
ViewportLayer viewportLayer = new ViewportLayer(this.treeLayer);
// this will avoid tree specific rendering regarding alignment and
// indentation in case no grouping is active
viewportLayer.setConfigLabelAccumulator(new GroupByConfigLabelModifier(getGroupByModel()));
setUnderlyingLayer(viewportLayer);
}
public ILayer getGlazedListsEventLayer() {
return this.glazedListsEventLayer;
}
public TreeLayer getTreeLayer() {
return this.treeLayer;
}
public SelectionLayer getSelectionLayer() {
return this.selectionLayer;
}
public EventList<T> getEventList() {
return this.eventList;
}
public SortedList<T> getSortedList() {
return this.sortedList;
}
public FilterList<T> getFilterList() {
return this.filterList;
}
public IDataProvider getBodyDataProvider() {
return this.bodyDataProvider;
}
public GroupByDataLayer<T> getBodyDataLayer() {
return this.bodyDataLayer;
}
public GroupByModel getGroupByModel() {
return this.groupByModel;
}
}
static class ComboUpdateListener implements IFilterRowComboUpdateListener {
// Received events are kept in order
private final List<FilterRowComboUpdateEvent> receivedEvents = new LinkedList<>();
private CountDownLatch countDownLatch;
public void setCountDown(CountDownLatch countDown) {
this.countDownLatch = countDown;
}
@Override
public void handleEvent(FilterRowComboUpdateEvent event) {
this.receivedEvents.add(event);
if (this.countDownLatch != null) {
this.countDownLatch.countDown();
}
}
public List<FilterRowComboUpdateEvent> getReceivedEvents() {
return this.receivedEvents;
}
public void clearReceivedEvents() {
this.receivedEvents.clear();
}
public int getEventsCount() {
return this.receivedEvents.size();
}
}
}