nattable.DataGridNatTable.java Source code

Java tutorial

Introduction

Here is the source code for nattable.DataGridNatTable.java

Source

/*******************************************************************************
 * Copyright (c) 2012 Original authors and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Original authors and others - initial API and implementation
 ******************************************************************************/
package nattable;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.eclipse.nebula.widgets.nattable.examples.PersistentNatExampleWrapper;
import static org.eclipse.nebula.widgets.nattable.util.ObjectUtils.isNotNull;

import java.beans.PropertyChangeListener;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.RandomUtils;
import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.blink.BlinkConfigAttributes;
import org.eclipse.nebula.widgets.nattable.blink.BlinkingCellResolver;
import org.eclipse.nebula.widgets.nattable.blink.IBlinkingCellResolver;
import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
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.EditableRule;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.IConfiguration;
import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
import org.eclipse.nebula.widgets.nattable.config.NullComparator;
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
import org.eclipse.nebula.widgets.nattable.data.ReflectiveColumnPropertyAccessor;
import org.eclipse.nebula.widgets.nattable.data.convert.DefaultBooleanDisplayConverter;
import org.eclipse.nebula.widgets.nattable.data.convert.DisplayConverter;
import org.eclipse.nebula.widgets.nattable.data.convert.IDisplayConverter;
import org.eclipse.nebula.widgets.nattable.data.validate.DataValidator;
import org.eclipse.nebula.widgets.nattable.data.validate.DefaultNumericDataValidator;
import org.eclipse.nebula.widgets.nattable.data.validate.IDataValidator;
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
import org.eclipse.nebula.widgets.nattable.edit.action.ToggleCheckBoxColumnAction;
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.ICellEditor;
import org.eclipse.nebula.widgets.nattable.edit.editor.TextCellEditor;
import org.eclipse.nebula.widgets.nattable.examples.AbstractNatExample;
import org.eclipse.nebula.widgets.nattable.examples.fixtures.FullFeaturedBodyLayerStack;
import org.eclipse.nebula.widgets.nattable.examples.fixtures.FullFeaturedColumnHeaderLayerStack;
import org.eclipse.nebula.widgets.nattable.examples.fixtures.GlazedListsGridLayer;
import org.eclipse.nebula.widgets.nattable.examples.fixtures.Person;
import org.eclipse.nebula.widgets.nattable.examples.fixtures.StyledColumnHeaderConfiguration;
import org.eclipse.nebula.widgets.nattable.examples.fixtures.StyledRowHeaderConfiguration;
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
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.data.DefaultSummaryRowHeaderDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultGridLayer;
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.layer.AbstractLayer;
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.layer.cell.ColumnOverrideLabelAccumulator;
import org.eclipse.nebula.widgets.nattable.layer.cell.AggregrateConfigLabelAccumulator;
import org.eclipse.nebula.widgets.nattable.layer.stack.DefaultBodyLayerStack;
import org.eclipse.nebula.widgets.nattable.layer.stack.DummyGridLayerStack;
import org.eclipse.nebula.widgets.nattable.painter.cell.CheckBoxPainter;
import org.eclipse.nebula.widgets.nattable.painter.cell.ColumnHeaderCheckBoxPainter;
import org.eclipse.nebula.widgets.nattable.painter.cell.ComboBoxPainter;
import org.eclipse.nebula.widgets.nattable.painter.cell.ICellPainter;
import org.eclipse.nebula.widgets.nattable.painter.cell.TextPainter;
import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.PaddingDecorator;
import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.BeveledBorderDecorator;
import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.CellPainterDecorator;
import org.eclipse.nebula.widgets.nattable.selection.config.DefaultSelectionStyleConfiguration;
import org.eclipse.nebula.widgets.nattable.sort.SortConfigAttributes;
import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration;
import org.eclipse.nebula.widgets.nattable.style.BorderStyle;
import org.eclipse.nebula.widgets.nattable.style.VerticalAlignmentEnum;
import org.eclipse.nebula.widgets.nattable.style.BorderStyle.LineStyleEnum;
import org.eclipse.nebula.widgets.nattable.style.editor.command.DisplayColumnStyleEditorCommandHandler;
import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
import org.eclipse.nebula.widgets.nattable.style.HorizontalAlignmentEnum;
import org.eclipse.nebula.widgets.nattable.style.Style;
import org.eclipse.nebula.widgets.nattable.test.fixture.data.BlinkingRowDataFixture;
import org.eclipse.nebula.widgets.nattable.test.fixture.data.PricingTypeBean;
import org.eclipse.nebula.widgets.nattable.test.fixture.data.RowDataListFixture;
import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
import org.eclipse.nebula.widgets.nattable.ui.matcher.CellPainterMouseEventMatcher;
import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher;
import org.eclipse.nebula.widgets.nattable.ui.menu.DebugMenuConfiguration;
import org.eclipse.nebula.widgets.nattable.ui.menu.HeaderMenuConfiguration;
import org.eclipse.nebula.widgets.nattable.ui.util.CellEdgeEnum;
import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
import org.eclipse.nebula.widgets.nattable.widget.WaitDialog;
import org.eclipse.nebula.widgets.nattable.grid.layer.config.DefaultRowStyleConfiguration;
import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.graphics.FontData;

