| /******************************************************************************* |
| * 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(); |
| } |
| |
| } |
| } |