| /******************************************************************************* |
| * Copyright (c) 2023 Dirk Fauth and others. |
| * |
| * 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.examples._800_Integration; |
| |
| import java.io.Serializable; |
| import java.text.DecimalFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| import org.eclipse.jface.bindings.keys.KeyStroke; |
| import org.eclipse.jface.bindings.keys.ParseException; |
| import org.eclipse.jface.fieldassist.SimpleContentProposalProvider; |
| import org.eclipse.jface.fieldassist.TextContentAdapter; |
| import org.eclipse.jface.layout.GridDataFactory; |
| import org.eclipse.nebula.widgets.nattable.NatTable; |
| import org.eclipse.nebula.widgets.nattable.columnChooser.command.DisplayColumnChooserCommandHandler; |
| import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration; |
| import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration; |
| 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.config.IEditableRule; |
| 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.data.IRowIdAccessor; |
| import org.eclipse.nebula.widgets.nattable.data.ListDataProvider; |
| import org.eclipse.nebula.widgets.nattable.data.convert.ContextualDisplayConverter; |
| import org.eclipse.nebula.widgets.nattable.data.convert.DefaultBooleanDisplayConverter; |
| import org.eclipse.nebula.widgets.nattable.data.convert.DefaultDateDisplayConverter; |
| import org.eclipse.nebula.widgets.nattable.data.convert.DefaultDisplayConverter; |
| import org.eclipse.nebula.widgets.nattable.data.convert.DefaultIntegerDisplayConverter; |
| import org.eclipse.nebula.widgets.nattable.data.convert.DisplayConverter; |
| import org.eclipse.nebula.widgets.nattable.dataset.person.Address; |
| import org.eclipse.nebula.widgets.nattable.dataset.person.DataModelConstants; |
| 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.dataset.person.PersonWithAddress; |
| import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes; |
| import org.eclipse.nebula.widgets.nattable.edit.editor.CheckBoxCellEditor; |
| import org.eclipse.nebula.widgets.nattable.edit.editor.ComboBoxCellEditor; |
| 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; |
| import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel; |
| import org.eclipse.nebula.widgets.nattable.extension.glazedlists.filterrow.ComboBoxFilterRowHeaderComposite; |
| import org.eclipse.nebula.widgets.nattable.extension.glazedlists.filterrow.ComboBoxGlazedListsWithExcludeFilterStrategy; |
| import org.eclipse.nebula.widgets.nattable.extension.glazedlists.filterrow.FilterRowUtils; |
| import org.eclipse.nebula.widgets.nattable.extension.glazedlists.filterrow.GlazedListsFilterRowComboBoxDataProvider; |
| import org.eclipse.nebula.widgets.nattable.extension.nebula.richtext.MarkupDisplayConverter; |
| import org.eclipse.nebula.widgets.nattable.extension.nebula.richtext.RegexMarkupValue; |
| import org.eclipse.nebula.widgets.nattable.extension.nebula.richtext.RichTextCellPainter; |
| import org.eclipse.nebula.widgets.nattable.extension.nebula.richtext.RichTextConfigAttributes; |
| import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowDataLayer; |
| import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowDataProvider; |
| import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowPainter; |
| import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowTextCellEditor; |
| import org.eclipse.nebula.widgets.nattable.filterrow.IFilterStrategy; |
| import org.eclipse.nebula.widgets.nattable.filterrow.ParseResult; |
| import org.eclipse.nebula.widgets.nattable.filterrow.TextMatchingMode; |
| import org.eclipse.nebula.widgets.nattable.filterrow.combobox.ComboBoxFilterIconPainter; |
| import org.eclipse.nebula.widgets.nattable.filterrow.combobox.ComboBoxFilterRowConfiguration; |
| import org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboBoxCellEditor; |
| import org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboBoxDataProvider; |
| import org.eclipse.nebula.widgets.nattable.filterrow.config.FilterRowConfigAttributes; |
| import org.eclipse.nebula.widgets.nattable.filterrow.event.FilterAppliedEvent; |
| import org.eclipse.nebula.widgets.nattable.freeze.CompositeFreezeLayer; |
| import org.eclipse.nebula.widgets.nattable.freeze.FreezeLayer; |
| import org.eclipse.nebula.widgets.nattable.grid.GridRegion; |
| 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.group.performance.ColumnGroupExpandCollapseLayer; |
| import org.eclipse.nebula.widgets.nattable.group.performance.ColumnGroupHeaderLayer; |
| import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer; |
| import org.eclipse.nebula.widgets.nattable.hover.HoverLayer; |
| import org.eclipse.nebula.widgets.nattable.hover.action.ClearHoverStylingAction; |
| import org.eclipse.nebula.widgets.nattable.hover.config.ColumnHeaderResizeHoverBindings; |
| import org.eclipse.nebula.widgets.nattable.hover.config.RowHeaderResizeHoverBindings; |
| import org.eclipse.nebula.widgets.nattable.layer.AbstractIndexLayerTransform; |
| import org.eclipse.nebula.widgets.nattable.layer.DataLayer; |
| import org.eclipse.nebula.widgets.nattable.layer.ILayer; |
| import org.eclipse.nebula.widgets.nattable.layer.ILayerListener; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.AggregateConfigLabelAccumulator; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnLabelAccumulator; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.IConfigLabelAccumulator; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; |
| import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent; |
| import org.eclipse.nebula.widgets.nattable.layer.event.RowStructuralRefreshEvent; |
| import org.eclipse.nebula.widgets.nattable.painter.cell.BackgroundPainter; |
| import org.eclipse.nebula.widgets.nattable.painter.cell.CheckBoxPainter; |
| import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.PaddingDecorator; |
| import org.eclipse.nebula.widgets.nattable.persistence.command.DisplayPersistenceDialogCommandHandler; |
| import org.eclipse.nebula.widgets.nattable.reorder.ColumnReorderLayer; |
| import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer; |
| import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer; |
| import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration; |
| import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes; |
| import org.eclipse.nebula.widgets.nattable.style.DisplayMode; |
| import org.eclipse.nebula.widgets.nattable.style.Style; |
| import org.eclipse.nebula.widgets.nattable.style.theme.IThemeExtension; |
| import org.eclipse.nebula.widgets.nattable.style.theme.ModernNatTableThemeConfiguration; |
| import org.eclipse.nebula.widgets.nattable.style.theme.ThemeConfiguration; |
| import org.eclipse.nebula.widgets.nattable.ui.NatEventData; |
| import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry; |
| import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher; |
| import org.eclipse.nebula.widgets.nattable.ui.menu.HeaderMenuConfiguration; |
| import org.eclipse.nebula.widgets.nattable.ui.menu.IMenuItemProvider; |
| import org.eclipse.nebula.widgets.nattable.ui.menu.IMenuItemState; |
| import org.eclipse.nebula.widgets.nattable.ui.menu.MenuItemProviders; |
| import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuAction; |
| import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder; |
| import org.eclipse.nebula.widgets.nattable.ui.scaling.ScalingUiBindingConfiguration; |
| import org.eclipse.nebula.widgets.nattable.util.GUIHelper; |
| import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.KeyAdapter; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.events.ModifyEvent; |
| import org.eclipse.swt.events.ModifyListener; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.layout.RowLayout; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.MenuItem; |
| import org.eclipse.swt.widgets.Text; |
| |
| import ca.odell.glazedlists.EventList; |
| import ca.odell.glazedlists.FilterList; |
| import ca.odell.glazedlists.GlazedLists; |
| import ca.odell.glazedlists.SortedList; |
| import ca.odell.glazedlists.TextFilterator; |
| import ca.odell.glazedlists.TransformedList; |
| import ca.odell.glazedlists.matchers.Matcher; |
| import ca.odell.glazedlists.matchers.TextMatcherEditor; |
| |
| public class _818_SortableAllFilterPerformanceColumnGroupExample extends AbstractNatExample { |
| |
| private static final String EXCLUDE_LABEL = "EXCLUDE"; |
| |
| private ArrayList<Serializable> filterExcludes = new ArrayList<>(); |
| |
| private List<PersonWithAddress> mixedPersons = PersonService.getPersonsWithAddress(100); |
| // private List<PersonWithAddress> mixedPersons = createPersons(0); |
| private List<PersonWithAddress> alternativePersons = createAlternativePersons(); |
| |
| private AtomicBoolean alternativePersonsActive = new AtomicBoolean(false); |
| |
| public static void main(String[] args) { |
| StandaloneNatExampleRunner.run(new _818_SortableAllFilterPerformanceColumnGroupExample()); |
| } |
| |
| @Override |
| public String getDescription() { |
| return "This example shows a complex setup that combines multiple filter features like" |
| + " the mixed filter row within a grid that has Excel-like multi-select combobox filters, free text filters and single-selection combobox filters," |
| + " an additional single-line filter," |
| + " a configuration that allows to exclude rows from filtering" |
| + " and a configuration so that the filter comboboxes only show items for currently visible rows in the table."; |
| } |
| |
| @Override |
| public Control createExampleControl(Composite parent) { |
| Composite container = new Composite(parent, SWT.NONE); |
| container.setLayout(new GridLayout()); |
| GridDataFactory.fillDefaults().grab(true, true).applyTo(container); |
| |
| Text input = new Text(container, SWT.SINGLE | SWT.SEARCH | SWT.ICON_CANCEL); |
| input.setMessage("type filter text"); |
| GridDataFactory.fillDefaults().grab(true, false).applyTo(input); |
| |
| // create a new ConfigRegistry which will be needed for GlazedLists |
| // handling |
| ConfigRegistry configRegistry = new ConfigRegistry(); |
| |
| // property names of the Person class |
| String[] propertyNames = { |
| "firstName", |
| "lastName", |
| "gender", |
| "married", |
| "birthday", |
| "address.street", |
| "address.housenumber", |
| "address.postalCode", |
| "address.city" }; |
| |
| // 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("birthday", "Birthday"); |
| propertyToLabelMap.put("address.street", "Street"); |
| propertyToLabelMap.put("address.housenumber", "Housenumber"); |
| propertyToLabelMap.put("address.postalCode", "Postal Code"); |
| propertyToLabelMap.put("address.city", "City"); |
| |
| IColumnPropertyAccessor<PersonWithAddress> columnPropertyAccessor = |
| new ExtendedReflectiveColumnPropertyAccessor<>(propertyNames); |
| |
| IRowIdAccessor<PersonWithAddress> rowIdAccessor = PersonWithAddress::getId; |
| |
| final BodyLayerStack<PersonWithAddress> bodyLayerStack = |
| new BodyLayerStack<>( |
| this.mixedPersons, |
| columnPropertyAccessor); |
| |
| // add some null and empty values to verify the correct handling |
| bodyLayerStack.getBodyDataLayer().setDataValue(0, 3, ""); |
| bodyLayerStack.getBodyDataLayer().setDataValue(0, 5, null); |
| bodyLayerStack.getBodyDataLayer().setDataValue(1, 2, ""); |
| bodyLayerStack.getBodyDataLayer().setDataValue(1, 6, null); |
| bodyLayerStack.getBodyDataLayer().setDataValue(2, 3, null); |
| bodyLayerStack.getBodyDataLayer().setDataValue(2, 5, null); |
| |
| // build the column header layer |
| IDataProvider columnHeaderDataProvider = |
| new DefaultColumnHeaderDataProvider(propertyNames, propertyToLabelMap); |
| DataLayer columnHeaderDataLayer = |
| new DefaultColumnHeaderDataLayer(columnHeaderDataProvider); |
| |
| HoverLayer columnHoverLayer = |
| new HoverLayer(columnHeaderDataLayer, false); |
| |
| ColumnHeaderLayer columnHeaderLayer = |
| new ColumnHeaderLayer( |
| columnHoverLayer, |
| bodyLayerStack, |
| bodyLayerStack.getSelectionLayer(), |
| false); |
| |
| columnHeaderLayer.addConfiguration( |
| new ColumnHeaderResizeHoverBindings(columnHoverLayer)); |
| |
| SortHeaderLayer<PersonWithAddress> sortHeaderLayer = |
| new SortHeaderLayer<>( |
| columnHeaderLayer, |
| new GlazedListsSortModel<>( |
| bodyLayerStack.getSortedList(), |
| columnPropertyAccessor, |
| configRegistry, |
| columnHeaderDataLayer)); |
| |
| ColumnGroupHeaderLayer columnGroupHeaderLayer = new ColumnGroupHeaderLayer( |
| sortHeaderLayer, |
| bodyLayerStack.getSelectionLayer()); |
| columnGroupHeaderLayer.setCalculateHeight(true); |
| |
| columnGroupHeaderLayer.addGroup("Person", 0, 4); |
| |
| // Create a customized FilterRowComboBoxDataProvider that |
| // distincts the empty string and null from the collected values. This |
| // way null and "" entries in the collection are treated the same way |
| // and there is only a single "empty" entry in the dropdown. |
| FilterRowComboBoxDataProvider<PersonWithAddress> filterRowComboBoxDataProvider = |
| new GlazedListsFilterRowComboBoxDataProvider<>( |
| bodyLayerStack.getGlazedListsEventLayer(), |
| bodyLayerStack.getSortedList(), |
| columnPropertyAccessor); |
| filterRowComboBoxDataProvider.setDistinctNullAndEmpty(true); |
| filterRowComboBoxDataProvider.setCachingEnabled(true); |
| |
| ComboBoxGlazedListsWithExcludeFilterStrategy<PersonWithAddress> filterStrategy = |
| new ComboBoxGlazedListsWithExcludeFilterStrategy<>( |
| filterRowComboBoxDataProvider, |
| bodyLayerStack.getFilterList(), |
| columnPropertyAccessor, |
| configRegistry); |
| |
| // create the ComboBoxFilterRowHeaderComposite without the default |
| // configuration |
| ComboBoxFilterRowHeaderComposite<PersonWithAddress> filterRowHeaderLayer = |
| new ComboBoxFilterRowHeaderComposite<>( |
| filterStrategy, |
| filterRowComboBoxDataProvider, |
| columnGroupHeaderLayer, |
| columnHeaderDataProvider, |
| configRegistry, |
| false); |
| |
| filterRowComboBoxDataProvider.setFilterCollection(bodyLayerStack.getFilterList(), filterRowHeaderLayer); |
| |
| // add a default ComboBoxFilterRowConfiguration with an updated editor |
| // that shows a filter icon if a filter is applied |
| FilterRowComboBoxCellEditor filterEditor = new FilterRowComboBoxCellEditor(filterRowComboBoxDataProvider, 10); |
| filterEditor.configureDropdownFilter(true, true); |
| filterRowHeaderLayer.addConfiguration( |
| new ComboBoxFilterRowConfiguration( |
| filterEditor, |
| new ComboBoxFilterIconPainter(filterRowComboBoxDataProvider), |
| filterRowComboBoxDataProvider)); |
| |
| // add the specialized configuration to the |
| // ComboBoxFilterRowHeaderComposite |
| filterRowHeaderLayer.addConfiguration(new FilterRowConfiguration()); |
| |
| // build the row header layer |
| IDataProvider rowHeaderDataProvider = |
| new DefaultRowHeaderDataProvider(bodyLayerStack.getBodyDataProvider()); |
| DataLayer rowHeaderDataLayer = |
| new DefaultRowHeaderDataLayer(rowHeaderDataProvider); |
| |
| HoverLayer rowHoverLayer = |
| new HoverLayer(rowHeaderDataLayer, false); |
| RowHeaderLayer rowHeaderLayer = |
| new RowHeaderLayer( |
| rowHoverLayer, |
| bodyLayerStack, |
| bodyLayerStack.getSelectionLayer(), |
| false); |
| |
| // add RowHeaderHoverLayerConfiguration to ensure that hover styling and |
| // resizing is working together |
| rowHeaderLayer.addConfiguration( |
| new RowHeaderResizeHoverBindings(rowHoverLayer)); |
| |
| // 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( |
| bodyLayerStack, |
| filterRowHeaderLayer, |
| rowHeaderLayer, |
| cornerLayer); |
| |
| // turn the auto configuration off as we want to add our header menu |
| // configuration |
| NatTable natTable = new NatTable(container, gridLayer, false); |
| |
| // as the autoconfiguration of the NatTable is turned off, we have to |
| // add the DefaultNatTableStyleConfiguration and the ConfigRegistry |
| // manually |
| natTable.setConfigRegistry(configRegistry); |
| natTable.addConfiguration(new DefaultNatTableStyleConfiguration()); |
| |
| // edit configuration |
| natTable.addConfiguration(new EditConfiguration()); |
| |
| natTable.addConfiguration(new SingleClickSortConfiguration()); |
| |
| // add a ui binding to clear the hover in the column header also if you |
| // move over a column group or filter row cell |
| natTable.addConfiguration(new AbstractUiBindingConfiguration() { |
| |
| @Override |
| public void configureUiBindings(UiBindingRegistry uiBindingRegistry) { |
| uiBindingRegistry.registerFirstMouseMoveBinding((natTable, event, regionLabels) -> ((natTable != null && regionLabels == null) || regionLabels != null |
| && (regionLabels.hasLabel(GridRegion.BODY) || regionLabels.hasLabel(GridRegion.COLUMN_GROUP_HEADER) || regionLabels.hasLabel(GridRegion.FILTER_ROW))), |
| new ClearHoverStylingAction()); |
| } |
| }); |
| |
| // header menu configuration |
| natTable.addConfiguration(new HeaderMenuConfiguration(natTable) { |
| |
| @Override |
| protected PopupMenuBuilder createCornerMenu(NatTable natTable) { |
| return super.createCornerMenu(natTable) |
| .withStateManagerMenuItemProvider() |
| .withClearAllFilters(); |
| } |
| |
| @Override |
| protected PopupMenuBuilder createColumnHeaderMenu(NatTable natTable) { |
| return new PopupMenuBuilder(natTable) |
| .withHideColumnMenuItem() |
| .withShowAllColumnsMenuItem() |
| .withColumnChooserMenuItem() |
| .withCreateColumnGroupMenuItem() |
| .withUngroupColumnsMenuItem() |
| .withAutoResizeSelectedColumnsMenuItem() |
| .withColumnStyleEditor() |
| .withColumnRenameDialog() |
| .withClearAllFilters(); |
| } |
| }); |
| |
| // Column group header menu |
| final Menu columnGroupHeaderMenu = new PopupMenuBuilder(natTable) |
| .withRenameColumnGroupMenuItem() |
| .withRemoveColumnGroupMenuItem() |
| .build(); |
| |
| natTable.addConfiguration(new AbstractUiBindingConfiguration() { |
| @Override |
| public void configureUiBindings(UiBindingRegistry uiBindingRegistry) { |
| uiBindingRegistry.registerFirstMouseDownBinding( |
| new MouseEventMatcher( |
| SWT.NONE, |
| GridRegion.COLUMN_GROUP_HEADER, |
| MouseEventMatcher.RIGHT_BUTTON), |
| new PopupMenuAction(columnGroupHeaderMenu)); |
| } |
| }); |
| |
| // body menu configuration |
| natTable.addConfiguration(new BodyMenuConfiguration<PersonWithAddress>( |
| natTable, |
| bodyLayerStack, |
| rowIdAccessor, |
| filterStrategy, |
| filterRowHeaderLayer.getFilterRowDataLayer().getFilterRowDataProvider())); |
| |
| natTable.addConfiguration(new ScalingUiBindingConfiguration(natTable)); |
| |
| // Register column chooser |
| DisplayColumnChooserCommandHandler columnChooserCommandHandler = |
| new DisplayColumnChooserCommandHandler( |
| bodyLayerStack.getColumnHideShowLayer(), |
| columnHeaderLayer, |
| columnHeaderDataLayer, |
| columnGroupHeaderLayer, |
| false); |
| |
| bodyLayerStack.registerCommandHandler(columnChooserCommandHandler); |
| |
| natTable.configure(); |
| |
| // The painter instances in a theme configuration are created on demand |
| // to avoid unnecessary painter instances in memory. To change the |
| // default filter row cell painter with the one for the excel like |
| // filter row, we get the painter from the ConfigRegistry after |
| // natTable#configure() and override createPainterInstances() of the |
| // theme configuration. |
| // Additionally we override the default body painter and data to be able |
| // to highlight the single field filter expression |
| ModernNatTableThemeConfiguration themeConfiguration = new ModernNatTableThemeConfiguration() { |
| @Override |
| public void createPainterInstances() { |
| super.createPainterInstances(); |
| this.filterRowCellPainter = configRegistry.getConfigAttribute( |
| CellConfigAttributes.CELL_PAINTER, |
| DisplayMode.NORMAL, |
| GridRegion.FILTER_ROW); |
| |
| this.defaultCellPainter = new BackgroundPainter( |
| new PaddingDecorator(new RichTextCellPainter(false, false), 0, 5, 0, 5, false)); |
| |
| } |
| }; |
| |
| // add the style configuration for hover |
| themeConfiguration.addThemeExtension(new IThemeExtension() { |
| |
| @Override |
| public void unregisterStyles(IConfigRegistry configRegistry) { |
| configRegistry.unregisterConfigAttribute( |
| CellConfigAttributes.CELL_STYLE, |
| DisplayMode.HOVER, |
| GridRegion.COLUMN_HEADER); |
| configRegistry.unregisterConfigAttribute( |
| CellConfigAttributes.CELL_STYLE, |
| DisplayMode.SELECT_HOVER, |
| GridRegion.COLUMN_HEADER); |
| |
| configRegistry.unregisterConfigAttribute( |
| CellConfigAttributes.CELL_STYLE, |
| DisplayMode.HOVER, |
| GridRegion.ROW_HEADER); |
| configRegistry.unregisterConfigAttribute( |
| CellConfigAttributes.CELL_STYLE, |
| DisplayMode.SELECT_HOVER, |
| GridRegion.ROW_HEADER); |
| } |
| |
| @Override |
| public void registerStyles(IConfigRegistry configRegistry) { |
| Style style = new Style(); |
| style.setAttributeValue( |
| CellStyleAttributes.BACKGROUND_COLOR, |
| GUIHelper.getColor(173, 216, 230)); |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.CELL_STYLE, |
| style, |
| DisplayMode.HOVER, |
| GridRegion.COLUMN_HEADER); |
| |
| style = new Style(); |
| style.setAttributeValue( |
| CellStyleAttributes.BACKGROUND_COLOR, |
| GUIHelper.getColor(0, 71, 171)); |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.CELL_STYLE, |
| style, |
| DisplayMode.SELECT_HOVER, |
| GridRegion.COLUMN_HEADER); |
| |
| style = new Style(); |
| style.setAttributeValue( |
| CellStyleAttributes.BACKGROUND_COLOR, |
| GUIHelper.COLOR_RED); |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.CELL_STYLE, |
| style, |
| DisplayMode.HOVER, |
| GridRegion.ROW_HEADER); |
| |
| style = new Style(); |
| style.setAttributeValue( |
| CellStyleAttributes.BACKGROUND_COLOR, |
| GUIHelper.COLOR_BLUE); |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.CELL_STYLE, |
| style, |
| DisplayMode.SELECT_HOVER, |
| GridRegion.ROW_HEADER); |
| } |
| }); |
| |
| // configure the filter exclude support |
| configureFilterExcludes(rowIdAccessor, filterStrategy, bodyLayerStack, themeConfiguration); |
| |
| // configure the single field filter support |
| configureSingleFieldFilter(input, filterStrategy, bodyLayerStack, themeConfiguration, natTable, filterRowHeaderLayer); |
| |
| natTable.setTheme(themeConfiguration); |
| |
| natTable.registerCommandHandler( |
| new DisplayPersistenceDialogCommandHandler(natTable)); |
| |
| GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable); |
| |
| Label rowCount = new Label(container, SWT.NONE); |
| rowCount.setText(bodyLayerStack.getFilterList().size() + " / " + bodyLayerStack.getSortedList().size()); |
| |
| natTable.addLayerListener(new ILayerListener() { |
| |
| @Override |
| public void handleLayerEvent(ILayerEvent event) { |
| if (event instanceof RowStructuralRefreshEvent) { |
| rowCount.setText(bodyLayerStack.getFilterList().size() + " / " + bodyLayerStack.getSortedList().size()); |
| } |
| } |
| }); |
| |
| Composite buttonPanel = new Composite(container, SWT.NONE); |
| buttonPanel.setLayout(new RowLayout()); |
| GridDataFactory.fillDefaults().grab(true, false).applyTo(buttonPanel); |
| |
| Button addRowButton = new Button(buttonPanel, SWT.PUSH); |
| addRowButton.setText("Add Row"); |
| addRowButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| Address address = new Address(); |
| address.setStreet("Some Street"); |
| address.setHousenumber(42); |
| address.setPostalCode(12345); |
| address.setCity("In the clouds"); |
| PersonWithAddress person = new PersonWithAddress(42, "Ralph", |
| "Wiggum", Gender.MALE, false, new Date(), address); |
| |
| bodyLayerStack.getFilterList().add(person); |
| |
| // as the GlazedListsEventLayer listens on list changes of the |
| // FilterList, but the new entry will be filtered, there will be |
| // no ListChangeEvent. Therefore we need to fire a row |
| // structural refresh event manually to trigger a combobox cache |
| // update |
| bodyLayerStack.getGlazedListsEventLayer().fireLayerEvent( |
| new RowStructuralRefreshEvent(bodyLayerStack.getBodyDataLayer())); |
| } |
| }); |
| |
| Button toggleComboContentButton = new Button(buttonPanel, SWT.PUSH); |
| toggleComboContentButton.setText("Disable Dynamic Filter ComboBox Content"); |
| toggleComboContentButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| String buttonText = toggleComboContentButton.getText(); |
| if (buttonText.startsWith("Disable")) { |
| filterRowComboBoxDataProvider.setFilterCollection(null, null); |
| toggleComboContentButton.setText("Enable Dynamic Filter ComboBox Content"); |
| } else { |
| filterRowComboBoxDataProvider.setFilterCollection(bodyLayerStack.getFilterList(), filterRowHeaderLayer); |
| toggleComboContentButton.setText("Disable Dynamic Filter ComboBox Content"); |
| } |
| } |
| }); |
| |
| Button replaceContentButton = new Button(buttonPanel, SWT.PUSH); |
| replaceContentButton.setText("Replace"); |
| replaceContentButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| bodyLayerStack.getSortedList().clear(); |
| if (_818_SortableAllFilterPerformanceColumnGroupExample.this.alternativePersonsActive.compareAndSet(true, false)) { |
| bodyLayerStack.getSortedList().addAll(_818_SortableAllFilterPerformanceColumnGroupExample.this.mixedPersons); |
| } else { |
| _818_SortableAllFilterPerformanceColumnGroupExample.this.alternativePersonsActive.set(true); |
| bodyLayerStack.getSortedList().addAll(_818_SortableAllFilterPerformanceColumnGroupExample.this.alternativePersons); |
| } |
| |
| } |
| }); |
| |
| return container; |
| } |
| |
| /** |
| * This method is used to configure the filter exclude support. This means |
| * it creates and applies an exclude {@link Matcher}, configures an |
| * {@link IConfigLabelAccumulator} and registers the styling via |
| * {@link IThemeExtension}. |
| * |
| * @param rowIdAccessor |
| * @param filterStrategy |
| * @param bodyLayerStack |
| * @param themeConfiguration |
| */ |
| private void configureFilterExcludes( |
| IRowIdAccessor<PersonWithAddress> rowIdAccessor, |
| ComboBoxGlazedListsWithExcludeFilterStrategy<PersonWithAddress> filterStrategy, |
| BodyLayerStack<PersonWithAddress> bodyLayerStack, |
| ThemeConfiguration themeConfiguration) { |
| |
| // register the Matcher to the |
| // ComboBoxGlazedListsWithExcludeFilterStrategy |
| Matcher<PersonWithAddress> idMatcher = item -> _818_SortableAllFilterPerformanceColumnGroupExample.this.filterExcludes.contains(rowIdAccessor.getRowId(item)); |
| filterStrategy.addExcludeFilter(idMatcher); |
| |
| // register the IConfigLabelAccumulator to the body DataLayer |
| AggregateConfigLabelAccumulator aggregate = new AggregateConfigLabelAccumulator(); |
| aggregate.add(bodyLayerStack.getBodyDataLayer().getConfigLabelAccumulator()); |
| aggregate.add((IConfigLabelAccumulator) (configLabels, columnPosition, rowPosition) -> { |
| if (idMatcher.matches(bodyLayerStack.getBodyDataProvider().getRowObject(rowPosition))) { |
| configLabels.add(EXCLUDE_LABEL); |
| } |
| }); |
| bodyLayerStack.getBodyDataLayer().setConfigLabelAccumulator(aggregate); |
| |
| // extend the ThemeConfiguration to add styling for the EXCLUDE_LABEL |
| themeConfiguration.addThemeExtension(new IThemeExtension() { |
| |
| @Override |
| public void unregisterStyles(IConfigRegistry configRegistry) { |
| configRegistry.unregisterConfigAttribute( |
| CellConfigAttributes.CELL_STYLE, |
| DisplayMode.NORMAL, |
| EXCLUDE_LABEL); |
| } |
| |
| @Override |
| public void registerStyles(IConfigRegistry configRegistry) { |
| Style style = new Style(); |
| style.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR, GUIHelper.COLOR_WIDGET_LIGHT_SHADOW); |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.CELL_STYLE, |
| style, |
| DisplayMode.NORMAL, |
| EXCLUDE_LABEL); |
| } |
| }); |
| } |
| |
| /** |
| * This method is used to configure the single field filter support. |
| * |
| * @param input |
| * @param filterStrategy |
| * @param bodyLayerStack |
| * @param themeConfiguration |
| * @param natTable |
| */ |
| private void configureSingleFieldFilter( |
| Text input, |
| ComboBoxGlazedListsWithExcludeFilterStrategy<PersonWithAddress> filterStrategy, |
| BodyLayerStack<PersonWithAddress> bodyLayerStack, |
| ThemeConfiguration themeConfiguration, |
| NatTable natTable, |
| ILayer columnHeaderLayer) { |
| |
| // define a TextMatcherEditor and add it as static filter |
| TextMatcherEditor<PersonWithAddress> matcherEditor = new TextMatcherEditor<>(new TextFilterator<PersonWithAddress>() { |
| |
| @Override |
| public void getFilterStrings(List<String> baseList, PersonWithAddress element) { |
| // add all values that should be included in filtering |
| // Note: |
| // if special converters are involved in rendering, |
| // consider using them for adding the String values |
| baseList.add(element.getFirstName()); |
| baseList.add(element.getLastName()); |
| baseList.add("" + element.getGender()); |
| baseList.add("" + element.isMarried()); |
| baseList.add("" + element.getBirthday()); |
| baseList.add(element.getAddress().getStreet()); |
| baseList.add("" + element.getAddress().getHousenumber()); |
| baseList.add("" + element.getAddress().getPostalCode()); |
| baseList.add(element.getAddress().getCity()); |
| } |
| }); |
| matcherEditor.setMode(TextMatcherEditor.CONTAINS); |
| |
| filterStrategy.addStaticFilter(matcherEditor); |
| |
| RegexMarkupValue regexMarkup = new RegexMarkupValue("", |
| "<span style=\"background-color:rgb(255, 255, 0)\">", |
| "</span>"); |
| |
| // markup for highlighting |
| MarkupDisplayConverter markupConverter = new MarkupDisplayConverter(); |
| markupConverter.registerMarkup("highlight", regexMarkup); |
| // register markup display converter to be able to combine the markup |
| // with other body display content provider |
| natTable.getConfigRegistry().registerConfigAttribute( |
| RichTextConfigAttributes.MARKUP_DISPLAY_CONVERTER, |
| markupConverter, |
| DisplayMode.NORMAL, |
| GridRegion.BODY); |
| |
| // connect the input field with the matcher |
| input.addKeyListener(new KeyAdapter() { |
| @Override |
| public void keyPressed(KeyEvent e) { |
| if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) { |
| String text = input.getText(); |
| matcherEditor.setFilterText(new String[] { text }); |
| regexMarkup.setRegexValue(text.isEmpty() ? "" : "(" + text + ")"); |
| natTable.refresh(false); |
| columnHeaderLayer.fireLayerEvent(new FilterAppliedEvent(columnHeaderLayer)); |
| } |
| } |
| }); |
| |
| input.addModifyListener(new ModifyListener() { |
| |
| @Override |
| public void modifyText(ModifyEvent e) { |
| String text = input.getText(); |
| if (text == null || text.isEmpty()) { |
| matcherEditor.setFilterText(new String[] {}); |
| regexMarkup.setRegexValue(""); |
| natTable.refresh(false); |
| columnHeaderLayer.fireLayerEvent(new FilterAppliedEvent(columnHeaderLayer, true)); |
| } |
| } |
| }); |
| |
| } |
| |
| /** |
| * Always encapsulate the body layer stack in an AbstractLayerTransform to |
| * ensure that the index transformations are performed in later commands. |
| * |
| * @param <T> |
| */ |
| class BodyLayerStack<T> extends AbstractIndexLayerTransform { |
| |
| private final SortedList<T> sortedList; |
| private final FilterList<T> filterList; |
| |
| private final ListDataProvider<T> bodyDataProvider; |
| private final DataLayer bodyDataLayer; |
| private final GlazedListsEventLayer<T> glazedListsEventLayer; |
| |
| private final ColumnHideShowLayer columnHideShowLayer; |
| |
| private final SelectionLayer selectionLayer; |
| |
| public BodyLayerStack(List<T> values, IColumnPropertyAccessor<T> columnPropertyAccessor) { |
| // wrapping of the list to show into GlazedLists |
| // see http://publicobject.com/glazedlists/ for further information |
| EventList<T> eventList = GlazedLists.eventList(values); |
| TransformedList<T, T> rowObjectsGlazedList = GlazedLists.threadSafeList(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); |
| |
| this.bodyDataProvider = |
| new ListDataProvider<>(this.filterList, columnPropertyAccessor); |
| this.bodyDataLayer = new DataLayer(getBodyDataProvider()); |
| this.bodyDataLayer.setConfigLabelAccumulator(new ColumnLabelAccumulator()); |
| |
| // layer for event handling of GlazedLists and PropertyChanges |
| this.glazedListsEventLayer = |
| new GlazedListsEventLayer<>(this.bodyDataLayer, this.filterList); |
| |
| ColumnReorderLayer reorderLayer = new ColumnReorderLayer(this.glazedListsEventLayer); |
| this.columnHideShowLayer = new ColumnHideShowLayer(reorderLayer); |
| |
| ColumnGroupExpandCollapseLayer columnGroupExpandCollapseLayer = |
| new ColumnGroupExpandCollapseLayer(this.columnHideShowLayer); |
| |
| this.selectionLayer = new SelectionLayer(columnGroupExpandCollapseLayer); |
| ViewportLayer viewportLayer = new ViewportLayer(this.selectionLayer); |
| |
| FreezeLayer freezeLayer = new FreezeLayer(this.selectionLayer); |
| CompositeFreezeLayer compositeFreezeLayer = |
| new CompositeFreezeLayer(freezeLayer, viewportLayer, this.selectionLayer); |
| |
| setUnderlyingLayer(compositeFreezeLayer); |
| } |
| |
| public SelectionLayer getSelectionLayer() { |
| return this.selectionLayer; |
| } |
| |
| public SortedList<T> getSortedList() { |
| return this.sortedList; |
| } |
| |
| public FilterList<T> getFilterList() { |
| return this.filterList; |
| } |
| |
| public ListDataProvider<T> getBodyDataProvider() { |
| return this.bodyDataProvider; |
| } |
| |
| public DataLayer getBodyDataLayer() { |
| return this.bodyDataLayer; |
| } |
| |
| public GlazedListsEventLayer<T> getGlazedListsEventLayer() { |
| return this.glazedListsEventLayer; |
| } |
| |
| public ColumnHideShowLayer getColumnHideShowLayer() { |
| return this.columnHideShowLayer; |
| } |
| } |
| |
| /** |
| * The configuration to enable editing of {@link PersonWithAddress} objects. |
| */ |
| class EditConfiguration extends AbstractRegistryConfiguration { |
| |
| @Override |
| public void configureRegistry(IConfigRegistry configRegistry) { |
| configRegistry.registerConfigAttribute( |
| EditConfigAttributes.CELL_EDITABLE_RULE, |
| IEditableRule.ALWAYS_EDITABLE); |
| |
| DefaultIntegerDisplayConverter housenumberConverter = new DefaultIntegerDisplayConverter(); |
| DecimalFormat housenumberFormat = new DecimalFormat(); |
| housenumberFormat.setGroupingUsed(false); |
| housenumberConverter.setNumberFormat(housenumberFormat); |
| |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.DISPLAY_CONVERTER, |
| housenumberConverter, |
| DisplayMode.NORMAL, |
| ColumnLabelAccumulator.COLUMN_LABEL_PREFIX |
| + DataModelConstants.HOUSENUMBER_COLUMN_POSITION); |
| |
| // Gender |
| |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.DISPLAY_CONVERTER, |
| new DisplayConverter() { |
| |
| @Override |
| public Object canonicalToDisplayValue(Object canonicalValue) { |
| return (canonicalValue != null) ? canonicalValue.toString() : ""; |
| } |
| |
| @Override |
| public Object displayToCanonicalValue(Object displayValue) { |
| try { |
| return Gender.valueOf(displayValue.toString()); |
| } catch (IllegalArgumentException e) { |
| return null; |
| } |
| } |
| |
| }, |
| DisplayMode.NORMAL, |
| ColumnLabelAccumulator.COLUMN_LABEL_PREFIX |
| + DataModelConstants.GENDER_COLUMN_POSITION); |
| |
| // Married |
| |
| configRegistry.registerConfigAttribute( |
| EditConfigAttributes.CELL_EDITOR, |
| new CheckBoxCellEditor(), |
| DisplayMode.EDIT, |
| ColumnLabelAccumulator.COLUMN_LABEL_PREFIX |
| + DataModelConstants.MARRIED_COLUMN_POSITION); |
| |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.CELL_PAINTER, |
| new CheckBoxPainter(), |
| DisplayMode.NORMAL, |
| ColumnLabelAccumulator.COLUMN_LABEL_PREFIX |
| + DataModelConstants.MARRIED_COLUMN_POSITION); |
| |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.DISPLAY_CONVERTER, |
| new DefaultBooleanDisplayConverter(), |
| DisplayMode.NORMAL, |
| ColumnLabelAccumulator.COLUMN_LABEL_PREFIX |
| + DataModelConstants.MARRIED_COLUMN_POSITION); |
| |
| } |
| } |
| |
| /** |
| * The configuration to specialize the combobox filter row to mix it with |
| * default filters like free text. |
| */ |
| class FilterRowConfiguration extends AbstractRegistryConfiguration { |
| |
| @Override |
| public void configureRegistry(IConfigRegistry configRegistry) { |
| |
| // ##### |
| // Free Edit Text Filter for Firstname column that supports |
| // wildcards |
| // ##### |
| |
| // register the FilterRowTextCellEditor in the first column which |
| // immediately commits on key press |
| FilterRowTextCellEditor editor = new FilterRowTextCellEditor(); |
| SimpleContentProposalProvider contentProposalProvider = |
| new SimpleContentProposalProvider( |
| CustomFilterRowRegularExpressionConverter.EMPTY_LITERAL, |
| CustomFilterRowRegularExpressionConverter.NOT_EMPTY_LITERAL); |
| contentProposalProvider.setFiltering(true); |
| KeyStroke keystroke = null; |
| try { |
| keystroke = KeyStroke.getInstance("Ctrl+Space"); |
| } catch (ParseException e) { |
| // should not happen as the string is correct |
| } |
| char[] autoActivationChars = ("<").toCharArray(); |
| |
| editor.enableContentProposal( |
| new TextContentAdapter(), |
| contentProposalProvider, |
| keystroke, |
| autoActivationChars); |
| |
| configRegistry.registerConfigAttribute( |
| EditConfigAttributes.CELL_EDITOR, |
| editor, |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.FIRSTNAME_COLUMN_POSITION); |
| |
| // register the FilterRowPainter in the first column to visualize |
| // the free edit filter field as the ComboBoxFilterRowConfiguration |
| // registers the ComboBoxFilterIconPainter as default |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.CELL_PAINTER, |
| new PaddingDecorator(new FilterRowPainter(), 0, 0, 0, 5), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.FIRSTNAME_COLUMN_POSITION); |
| |
| // register display converters for the first column to support an |
| // unidirectional conversion of user friendly strings to complex |
| // regular expressions. |
| |
| // CellConfigAttributes.DISPLAY_CONVERTER is needed for editing. |
| // Using the DefaultDisplayConverter will simply take the entered |
| // value to the data model. That means, the filter row contains |
| // exactly the value that was entered by the user. |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.DISPLAY_CONVERTER, |
| new DefaultDisplayConverter(), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.FIRSTNAME_COLUMN_POSITION); |
| |
| // FilterRowConfigAttributes.FILTER_DISPLAY_CONVERTER is used to |
| // convert the value in the filter row to a filter string. It is |
| // used for the unidirectional conversion of the user value to a |
| // complex regular expression. |
| configRegistry.registerConfigAttribute( |
| FilterRowConfigAttributes.FILTER_DISPLAY_CONVERTER, |
| new CustomFilterRowRegularExpressionConverter(), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.FIRSTNAME_COLUMN_POSITION); |
| |
| // FilterRowConfigAttributes.FILTER_CONTENT_DISPLAY_CONVERTER is |
| // needed to convert the body data. This is necessary as the filter |
| // row does not know about the display converter in the body. If it |
| // is not set it would use the FILTER_DISPLAY_CONVERTER, which would |
| // cause issues in the further processing for the regular expression |
| // conversion. |
| configRegistry.registerConfigAttribute( |
| FilterRowConfigAttributes.FILTER_CONTENT_DISPLAY_CONVERTER, |
| new DefaultDisplayConverter(), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.FIRSTNAME_COLUMN_POSITION); |
| |
| // ##### |
| // Fixed value single-selection combobox filter for Gender column |
| // ##### |
| |
| // register a combo box cell editor for the gender column in the |
| // filter row the label is set automatically to the value of |
| // FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX + column |
| // position |
| ComboBoxCellEditor comboBoxCellEditor = new ComboBoxCellEditor(Arrays.asList( |
| CustomFilterRowRegularExpressionConverter.EMPTY_LITERAL, |
| CustomFilterRowRegularExpressionConverter.NOT_EMPTY_LITERAL, |
| Gender.FEMALE.toString(), |
| Gender.MALE.toString())); |
| configRegistry.registerConfigAttribute( |
| EditConfigAttributes.CELL_EDITOR, |
| comboBoxCellEditor, |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.GENDER_COLUMN_POSITION); |
| |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.CELL_PAINTER, |
| new PaddingDecorator(new FilterRowPainter(), 0, 0, 0, 5), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.GENDER_COLUMN_POSITION); |
| |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.DISPLAY_CONVERTER, |
| new DefaultDisplayConverter(), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.GENDER_COLUMN_POSITION); |
| |
| configRegistry.registerConfigAttribute( |
| FilterRowConfigAttributes.FILTER_DISPLAY_CONVERTER, |
| new CustomFilterRowRegularExpressionConverter(), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.GENDER_COLUMN_POSITION); |
| |
| configRegistry.registerConfigAttribute( |
| FilterRowConfigAttributes.FILTER_CONTENT_DISPLAY_CONVERTER, |
| new DefaultDisplayConverter(), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.GENDER_COLUMN_POSITION); |
| |
| // ##### |
| // Fixed value single-selection combobox filter for Married column |
| // ##### |
| |
| comboBoxCellEditor = new ComboBoxCellEditor(Arrays.asList(Boolean.TRUE, Boolean.FALSE)); |
| configRegistry.registerConfigAttribute( |
| EditConfigAttributes.CELL_EDITOR, |
| comboBoxCellEditor, |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.MARRIED_COLUMN_POSITION); |
| |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.CELL_PAINTER, |
| new PaddingDecorator(new FilterRowPainter(), 0, 0, 0, 5), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.MARRIED_COLUMN_POSITION); |
| |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.DISPLAY_CONVERTER, |
| new DefaultDisplayConverter(), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.MARRIED_COLUMN_POSITION); |
| |
| // ##### |
| // Free Edit Text Filter for Housenumber column that supports |
| // expressions like greater, lesser, equals |
| // ##### |
| |
| configRegistry.registerConfigAttribute( |
| EditConfigAttributes.CELL_EDITOR, |
| new TextCellEditor(), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.HOUSENUMBER_COLUMN_POSITION); |
| |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.CELL_PAINTER, |
| new PaddingDecorator(new FilterRowPainter(), 0, 0, 0, 5), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.HOUSENUMBER_COLUMN_POSITION); |
| |
| configRegistry.registerConfigAttribute( |
| FilterRowConfigAttributes.FILTER_DISPLAY_CONVERTER, |
| new DefaultIntegerDisplayConverter(), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.HOUSENUMBER_COLUMN_POSITION); |
| |
| // register a default display converter to be able to use expression |
| // characters |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.DISPLAY_CONVERTER, |
| new DefaultDisplayConverter(), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.HOUSENUMBER_COLUMN_POSITION); |
| |
| // register a display converter on the filter row in general that |
| // shows a value for an empty entry in the dropdown |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.DISPLAY_CONVERTER, |
| new DefaultDisplayConverter() { |
| @Override |
| public Object canonicalToDisplayValue(Object sourceValue) { |
| return (sourceValue != null && !sourceValue.toString().isEmpty()) |
| ? sourceValue.toString() |
| : CustomFilterRowRegularExpressionConverter.EMPTY_LITERAL; |
| } |
| }, |
| DisplayMode.NORMAL, |
| GridRegion.FILTER_ROW); |
| |
| configRegistry.registerConfigAttribute( |
| FilterRowConfigAttributes.TEXT_DELIMITER, "[&\\|]"); //$NON-NLS-1$ |
| |
| // ##### |
| // Free Edit Text Filter for Birthday column that supports |
| // expressions like greater, lesser, equals |
| // ##### |
| |
| configRegistry.registerConfigAttribute( |
| EditConfigAttributes.CELL_EDITOR, |
| new TextCellEditor(), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.BIRTHDAY_COLUMN_POSITION); |
| |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.CELL_PAINTER, |
| new PaddingDecorator(new FilterRowPainter(), 0, 0, 0, 5), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.BIRTHDAY_COLUMN_POSITION); |
| |
| // register a default display converter to be able to use expression |
| // characters |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.DISPLAY_CONVERTER, |
| new DateThresholdConverter(), |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.BIRTHDAY_COLUMN_POSITION); |
| |
| // register a date converter for the birthday column |
| DefaultDateDisplayConverter converter = new DefaultDateDisplayConverter("dd.MM.yyyy"); |
| configRegistry.registerConfigAttribute( |
| CellConfigAttributes.DISPLAY_CONVERTER, |
| converter, |
| DisplayMode.NORMAL, |
| ColumnLabelAccumulator.COLUMN_LABEL_PREFIX |
| + DataModelConstants.BIRTHDAY_COLUMN_POSITION); |
| |
| // register the same converter in the filter row as content |
| // converter to support text based filtering on formatted Date |
| // objects (e.g. filter for "-08-" to get all birthdays in August) |
| configRegistry.registerConfigAttribute( |
| FilterRowConfigAttributes.FILTER_CONTENT_DISPLAY_CONVERTER, |
| converter, |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX |
| + DataModelConstants.BIRTHDAY_COLUMN_POSITION); |
| |
| } |
| } |
| |
| /** |
| * Special converter that is able to convert simple year based expressions |
| * like > 2000 to a threshold expression that returns all entries that |
| * are after 31.12.2000. |
| */ |
| class DateThresholdConverter extends DefaultDisplayConverter { |
| @Override |
| public Object displayToCanonicalValue(Object displayValue) { |
| if (displayValue != null) { |
| if (displayValue.toString().matches("(\\d){8}")) { |
| String ds = displayValue.toString(); |
| displayValue = ds.substring(0, 2) + "." + ds.substring(2, 4) + "." + ds.substring(4); |
| } |
| |
| ParseResult parse = FilterRowUtils.parseExpression(displayValue.toString()); |
| if (parse.getValueToMatch() != null && parse.getValueToMatch().matches("(\\d){4}")) { |
| switch (parse.getMatchOperation()) { |
| case GREATER_THAN: |
| displayValue = "> 31.12." + parse.getValueToMatch(); |
| break; |
| case GREATER_THAN_OR_EQUAL: |
| displayValue = ">= 31.12." + (Integer.valueOf(parse.getValueToMatch()) - 1); |
| break; |
| case LESS_THAN: |
| displayValue = "< 01.01." + parse.getValueToMatch(); |
| break; |
| case LESS_THAN_OR_EQUAL: |
| displayValue = "<= 01.01." + (Integer.valueOf(parse.getValueToMatch()) + 1); |
| break; |
| case NOT_EQUAL: |
| displayValue = "< 01.01." + parse.getValueToMatch() + " | > 31.12." + parse.getValueToMatch(); |
| break; |
| default: |
| // equal or none |
| displayValue = ">= 01.01." + parse.getValueToMatch() + " & <= 31.12." + parse.getValueToMatch(); |
| break; |
| } |
| } |
| |
| } |
| return super.displayToCanonicalValue(displayValue); |
| } |
| |
| @Override |
| public Object canonicalToDisplayValue(Object sourceValue) { |
| if (sourceValue != null) { |
| if (sourceValue.toString().matches("(\\d){2}\\.(\\d){2}\\.(\\d){4}")) { |
| sourceValue = sourceValue.toString().replace(".", ""); |
| } else { |
| String[] splitted = sourceValue.toString().split("[&\\\\|]"); |
| if (splitted.length > 0) { |
| ParseResult parse = FilterRowUtils.parseExpression(splitted[0]); |
| if (parse.getValueToMatch() != null && parse.getValueToMatch().length() > 6) { |
| String year = parse.getValueToMatch().substring(6); |
| switch (parse.getMatchOperation()) { |
| case GREATER_THAN: |
| sourceValue = "> " + year; |
| break; |
| case GREATER_THAN_OR_EQUAL: |
| if (splitted.length == 1) { |
| sourceValue = ">= " + (Integer.valueOf(year) + 1); |
| } else if (splitted.length == 2) { |
| // equal |
| sourceValue = "= " + year; |
| } |
| break; |
| case LESS_THAN: |
| if (splitted.length == 1) { |
| sourceValue = "< " + year; |
| } else if (splitted.length == 2) { |
| // not equal |
| sourceValue = "<> " + year; |
| } |
| break; |
| case LESS_THAN_OR_EQUAL: |
| sourceValue = "<= " + (Integer.valueOf(year) - 1); |
| break; |
| default: |
| sourceValue = year; |
| break; |
| } |
| } |
| } |
| } |
| } |
| return super.canonicalToDisplayValue(sourceValue); |
| } |
| } |
| |
| /** |
| * Special implementation of a {@link ContextualDisplayConverter} that is |
| * used to convert a filter string with special literals and wildcards |
| * characters to a corresponding complex regular expression. |
| */ |
| class CustomFilterRowRegularExpressionConverter extends ContextualDisplayConverter { |
| |
| static final String EMPTY_LITERAL = "<empty>"; |
| static final String EMPTY_REGEX = "^$"; |
| static final String NOT_EMPTY_LITERAL = "<not_empty>"; |
| static final String NOT_EMPTY_REGEX = "^(?!\\s*$).+"; |
| |
| static final String IGNORE_CASE_MODE_FLAG = "(?i)"; |
| |
| static final String NOT_EQUALS_LITERAL = "<>"; |
| static final String NOT_EQUALS_REGEX_PREFIX = "^((?!"; |
| static final String NOT_EQUALS_REGEX_SUFFIX = ").)*$"; |
| |
| @Override |
| public Object canonicalToDisplayValue(ILayerCell cell, IConfigRegistry configRegistry, Object canonicalValue) { |
| if (canonicalValue != null) { |
| // first convert the wildcards |
| if (canonicalValue != null) { |
| canonicalValue = canonicalValue.toString().replaceAll("\\*", "(.\\*)"); //$NON-NLS-1$ //$NON-NLS-2$ |
| canonicalValue = canonicalValue.toString().replaceAll("\\?", "(.\\?)"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| String cvString = canonicalValue.toString(); |
| |
| if (cvString.contains(EMPTY_LITERAL) |
| || cvString.contains(NOT_EMPTY_LITERAL) |
| || cvString.contains("*") |
| || cvString.contains("?")) { |
| |
| configRegistry.registerConfigAttribute( |
| FilterRowConfigAttributes.TEXT_MATCHING_MODE, |
| TextMatchingMode.REGULAR_EXPRESSION, |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX + cell.getColumnIndex()); |
| |
| // add the ignore case flag to the regex |
| cvString = IGNORE_CASE_MODE_FLAG + cvString; |
| } else { |
| |
| if (cvString.startsWith("=")) { |
| |
| configRegistry.registerConfigAttribute( |
| FilterRowConfigAttributes.TEXT_MATCHING_MODE, |
| TextMatchingMode.EXACT, |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX + cell.getColumnIndex()); |
| |
| cvString = cvString.substring(1).trim(); |
| } else if (cvString.startsWith(NOT_EQUALS_LITERAL)) { |
| configRegistry.registerConfigAttribute( |
| FilterRowConfigAttributes.TEXT_MATCHING_MODE, |
| TextMatchingMode.REGULAR_EXPRESSION, |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX + cell.getColumnIndex()); |
| |
| cvString = IGNORE_CASE_MODE_FLAG + NOT_EQUALS_REGEX_PREFIX + cvString.substring(2).trim() + NOT_EQUALS_REGEX_SUFFIX; |
| } else { |
| // only switch to CONTAINS if RegEx filtering is not |
| // activated |
| configRegistry.registerConfigAttribute( |
| FilterRowConfigAttributes.TEXT_MATCHING_MODE, |
| TextMatchingMode.CONTAINS, |
| DisplayMode.NORMAL, |
| FilterRowDataLayer.FILTER_ROW_COLUMN_LABEL_PREFIX + cell.getColumnIndex()); |
| } |
| } |
| |
| cvString = cvString.replace(EMPTY_LITERAL, EMPTY_REGEX); |
| cvString = cvString.replace(NOT_EMPTY_LITERAL, NOT_EMPTY_REGEX); |
| |
| return cvString; |
| } |
| return canonicalValue; |
| } |
| |
| @Override |
| public Object displayToCanonicalValue(ILayerCell cell, IConfigRegistry configRegistry, Object displayValue) { |
| // empty as never called |
| return null; |
| } |
| } |
| |
| /** |
| * Menu configuration that adds a menu on the body with menu items to |
| * exclude/include items from filtering. |
| * |
| * @param <T> |
| */ |
| class BodyMenuConfiguration<T> extends AbstractUiBindingConfiguration { |
| |
| private static final String EXCLUDE_MENU_ID = "exclude"; |
| private static final String INCLUDE_MENU_ID = "include"; |
| |
| private final Menu bodyMenu; |
| |
| private BodyMenuConfiguration( |
| NatTable natTable, |
| BodyLayerStack<T> bodyLayerStack, |
| IRowIdAccessor<T> rowIdAccessor, |
| IFilterStrategy<T> filterStrategy, |
| FilterRowDataProvider<T> filterRowDataProvider) { |
| |
| this.bodyMenu = new PopupMenuBuilder(natTable) |
| .withMenuItemProvider(EXCLUDE_MENU_ID, new IMenuItemProvider() { |
| |
| @Override |
| public void addMenuItem(NatTable natTable, Menu popupMenu) { |
| MenuItem excludeRow = new MenuItem(popupMenu, SWT.PUSH); |
| excludeRow.setText("Exclude from filter"); |
| excludeRow.setEnabled(true); |
| |
| excludeRow.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent event) { |
| int rowPosition = MenuItemProviders.getNatEventData(event).getRowPosition(); |
| int rowIndex = natTable.getRowIndexByPosition(rowPosition); |
| T rowObject = bodyLayerStack.getBodyDataProvider().getRowObject(rowIndex); |
| Serializable rowId = rowIdAccessor.getRowId(rowObject); |
| _818_SortableAllFilterPerformanceColumnGroupExample.this.filterExcludes.add(rowId); |
| natTable.refresh(false); |
| } |
| }); |
| } |
| }) |
| .withVisibleState(EXCLUDE_MENU_ID, new IMenuItemState() { |
| |
| @Override |
| public boolean isActive(NatEventData natEventData) { |
| int rowPosition = natEventData.getRowPosition(); |
| int rowIndex = natTable.getRowIndexByPosition(rowPosition); |
| T rowObject = bodyLayerStack.getBodyDataProvider().getRowObject(rowIndex); |
| Serializable rowId = rowIdAccessor.getRowId(rowObject); |
| return !_818_SortableAllFilterPerformanceColumnGroupExample.this.filterExcludes.contains(rowId); |
| } |
| }) |
| .withMenuItemProvider(INCLUDE_MENU_ID, new IMenuItemProvider() { |
| |
| @Override |
| public void addMenuItem(NatTable natTable, Menu popupMenu) { |
| MenuItem includeRow = new MenuItem(popupMenu, SWT.PUSH); |
| includeRow.setText("Include to filter"); |
| includeRow.setEnabled(true); |
| |
| includeRow.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent event) { |
| int rowPosition = MenuItemProviders.getNatEventData(event).getRowPosition(); |
| int rowIndex = natTable.getRowIndexByPosition(rowPosition); |
| T rowObject = bodyLayerStack.getBodyDataProvider().getRowObject(rowIndex); |
| Serializable rowId = rowIdAccessor.getRowId(rowObject); |
| _818_SortableAllFilterPerformanceColumnGroupExample.this.filterExcludes.remove(rowId); |
| filterStrategy.applyFilter(filterRowDataProvider.getFilterIndexToObjectMap()); |
| } |
| }); |
| } |
| }) |
| .withVisibleState(INCLUDE_MENU_ID, new IMenuItemState() { |
| |
| @Override |
| public boolean isActive(NatEventData natEventData) { |
| int rowPosition = natEventData.getRowPosition(); |
| int rowIndex = natTable.getRowIndexByPosition(rowPosition); |
| T rowObject = bodyLayerStack.getBodyDataProvider().getRowObject(rowIndex); |
| Serializable rowId = rowIdAccessor.getRowId(rowObject); |
| return _818_SortableAllFilterPerformanceColumnGroupExample.this.filterExcludes.contains(rowId); |
| } |
| }) |
| .withInspectLabelsMenuItem() |
| .build(); |
| } |
| |
| @Override |
| public void configureUiBindings(UiBindingRegistry uiBindingRegistry) { |
| uiBindingRegistry.registerMouseDownBinding( |
| new MouseEventMatcher( |
| SWT.NONE, |
| GridRegion.BODY, |
| MouseEventMatcher.RIGHT_BUTTON), |
| new PopupMenuAction(this.bodyMenu)); |
| } |
| } |
| |
| private List<PersonWithAddress> createAlternativePersons() { |
| List<PersonWithAddress> result = new ArrayList<>(); |
| |
| for (int i = 0; i < 100; i++) { |
| Address address = new Address(); |
| address.setStreet("Evergreen Terrace"); |
| address.setHousenumber(732); |
| address.setPostalCode(54321); |
| address.setCity("Springfield"); |
| result.add(new PersonWithAddress(i, |
| "Ralph", "Wiggum", Gender.MALE, false, new Date(), |
| address)); |
| result.add(new PersonWithAddress(i, |
| "Clancy", "Wiggum", Gender.MALE, true, new Date(), |
| address)); |
| result.add(new PersonWithAddress(i, |
| "Sarah", "Wiggum", Gender.FEMALE, true, new Date(), |
| address)); |
| } |
| |
| for (int i = 400; i < 500; i++) { |
| Address address = new Address(); |
| address.setStreet("Fish Smell Drive"); |
| address.setHousenumber(19); |
| address.setPostalCode(54321); |
| address.setCity("Springfield"); |
| result.add(new PersonWithAddress(i, |
| "Nelson", "Muntz", Gender.MALE, false, new Date(), |
| address)); |
| } |
| |
| return result; |
| } |
| |
| private static List<PersonWithAddress> createPersons(int startId) { |
| List<PersonWithAddress> result = new ArrayList<>(); |
| |
| Address evergreen = new Address(); |
| evergreen.setStreet("Evergreen Terrace"); |
| evergreen.setHousenumber(42); |
| evergreen.setPostalCode(11111); |
| evergreen.setCity("Springfield"); |
| |
| Address south = new Address(); |
| south.setStreet("South Street"); |
| south.setHousenumber(23); |
| south.setPostalCode(22222); |
| south.setCity("Shelbyville"); |
| |
| Address main = new Address(); |
| main.setStreet("Main Street"); |
| main.setHousenumber(4711); |
| main.setPostalCode(33333); |
| main.setCity("Ogdenville"); |
| |
| result.add(new PersonWithAddress( |
| new Person(startId + 1, "Homer", "Simpson", Gender.MALE, true, new Date()), |
| evergreen)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 2, "Homer", "Simpson", Gender.MALE, true, new Date()), |
| evergreen)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 3, "Marge", "Simpson", Gender.FEMALE, true, new Date()), |
| evergreen)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 4, "Marge", "Simpson", Gender.FEMALE, true, new Date()), |
| evergreen)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 5, "Marge", "Simpson", Gender.FEMALE, true, new Date(), null), |
| evergreen)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 6, "Ned", null, Gender.MALE, true, new Date()), |
| evergreen)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 7, "Maude", null, Gender.FEMALE, true, new Date()), |
| evergreen)); |
| |
| result.add(new PersonWithAddress( |
| new Person(startId + 8, "Homer", "Simpson", Gender.MALE, true, new Date()), |
| south)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 9, "Homer", "Simpson", Gender.MALE, true, new Date()), |
| south)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 10, "Homer", "Simpson", Gender.MALE, true, new Date()), |
| south)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 11, "Bart", "Simpson", Gender.MALE, false, new Date()), |
| south)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 12, "Bart", "Simpson", Gender.MALE, false, new Date()), |
| south)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 13, "Bart", "Simpson", Gender.MALE, false, new Date()), |
| south)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 14, "Marge", "Simpson", Gender.FEMALE, true, new Date()), |
| south)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 15, "Marge", "Simpson", Gender.FEMALE, true, new Date()), |
| south)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 16, "Lisa", "Simpson", Gender.FEMALE, false, new Date()), |
| south)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 17, "Lisa", "Simpson", Gender.FEMALE, false, new Date()), |
| south)); |
| |
| result.add(new PersonWithAddress( |
| new Person(startId + 18, "Ned", "Flanders", Gender.MALE, true, new Date()), |
| evergreen)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 19, "Ned", "Flanders", Gender.MALE, true, new Date()), |
| evergreen)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 20, "Maude", "Flanders", Gender.FEMALE, true, new Date()), |
| evergreen)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 21, "Maude", "Flanders", Gender.FEMALE, true, new Date()), |
| evergreen)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 22, "Rod", "Flanders", Gender.MALE, false, new Date()), |
| evergreen)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 23, "Rod", "Flanders", Gender.MALE, false, new Date()), |
| evergreen)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 24, "Tod", "Flanders", Gender.MALE, false, new Date()), |
| evergreen)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 25, "Tod", "Flanders", Gender.MALE, false, new Date()), |
| evergreen)); |
| |
| result.add(new PersonWithAddress( |
| new Person(startId + 26, "Lenny", "Leonard", Gender.MALE, false, new Date()), |
| main)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 27, "Lenny", "Leonard", Gender.MALE, false, new Date()), |
| main)); |
| |
| result.add(new PersonWithAddress( |
| new Person(startId + 28, "Carl", "Carlson", Gender.MALE, false, new Date()), |
| main)); |
| result.add(new PersonWithAddress( |
| new Person(startId + 29, "Carl", "Carlson", Gender.MALE, false, new Date()), |
| main)); |
| |
| result.add(new PersonWithAddress( |
| new Person(startId + 30, "Timothy", "Lovejoy", Gender.MALE, false, new Date()), |
| main)); |
| return result; |
| |
| } |
| |
| } |