import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.FilterList;
import ca.odell.glazedlists.ObservableElementList;
import ca.odell.glazedlists.SortedList;

public class DataGridNatTable extends AbstractNatExample {

    public static final String ASK_PRICE_CONFIG_LABEL = "askPriceConfigLabel";
    public static final String SECURITY_ID_CONFIG_LABEL = "SecurityIdConfigLabel";
    public static final String SECURITY_ID_EDITOR = "SecurityIdEditor";
    public static final String BID_PRICE_CONFIG_LABEL = "bidPriceConfigLabel";
    public static final String LOT_SIZE_CONFIG_LABEL = "lotSizeConfigLabel";
    public static final String SPREAD_CONFIG_LABEL = "spreadConfigLabel";
    private static final String COLUMN_LABEL_1 = "ColumnLabel_1";
    private static final String BODY_LABEL_1 = "BodyLabel_1";
    private static final String CUSTOM_COMPARATOR_LABEL = "customComparatorLabel";
    protected static final String NO_SORT_LABEL = "noSortLabel";

    public static final String FORMAT_DATE_CONFIG_LABEL = "formatDateConfigLabel";
    public static final String FORMAT_DOUBLE_2_PLACES_CONFIG_LABEL = "formatDouble2PlacesConfigLabel";
    public static final String FORMAT_DOUBLE_6_PLACES_CONFIG_LABEL = "formatDouble6PlacesConfigLabel";
    public static final String FORMAT_IN_MILLIONS_CONFIG_LABEL = "formatInMilliosConfigLabel";
    public static final String FORMAT_PRICING_TYPE_CONFIG_LABEL = "formatPricingTypeConfigLabel";

    public static final String ALIGN_CELL_CONTENTS_LEFT_CONFIG_LABEL = "alignCellContentsLeftConfigLabel";
    public static final String ALIGN_CELL_CONTENTS_RIGHT_CONFIG_LABEL = "alignCellContentsRightConfigLabel";

    public static final String CHECK_BOX_CONFIG_LABEL = "checkBox";
    public static final String CHECK_BOX_EDITOR_CONFIG_LABEL = "checkBoxEditor";
    public static final String COMBO_BOX_CONFIG_LABEL = "comboBox";
    public static final String COMBO_BOX_EDITOR_CONFIG_LABEL = "comboBoxEditor";

    private TransformedList rowObjectsGlazedList;
    private NatTable natTable;
    final String[] propertyNames = RowDataListFixture.getPropertyNames();
    final Map propertyToLabelMap = RowDataListFixture.getPropertyToLabelMap();

    private static final String BLINK_UP_CONFIG_LABEL = "blinkUpConfigLabel";
    private static final String BLINK_DOWN_CONFIG_LABEL = "blinkDownConfigLabel";

    private EventList baseEventList;
    private PropertyChangeListener propertyChangeListener;
    private ListDataProvider bodyDataProvider;
    private ScheduledExecutorService scheduledThreadPool;

    public Control createExampleControl(Composite parent) {

        EventList eventList = GlazedLists.eventList(RowDataListFixture.getList());
        rowObjectsGlazedList = GlazedLists.threadSafeList(eventList);

        ConfigRegistry configRegistry = new ConfigRegistry();

        ColumnGroupModel columnGroupModel = new ColumnGroupModel();

        // Body

        LinkedList rowData = new LinkedList();
        baseEventList = GlazedLists.threadSafeList(GlazedLists.eventList(rowData));
        ObservableElementList observableElementList = new ObservableElementList(baseEventList,
                GlazedLists.beanConnector(BlinkingRowDataFixture.class));
        FilterList filterList = new FilterList(observableElementList);
        SortedList sortedList = new SortedList(filterList, null);

        FullFeaturedBodyLayerStack bodyLayer = new FullFeaturedBodyLayerStack(sortedList,
                BlinkingRowDataFixture.rowIdAccessor, propertyNames, configRegistry, columnGroupModel);

        bodyDataProvider = bodyLayer.getBodyDataProvider();
        propertyChangeListener = bodyLayer.getGlazedListEventsLayer();

        //      //    blinking
        registerBlinkingConfigCells(configRegistry);

        // Column header
        FullFeaturedColumnHeaderLayerStack columnHeaderLayer = new FullFeaturedColumnHeaderLayerStack(sortedList,
                filterList, propertyNames, propertyToLabelMap, bodyLayer, bodyLayer.getSelectionLayer(),
                columnGroupModel, configRegistry);

        //      //    column groups
        //      setUpColumnGroups(columnHeaderLayer);

        // Row header
        final DefaultRowHeaderDataProvider rowHeaderDataProvider = new DefaultSummaryRowHeaderDataProvider(
                bodyDataProvider);
        DefaultRowHeaderDataLayer rowHeaderDataLayer = new DefaultRowHeaderDataLayer(rowHeaderDataProvider);
        rowHeaderDataLayer.setDefaultColumnWidth(50);
        ILayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer, bodyLayer, bodyLayer.getSelectionLayer());

        // Corner
        final DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider(
                columnHeaderLayer.getColumnHeaderDataProvider(), rowHeaderDataProvider);
        DataLayer cornerDataLayer = new DataLayer(cornerDataProvider);
        ILayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, columnHeaderLayer);

        // Grid
        GridLayer gridLayer = new GridLayer(bodyLayer, columnHeaderLayer, rowHeaderLayer, cornerLayer);

        DataLayer columnHeaderDataLayer = (DataLayer) columnHeaderLayer.getColumnHeaderDataLayer();
        columnHeaderDataLayer.setConfigLabelAccumulator(new ColumnLabelAccumulator());

        final DataLayer bodyDataLayer = bodyLayer.getBodyDataLayer();
        IDataProvider dataProvider = bodyDataLayer.getDataProvider();

        // NOTE: Register the accumulator on the body data layer.
        // This ensures that the labels are bound to the column index and are
        // unaffected by column order.
        ColumnOverrideLabelAccumulator columnLabelAccumulator = new ColumnOverrideLabelAccumulator(bodyDataLayer);
        ColumnOverrideLabelAccumulator bodyLabelAccumulator = new ColumnOverrideLabelAccumulator(bodyDataLayer);

        bodyDataLayer.setConfigLabelAccumulator(columnLabelAccumulator);

        natTable = new NatTable(parent, gridLayer, false);
        natTable.setConfigRegistry(configRegistry);
        // Add a label for the highlighted column
        // We will add a style for this label to the config registry in a bit
        bodyLabelAccumulator.registerColumnOverrides(2, BODY_LABEL_1);
        columnLabelAccumulator.registerColumnOverrides(2, COLUMN_LABEL_1);

        // Register a command handler for the StyleEditorDialog
        DisplayColumnStyleEditorCommandHandler styleChooserCommandHandler = new DisplayColumnStyleEditorCommandHandler(
                (bodyLayer.getSelectionLayer()), columnLabelAccumulator, natTable.getConfigRegistry());

        bodyLayer.registerCommandHandler(styleChooserCommandHandler);

        // Register the style editor as persistable
        // This will persist the style applied to the columns when
        // NatTable#saveState is invoked
        bodyLayer.registerPersistable(styleChooserCommandHandler);
        bodyLayer.registerPersistable(columnLabelAccumulator);

        addCustomStyling(natTable);

        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());

        // Change the default sort key bindings. Note that 'auto configure' was turned off
        // for the SortHeaderLayer (setup in the GlazedListsGridLayer)
        natTable.addConfiguration(new SingleClickSortConfiguration());
        natTable.addConfiguration(getCustomComparatorConfiguration(columnHeaderDataLayer));

        addColumnHighlight(natTable.getConfigRegistry());

        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());

        natTable.addConfiguration(new HeaderMenuConfiguration(natTable));
        natTable.addConfiguration(editableGridConfiguration(columnLabelAccumulator, dataProvider));

        final ColumnHeaderCheckBoxPainter columnHeaderCheckBoxPainter = new ColumnHeaderCheckBoxPainter(
                bodyDataLayer);
        final ICellPainter column9HeaderPainter = new BeveledBorderDecorator(
                new CellPainterDecorator(new TextPainter(), CellEdgeEnum.RIGHT, columnHeaderCheckBoxPainter));
        natTable.addConfiguration(new AbstractRegistryConfiguration() {
            public void configureRegistry(IConfigRegistry configRegistry) {
                configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, column9HeaderPainter,
                        DisplayMode.NORMAL, ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + 9);
            }

            @Override
            public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
                uiBindingRegistry.registerFirstSingleClickBinding(
                        new CellPainterMouseEventMatcher(GridRegion.COLUMN_HEADER, MouseEventMatcher.LEFT_BUTTON,
                                columnHeaderCheckBoxPainter),
                        new ToggleCheckBoxColumnAction(columnHeaderCheckBoxPainter, bodyDataLayer));
            }
        });
        natTable.addConfiguration(new DebugMenuConfiguration(natTable));

        natTable.configure();

        return natTable;
    }

    //   @Override
    public void onStart() {
        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                scheduledThreadPool = Executors.newScheduledThreadPool(1);
                System.out.println("Starting data load.");
                scheduledThreadPool.schedule(new DataLoader(propertyChangeListener, baseEventList), 100L,
                        TimeUnit.MILLISECONDS);
                //scheduledThreadPool.scheduleAtFixedRate(new DataUpdater(bodyDataProvider), 100L, 5000L, TimeUnit.MILLISECONDS);
            }
        });
    }

    @Override
    public void onStop() {
        if (isNotNull(scheduledThreadPool)) {
            scheduledThreadPool.shutdownNow();
        }
    }

    private void registerBlinkingConfigCells(ConfigRegistry configRegistry) {
        configRegistry.registerConfigAttribute(BlinkConfigAttributes.BLINK_RESOLVER, getBlinkResolver(),
                DisplayMode.NORMAL);

        // Styles
        Style cellStyle = new Style();
        cellStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR, GUIHelper.COLOR_GREEN);
        configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, cellStyle, DisplayMode.NORMAL,
                BLINK_UP_CONFIG_LABEL);

        cellStyle = new Style();
        cellStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR, GUIHelper.COLOR_RED);
        configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, cellStyle, DisplayMode.NORMAL,
                BLINK_DOWN_CONFIG_LABEL);
    }

    private IBlinkingCellResolver getBlinkResolver() {
        return new BlinkingCellResolver() {
            private final String[] configLabels = new String[1];

            public String[] resolve(Object oldValue, Object newValue) {
                double old = ((Double) oldValue).doubleValue();
                double latest = ((Double) newValue).doubleValue();
                configLabels[0] = (latest > old ? BLINK_UP_CONFIG_LABEL : BLINK_DOWN_CONFIG_LABEL);
                return configLabels;
            }
        };
    }

    private final Random random = new Random();
    private static final int DATASET_SIZE = 50;

    /**
     * Initial data load. Loads data in batches.
     *
     *   Note: It is a significantly more efficient to do the load in batches
     *    i.e using {@link List#addAll(java.util.Collection)} instead of adding objects
     *    individually.
     *
     *  @See https://sourceforge.net/projects/nattable/forums/forum/744994/topic/3410065
     */
    class DataLoader implements Runnable {
        private final PropertyChangeListener changeListener;
        private final EventList list;
        private final String waitMsg = "Loading data. Please wait... ";
        private WaitDialog dialog;

        public DataLoader(PropertyChangeListener changeListener, EventList baseEventList) {
            this.changeListener = changeListener;
            this.list = baseEventList;
        }

        public void run() {
            try {

                // Load data
                while (list.size() < DATASET_SIZE) {
                    //   Add to buffer
                    List buffer = new ArrayList();
                    for (int i = 0; i < 100; i++) {
                        buffer.addAll(BlinkingRowDataFixture.getList(changeListener));
                    }
                    //   Load as a batch
                    list.addAll(buffer);
                }
            } catch (Exception e) {
                e.printStackTrace(System.err);
            }
        }
    }

    public Runnable runWithBusyIndicator(final Runnable runnable) {
        return new Runnable() {
            public void run() {
                Display.getDefault().asyncExec(new Runnable() {
                    public void run() {
                        BusyIndicator.showWhile(Display.getDefault(), runnable);
                    }
                });
            }
        };
    }

    /**
     * Pumps data updates to drive blinking
     */
    //   class DataUpdater implements Runnable {
    //      ListDataProvider dataProvider;
    //      int counter = 0;
    //      DataUpdater(ListDataProvider dataProvider) {
    //         this.dataProvider = dataProvider;
    //      }
    //      public void run() {
    //         Display.getDefault().asyncExec(new Runnable() {
    //            public void run() {
    //               for (int i = 0; i < 5; i++) {
    //                  int index = RandomUtils.nextInt(13);
    //                  int nextAsk = random.nextInt(1000);
    //
    //                  if (dataProvider.getRowCount() > index) {
    //                      BlinkingRowDataFixture rowObject = (BlinkingRowDataFixture) dataProvider.getRowObject(index);
    //                      //System.out.println("Ask: "+rowObject.getAsk_price()+" --> "+nextAsk);
    //                      rowObject.setAsk_price(nextAsk);
    //                      rowObject.setBid_price(-1 * nextAsk);
    //                  }
    //               }
    //            }
    //         });
    //      }
    //   }

    /**
     * NOTE: The labels for the the custom comparators must go on the columnHeaderDataLayer - since,
     *       the SortHeaderLayer will resolve cell labels with respect to its underlying layer i.e columnHeaderDataLayer
     */
    private IConfiguration getCustomComparatorConfiguration(final AbstractLayer columnHeaderDataLayer) {

        return new AbstractRegistryConfiguration() {

            public void configureRegistry(IConfigRegistry configRegistry) {
                // Add label accumulator
                ColumnOverrideLabelAccumulator labelAccumulator = new ColumnOverrideLabelAccumulator(
                        columnHeaderDataLayer);
                columnHeaderDataLayer.setConfigLabelAccumulator(labelAccumulator);

                // Register custom comparator
                configRegistry.registerConfigAttribute(SortConfigAttributes.SORT_COMPARATOR, getCustomComparator(),
                        DisplayMode.NORMAL, CUSTOM_COMPARATOR_LABEL);

                // Register null comparator to disable sort
                configRegistry.registerConfigAttribute(SortConfigAttributes.SORT_COMPARATOR, new NullComparator(),
                        DisplayMode.NORMAL, NO_SORT_LABEL);
            }
        };
    }

    private Comparator getCustomComparator() {
        return new Comparator() {
            public int compare(String o1, String o2) {
                return o1.compareToIgnoreCase(o2);
            }

            @Override
            public int compare(Object arg0, Object arg1) {
                // TODO Auto-generated method stub
                return 0;
            }
        };
    };

    /**
     * Register an attribute to be applied to all cells with the highlight label.
     * A similar approach can be used to bind styling to an arbitrary group of cells
     */
    private void addColumnHighlight(IConfigRegistry configRegistry) {
        Style style = new Style();
        style.setAttributeValue(CellStyleAttributes.FOREGROUND_COLOR, GUIHelper.COLOR_BLUE);
        style.setAttributeValue(CellStyleAttributes.HORIZONTAL_ALIGNMENT, HorizontalAlignmentEnum.RIGHT);

        configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, // attribute to apply
                style, // value of the attribute
                DisplayMode.NORMAL, // apply during normal rendering i.e not during selection or edit
                COLUMN_LABEL_1); // apply the above for all cells with this label
    }

    private void addCustomStyling(NatTable natTable) {
        // Setup NatTable default styling

        // NOTE: Getting the colors and fonts from the GUIHelper ensures that
        // they are disposed properly (required by SWT)
        DefaultNatTableStyleConfiguration natTableConfiguration = new DefaultNatTableStyleConfiguration();
        natTableConfiguration.bgColor = GUIHelper.getColor(249, 172, 7);
        natTableConfiguration.fgColor = GUIHelper.getColor(30, 76, 19);
        natTableConfiguration.hAlign = HorizontalAlignmentEnum.LEFT;
        natTableConfiguration.vAlign = VerticalAlignmentEnum.TOP;

        // A custom painter can be plugged in to paint the cells differently
        natTableConfiguration.cellPainter = new PaddingDecorator(new TextPainter(), 1);

        // Setup even odd row colors - row colors override the NatTable default
        // colors
        DefaultRowStyleConfiguration rowStyleConfiguration = new DefaultRowStyleConfiguration();
        rowStyleConfiguration.oddRowBgColor = GUIHelper.getColor(254, 251, 243);
        rowStyleConfiguration.evenRowBgColor = GUIHelper.COLOR_WHITE;

        // Setup selection styling
        DefaultSelectionStyleConfiguration selectionStyle = new DefaultSelectionStyleConfiguration();
        selectionStyle.selectionFont = GUIHelper.getFont(new FontData("Verdana", 8, SWT.NORMAL));
        selectionStyle.selectionBgColor = GUIHelper.getColor(217, 232, 251);
        selectionStyle.selectionFgColor = GUIHelper.COLOR_BLACK;
        selectionStyle.anchorBorderStyle = new BorderStyle(1, GUIHelper.COLOR_DARK_GRAY, LineStyleEnum.SOLID);
        selectionStyle.anchorBgColor = GUIHelper.getColor(65, 113, 43);
        selectionStyle.selectedHeaderBgColor = GUIHelper.getColor(156, 209, 103);

        // Add all style configurations to NatTable
        natTable.addConfiguration(natTableConfiguration);
        natTable.addConfiguration(rowStyleConfiguration);
        natTable.addConfiguration(selectionStyle);

        // Column/Row header style and custom painters
        natTable.addConfiguration(new StyledRowHeaderConfiguration());
        natTable.addConfiguration(new StyledColumnHeaderConfiguration());

        // Add popup menu - build your own popup menu using the PopupMenuBuilder
        natTable.addConfiguration(new HeaderMenuConfiguration(natTable));
    }

    public static AbstractRegistryConfiguration editableGridConfiguration(
            final ColumnOverrideLabelAccumulator columnLabelAccumulator, final IDataProvider dataProvider) {

        return new AbstractRegistryConfiguration() {

            public void configureRegistry(IConfigRegistry configRegistry) {

                DataGridNatTable.registerConfigLabelsOnColumns(columnLabelAccumulator);

                registerISINValidator(configRegistry);
                registerAskPriceValidator(configRegistry, dataProvider);
                registerBidPriceValidator(configRegistry);

                registerSecurityDescriptionCellStyle(configRegistry);
                registerPricingCellStyle(configRegistry);

                registerPriceFormatter(configRegistry);
                registerDateFormatter(configRegistry);
                registerLotSizeFormatter(configRegistry);

                registerCheckBoxEditor(configRegistry, new CheckBoxPainter(), new CheckBoxCellEditor());
                registerComboBox(configRegistry, new ComboBoxPainter(), new ComboBoxCellEditor(
                        Arrays.asList(new PricingTypeBean("MN"), new PricingTypeBean("AT"))));

                registerEditableRules(configRegistry, dataProvider);
            }

        };
    }

    private static void registerConfigLabelsOnColumns(ColumnOverrideLabelAccumulator columnLabelAccumulator) {
        columnLabelAccumulator.registerColumnOverrides(
                RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.SECURITY_ID_PROP_NAME),
                SECURITY_ID_EDITOR, SECURITY_ID_CONFIG_LABEL);

        columnLabelAccumulator.registerColumnOverrides(
                RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.SECURITY_DESCRIPTION_PROP_NAME),
                ALIGN_CELL_CONTENTS_LEFT_CONFIG_LABEL);

        columnLabelAccumulator.registerColumnOverrides(
                RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.ISSUE_DATE_PROP_NAME),
                FORMAT_DATE_CONFIG_LABEL);

        columnLabelAccumulator.registerColumnOverrides(
                RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.PRICING_TYPE_PROP_NAME),
                COMBO_BOX_CONFIG_LABEL, COMBO_BOX_EDITOR_CONFIG_LABEL, FORMAT_PRICING_TYPE_CONFIG_LABEL);

        columnLabelAccumulator.registerColumnOverrides(
                RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.BID_PRICE_PROP_NAME),
                BID_PRICE_CONFIG_LABEL, FORMAT_DOUBLE_6_PLACES_CONFIG_LABEL, FORMAT_DOUBLE_2_PLACES_CONFIG_LABEL,
                ALIGN_CELL_CONTENTS_RIGHT_CONFIG_LABEL);

        columnLabelAccumulator.registerColumnOverrides(
                RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.ASK_PRICE_PROP_NAME),
                ASK_PRICE_CONFIG_LABEL, FORMAT_DOUBLE_6_PLACES_CONFIG_LABEL, FORMAT_DOUBLE_2_PLACES_CONFIG_LABEL,
                ALIGN_CELL_CONTENTS_RIGHT_CONFIG_LABEL);

        columnLabelAccumulator.registerColumnOverrides(
                RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.SPREAD_PROP_NAME),
                SPREAD_CONFIG_LABEL, FORMAT_DOUBLE_6_PLACES_CONFIG_LABEL, ALIGN_CELL_CONTENTS_RIGHT_CONFIG_LABEL);

        columnLabelAccumulator.registerColumnOverrides(
                RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.LOT_SIZE_PROP_NAME),
                LOT_SIZE_CONFIG_LABEL, FORMAT_IN_MILLIONS_CONFIG_LABEL, ALIGN_CELL_CONTENTS_RIGHT_CONFIG_LABEL);

        columnLabelAccumulator.registerColumnOverrides(
                RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.PUBLISH_FLAG_PROP_NAME),
                CHECK_BOX_EDITOR_CONFIG_LABEL, CHECK_BOX_CONFIG_LABEL);
    }

    private static void registerSecurityDescriptionCellStyle(IConfigRegistry configRegistry) {
        Style cellStyle = new Style();
        cellStyle.setAttributeValue(CellStyleAttributes.HORIZONTAL_ALIGNMENT, HorizontalAlignmentEnum.LEFT);
        configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, cellStyle, DisplayMode.NORMAL,
                ALIGN_CELL_CONTENTS_LEFT_CONFIG_LABEL);
    }

    private static void registerPricingCellStyle(IConfigRegistry configRegistry) {
        Style cellStyle = new Style();
        cellStyle.setAttributeValue(CellStyleAttributes.HORIZONTAL_ALIGNMENT, HorizontalAlignmentEnum.RIGHT);
        configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, cellStyle, DisplayMode.NORMAL,
                ALIGN_CELL_CONTENTS_RIGHT_CONFIG_LABEL);
    }

    private static void registerCheckBoxEditor(IConfigRegistry configRegistry, ICellPainter checkBoxCellPainter,
            ICellEditor checkBoxCellEditor) {
        configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, checkBoxCellPainter,
                DisplayMode.NORMAL, CHECK_BOX_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER,
                new DefaultBooleanDisplayConverter(), DisplayMode.NORMAL, CHECK_BOX_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, checkBoxCellEditor,
                DisplayMode.NORMAL, CHECK_BOX_EDITOR_CONFIG_LABEL);
    }

    private static void registerComboBox(IConfigRegistry configRegistry, ICellPainter comboBoxCellPainter,
            ICellEditor comboBoxCellEditor) {
        configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, comboBoxCellPainter,
                DisplayMode.NORMAL, COMBO_BOX_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, comboBoxCellEditor,
                DisplayMode.NORMAL, COMBO_BOX_EDITOR_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, comboBoxCellEditor,
                DisplayMode.EDIT, COMBO_BOX_EDITOR_CONFIG_LABEL);

        configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER,
                PricingTypeBean.getDisplayConverter(), DisplayMode.NORMAL, FORMAT_PRICING_TYPE_CONFIG_LABEL);
    }

    private static void registerISINValidator(IConfigRegistry configRegistry) {

        TextCellEditor textCellEditor = new TextCellEditor();
        textCellEditor.setErrorDecorationEnabled(true);
        textCellEditor
                .setErrorDecorationText("Security Id must be 3 alpha characters optionally followed by numbers");
        textCellEditor.setDecorationPositionOverride(SWT.LEFT | SWT.TOP);
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, textCellEditor, DisplayMode.NORMAL,
                SECURITY_ID_EDITOR);

        configRegistry.registerConfigAttribute(EditConfigAttributes.DATA_VALIDATOR, getSecurtityIdValidator(),
                DisplayMode.EDIT, SECURITY_ID_CONFIG_LABEL);
    }

    private static void registerDateFormatter(IConfigRegistry configRegistry) {
        configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER, getDateFormatter(),
                DisplayMode.NORMAL, FORMAT_DATE_CONFIG_LABEL);
    }

    private static void registerAskPriceValidator(IConfigRegistry configRegistry, IDataProvider dataProvider) {
        configRegistry.registerConfigAttribute(EditConfigAttributes.DATA_VALIDATOR,
                getAskPriceValidator(dataProvider), DisplayMode.EDIT, ASK_PRICE_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(EditConfigAttributes.DATA_VALIDATOR,
                getAskPriceValidator(dataProvider), DisplayMode.NORMAL, ASK_PRICE_CONFIG_LABEL);
    }

    private static void registerBidPriceValidator(IConfigRegistry configRegistry) {
        configRegistry.registerConfigAttribute(EditConfigAttributes.DATA_VALIDATOR,
                new DefaultNumericDataValidator(), DisplayMode.EDIT, BID_PRICE_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(EditConfigAttributes.DATA_VALIDATOR,
                new DefaultNumericDataValidator(), DisplayMode.NORMAL, BID_PRICE_CONFIG_LABEL);
    }

    private static void registerPriceFormatter(IConfigRegistry configRegistry) {
        configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER, getDoubleDisplayConverter(2),
                DisplayMode.NORMAL, FORMAT_DOUBLE_2_PLACES_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER, getDoubleDisplayConverter(6),
                DisplayMode.NORMAL, FORMAT_DOUBLE_6_PLACES_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER, getDoubleDisplayConverter(6),
                DisplayMode.EDIT, FORMAT_DOUBLE_6_PLACES_CONFIG_LABEL);
    }

    private static void registerEditableRules(IConfigRegistry configRegistry, IDataProvider dataProvider) {
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE,
                IEditableRule.ALWAYS_EDITABLE, DisplayMode.EDIT, SECURITY_ID_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE,
                IEditableRule.ALWAYS_EDITABLE, DisplayMode.EDIT, COMBO_BOX_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE,
                IEditableRule.ALWAYS_EDITABLE, DisplayMode.EDIT, CHECK_BOX_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE,
                IEditableRule.ALWAYS_EDITABLE, DisplayMode.EDIT, ALIGN_CELL_CONTENTS_LEFT_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE,
                IEditableRule.ALWAYS_EDITABLE, DisplayMode.EDIT, FORMAT_DATE_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE,
                IEditableRule.ALWAYS_EDITABLE, DisplayMode.EDIT, ALIGN_CELL_CONTENTS_RIGHT_CONFIG_LABEL);

        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE, getEditRule(dataProvider),
                DisplayMode.EDIT, ASK_PRICE_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE, getEditRule(dataProvider),
                DisplayMode.EDIT, BID_PRICE_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE, getEditRule(dataProvider),
                DisplayMode.EDIT, LOT_SIZE_CONFIG_LABEL);
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE,
                IEditableRule.NEVER_EDITABLE, DisplayMode.EDIT, SPREAD_CONFIG_LABEL);
    }

    private static void registerLotSizeFormatter(IConfigRegistry configRegistry) {
        configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER,
                getMillionsDisplayConverter(), DisplayMode.NORMAL, FORMAT_IN_MILLIONS_CONFIG_LABEL);
    }

    /**
     * Edit rule: If publish flag is true - bid, ask and size can be edited.
     */
    private static IEditableRule getEditRule(final IDataProvider dataProvider) {
        return new EditableRule() {
            public boolean isEditable(int columnIndex, int rowIndex) {
                int columnIndexOfPublishFlag = RowDataListFixture
                        .getColumnIndexOfProperty(RowDataListFixture.PUBLISH_FLAG_PROP_NAME);
                return ((Boolean) dataProvider.getDataValue(columnIndexOfPublishFlag, rowIndex)).booleanValue();
            }
        };
    }

    /**
     * Format: Double to a specified number of decimal places
     */
    private static IDisplayConverter getDoubleDisplayConverter(final int decimalPlaces) {
        return new DisplayConverter() {
            NumberFormat numberFormatter = NumberFormat.getInstance();

            public Object canonicalToDisplayValue(Object canonicalValue) {
                if (canonicalValue == null) {
                    return "";
                }
                numberFormatter.setMaximumFractionDigits(decimalPlaces);
                numberFormatter.setMinimumFractionDigits(decimalPlaces);
                return numberFormatter.format(Double.valueOf(canonicalValue.toString()));
            }

            public Object displayToCanonicalValue(Object displayValue) {
                try {
                    return numberFormatter.parse((String) displayValue);
                } catch (ParseException e) {
                    return null;
                }
            }

        };
    }

    /**
     * Format: String date value using a SimpleDateFormat
     */
    private static IDisplayConverter getDateFormatter() {
        return new DisplayConverter() {
            private final SimpleDateFormat dateFormatter = new SimpleDateFormat("MM-dd-yy");

            public Object canonicalToDisplayValue(Object canonicalValue) {
                return dateFormatter.format((Date) canonicalValue);
            }

            public Object displayToCanonicalValue(Object displayValue) {
                return dateFormatter.parse(displayValue.toString(), new ParsePosition(0));
            }
        };
    }

    /**
     * Format: Number as millions (with commas). Convert to int.
     */
    private static IDisplayConverter getMillionsDisplayConverter() {
        return new DisplayConverter() {
            NumberFormat numberFormatter = new DecimalFormat("###,###,###");

            public Object canonicalToDisplayValue(Object canonicalValue) {
                if (canonicalValue == null) {
                    return null;
                }
                return numberFormatter.format(Integer.valueOf(canonicalValue.toString()));
            }

            public Object displayToCanonicalValue(Object displayValue) {
                return (numberFormatter.parse(displayValue.toString(), new ParsePosition(0))).intValue();
            }
        };
    }

    /**
     * Validate: Ask price > Bid Price
     */
    private static IDataValidator getAskPriceValidator(final IDataProvider dataProvider) {

        return new DataValidator() {

            public boolean validate(int columnIndex, int rowIndex, Object newValue) {
                try {
                    int indexOfBidPrice = RowDataListFixture
                            .getColumnIndexOfProperty(RowDataListFixture.BID_PRICE_PROP_NAME);
                    double bidPrice = ((Double) dataProvider.getDataValue(indexOfBidPrice, rowIndex)).doubleValue();
                    double askPrice = Double.valueOf(newValue.toString()).doubleValue();
                    return askPrice > bidPrice;
                } catch (Exception ex) {
                    return false;
                }
            }
        };
    }

    /**
     * Validate: First three chars must be alphabetic and the following must be
     * numeric
     */
    private static IDataValidator getSecurtityIdValidator() {
        return new DataValidator() {

            public boolean validate(int columnIndex, int rowIndex, Object newValue) {
                if (newValue == null) {
                    return false;
                }
                String value = (String) newValue;
                if (value.length() > 3) {
                    String alphabeticPart = value.substring(0, 2);
                    String numericPart = value.substring(3, value.length());
                    return StringUtils.isAlpha(alphabeticPart) && StringUtils.isNumeric(numericPart);
                } else {
                    String alphabeticPart = value.substring(0, value.length());
                    return StringUtils.isAlpha(alphabeticPart);
                }
            }
        };
    }

}