| /******************************************************************************* |
| * Copyright (c) 2012, 2020 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 org.eclipse.nebula.widgets.nattable.summaryrow; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import java.util.Collection; |
| import java.util.Date; |
| import java.util.List; |
| |
| import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry; |
| import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; |
| import org.eclipse.nebula.widgets.nattable.data.ListDataProvider; |
| import org.eclipse.nebula.widgets.nattable.data.ReflectiveColumnPropertyAccessor; |
| import org.eclipse.nebula.widgets.nattable.dataset.fixture.data.PricingTypeBean; |
| import org.eclipse.nebula.widgets.nattable.dataset.fixture.data.RowDataFixture; |
| import org.eclipse.nebula.widgets.nattable.dataset.fixture.data.RowDataListFixture; |
| import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer; |
| import org.eclipse.nebula.widgets.nattable.layer.DataLayer; |
| import org.eclipse.nebula.widgets.nattable.layer.FixedScalingDpiConverter; |
| import org.eclipse.nebula.widgets.nattable.layer.ILayer; |
| import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer; |
| import org.eclipse.nebula.widgets.nattable.layer.LabelStack; |
| import org.eclipse.nebula.widgets.nattable.layer.NoScalingDpiConverter; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.AbstractOverrider; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnOverrideLabelAccumulator; |
| import org.eclipse.nebula.widgets.nattable.layer.command.ConfigureScalingCommand; |
| import org.eclipse.nebula.widgets.nattable.layer.event.CellVisualChangeEvent; |
| import org.eclipse.nebula.widgets.nattable.layer.event.RowInsertEvent; |
| import org.eclipse.nebula.widgets.nattable.reorder.ColumnReorderLayer; |
| import org.eclipse.nebula.widgets.nattable.resize.command.MultiRowResizeCommand; |
| import org.eclipse.nebula.widgets.nattable.resize.command.RowResizeCommand; |
| import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer; |
| import org.eclipse.nebula.widgets.nattable.style.DisplayMode; |
| import org.eclipse.nebula.widgets.nattable.summaryrow.command.CalculateSummaryRowValuesCommand; |
| import org.eclipse.nebula.widgets.nattable.test.fixture.NatTableFixture; |
| import org.eclipse.nebula.widgets.nattable.test.fixture.layer.LayerListenerFixture; |
| import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class SummaryRowIntegrationTest { |
| |
| private ILayer layerStackWithSummary; |
| private NatTableFixture natTable; |
| private List<RowDataFixture> dataList; |
| private SummaryRowLayer summaryRowLayer; |
| |
| private final int askPriceColumnIndex = RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.ASK_PRICE_PROP_NAME); |
| private final int bidPriceColumnIndex = RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.BID_PRICE_PROP_NAME); |
| private final int securityIdColumnIndex = RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.SECURITY_ID_PROP_NAME); |
| private final int lotSizeColumnIndex = RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.LOT_SIZE_PROP_NAME); |
| private final int ratingColumnIndex = RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.RATING_PROP_NAME); |
| private DataLayer dataLayer; |
| private ListDataProvider<RowDataFixture> dataProvider; |
| |
| @Before |
| public void initLayerStackWithSummaryRow() { |
| this.dataList = RowDataListFixture.getList().subList(0, 4); |
| // Rows 0, 1, 2, 3; Summary row would be position 4 |
| assertEquals(4, this.dataList.size()); |
| |
| this.dataProvider = new ListDataProvider<>( |
| this.dataList, |
| new ReflectiveColumnPropertyAccessor<RowDataFixture>(RowDataListFixture.getPropertyNames())); |
| |
| IConfigRegistry configRegistry = new ConfigRegistry(); |
| |
| this.dataLayer = new DataLayer(this.dataProvider); |
| this.summaryRowLayer = new SummaryRowLayer(this.dataLayer, configRegistry); |
| IUniqueIndexLayer columnReorderLayer = new ColumnReorderLayer(this.summaryRowLayer); |
| IUniqueIndexLayer columnHideShowLayer = new ColumnHideShowLayer(columnReorderLayer); |
| IUniqueIndexLayer selectionLayer = new SelectionLayer(columnHideShowLayer); |
| this.layerStackWithSummary = new ViewportLayer(selectionLayer); |
| |
| // NatTableFixture initializes the client area |
| this.natTable = new NatTableFixture(this.layerStackWithSummary, false); |
| this.natTable.setConfigRegistry(configRegistry); |
| this.natTable.addConfiguration(new TestSummaryRowConfiguration()); |
| |
| this.natTable.configure(); |
| |
| this.natTable.doCommand(new ConfigureScalingCommand(new NoScalingDpiConverter())); |
| } |
| |
| @Test |
| public void shouldAddExtraRowAtTheEnd() { |
| assertEquals(5, this.natTable.getRowCount()); |
| } |
| |
| @Test |
| public void shouldReturnDefaultValueImmediately() { |
| // First invocation triggers the summary calculation in a separate |
| // thread |
| Object askPriceSummary = this.natTable.getDataValueByPosition(this.askPriceColumnIndex, 4); |
| assertNull(askPriceSummary); |
| } |
| |
| @Test |
| public void shouldSummarizeAskPriceColumn() throws Exception { |
| // First invocation triggers the summary calculation in a separate |
| // thread |
| Object askPriceSummary = this.natTable.getDataValueByPosition(this.askPriceColumnIndex, 4); |
| assertNull(askPriceSummary); |
| |
| Thread.sleep(200); |
| |
| askPriceSummary = this.natTable.getDataValueByPosition(this.askPriceColumnIndex, 4); |
| assertEquals("110.0", askPriceSummary.toString()); |
| } |
| |
| @Test |
| public void shouldSummarizeAskPriceColumnImmediatelyOnPreCalculation() { |
| // Trigger summary calculation via command |
| this.natTable.doCommand(new CalculateSummaryRowValuesCommand()); |
| |
| Object askPriceSummary = this.natTable.getDataValueByPosition(this.askPriceColumnIndex, 4); |
| assertEquals("110.0", askPriceSummary.toString()); |
| } |
| |
| @Test |
| public void defaultHandlingOfNonNumericColumns() { |
| // Non numeric field |
| Object isinSummary = this.natTable.getDataValueByPosition(this.securityIdColumnIndex, 4); |
| assertNull(isinSummary); |
| |
| // Summary provider turned off |
| Object bidPriceSummary = this.natTable.getDataValueByPosition(this.bidPriceColumnIndex, 4); |
| assertNull(bidPriceSummary); |
| } |
| |
| @Test |
| public void shouldFireCellVisualChangeEventOnceSummaryIsCalculated() throws Exception { |
| // need to resize because otherwise the ViewportLayer would not process |
| // the CellVisualChangeEvent any further |
| this.natTable.setSize(800, 400); |
| |
| LayerListenerFixture listener = new LayerListenerFixture(); |
| this.natTable.addLayerListener(listener); |
| |
| // Trigger summary calculation |
| this.natTable.getDataValueByPosition(this.askPriceColumnIndex, 4); |
| |
| Thread.sleep(500); |
| |
| assertTrue(listener.containsInstanceOf(CellVisualChangeEvent.class)); |
| CellVisualChangeEvent event = (CellVisualChangeEvent) listener.getReceivedEvents().get(0); |
| |
| assertEquals(this.askPriceColumnIndex, event.getColumnPosition()); |
| assertEquals(4, event.getRowPosition()); |
| |
| Collection<Rectangle> changedPositionRectangles = event.getChangedPositionRectangles(); |
| assertEquals(1, changedPositionRectangles.size()); |
| |
| // only the cell gets updated |
| Rectangle rectangle = changedPositionRectangles.iterator().next(); |
| assertEquals(6, rectangle.x); |
| assertEquals(4, rectangle.y); |
| assertEquals(1, rectangle.width); |
| assertEquals(1, rectangle.height); |
| } |
| |
| @Test |
| public void rowAddShouldClearCacheAndCalculateNewSummary() throws Exception { |
| // Trigger summary calculation |
| Object askPriceSummary = this.natTable.getDataValueByPosition(this.askPriceColumnIndex, 4); |
| assertNull(askPriceSummary); |
| |
| Thread.sleep(100); |
| |
| // Verify calculated summary value |
| askPriceSummary = this.natTable.getDataValueByPosition(this.askPriceColumnIndex, 4); |
| assertEquals("110.0", askPriceSummary.toString()); |
| |
| // Add data and fire event |
| this.dataList.add(new RowDataFixture("SID", "SDesc", "A", new Date(), |
| new PricingTypeBean("MN"), 2.0, 2.1, 100, true, 3.0, 1.0, 1.0, |
| 1000, 100000, 50000)); |
| this.dataLayer.fireLayerEvent(new RowInsertEvent(this.dataLayer, 4)); |
| |
| // Trigger summary calculation - on the new summary row |
| askPriceSummary = this.natTable.getDataValueByPosition(this.askPriceColumnIndex, 5); |
| assertEquals("110.0", askPriceSummary.toString()); |
| |
| Thread.sleep(100); |
| |
| // Verify summary value is REcalculated |
| askPriceSummary = this.natTable.getDataValueByPosition(this.askPriceColumnIndex, 5); |
| assertEquals("112.1", askPriceSummary.toString()); |
| } |
| |
| @Test |
| public void getRowIndexByPositionForSummaryRow() { |
| assertEquals(4, this.natTable.getRowIndexByPosition(4)); |
| } |
| |
| @Test |
| public void shouldGetRowHeightByPositionForSummaryRow() { |
| assertEquals(20, this.natTable.getRowHeightByPosition(4)); |
| } |
| |
| @Test |
| public void shouldResizeSummaryRow() { |
| this.natTable.doCommand(new RowResizeCommand(this.natTable, 4, 100)); |
| assertEquals(100, this.natTable.getRowHeightByPosition(4)); |
| } |
| |
| @Test |
| public void shouldResizeSummaryRowWithoutDownScale() { |
| this.natTable.doCommand(new ConfigureScalingCommand(new FixedScalingDpiConverter(120))); |
| |
| // scaling enabled, therefore default height of 20 pixels is up scaled |
| // to 25 |
| assertEquals(25, this.dataLayer.getRowHeightByPosition(4)); |
| |
| RowResizeCommand rowResizeCommand = new RowResizeCommand(this.natTable, 4, 50); |
| this.natTable.doCommand(rowResizeCommand); |
| |
| // command executed with down scaling disabled, therefore set height 50 |
| // is up scaled to 63 |
| assertEquals(63, this.natTable.getRowHeightByPosition(4)); |
| } |
| |
| @Test |
| public void shouldResizeSummaryRowWithDownScale() { |
| this.natTable.doCommand(new ConfigureScalingCommand(new FixedScalingDpiConverter(120))); |
| |
| // scaling enabled, therefore default height of 20 pixels is up scaled |
| // to 25 |
| assertEquals(25, this.dataLayer.getRowHeightByPosition(4)); |
| |
| RowResizeCommand rowResizeCommand = new RowResizeCommand(this.natTable, 4, 50, true); |
| this.natTable.doCommand(rowResizeCommand); |
| |
| // command executed with down scaling enabled, therefore set height 50 |
| // is first down scaled on setting the value and then up scaled to 50 |
| // again on accessing the height |
| assertEquals(50, this.natTable.getRowHeightByPosition(4)); |
| } |
| |
| @Test |
| public void shouldResizeSummaryRowViaMultiRowResize() { |
| this.natTable.doCommand(new MultiRowResizeCommand(this.natTable, new int[] { 4 }, 100)); |
| assertEquals(100, this.natTable.getRowHeightByPosition(4)); |
| } |
| |
| @Test |
| public void shouldHandleMultiRowResizeForSummaryRowAndOthers() { |
| this.natTable.doCommand(new MultiRowResizeCommand(this.natTable, new int[] { 0, 2, 4 }, new int[] { 40, 60, 100 })); |
| assertEquals(40, this.natTable.getRowHeightByPosition(0)); |
| assertEquals(20, this.natTable.getRowHeightByPosition(1)); |
| assertEquals(60, this.natTable.getRowHeightByPosition(2)); |
| assertEquals(20, this.natTable.getRowHeightByPosition(3)); |
| assertEquals(100, this.natTable.getRowHeightByPosition(4)); |
| } |
| |
| @Test |
| public void shouldHandleMultiRowResizeForSummaryRowAndOthersWithoutDownScale() { |
| this.natTable.doCommand(new ConfigureScalingCommand(new FixedScalingDpiConverter(120))); |
| |
| this.natTable.doCommand(new MultiRowResizeCommand(this.natTable, new int[] { 0, 2, 4 }, new int[] { 40, 60, 100 })); |
| |
| assertEquals(50, this.natTable.getRowHeightByPosition(0)); |
| assertEquals(25, this.natTable.getRowHeightByPosition(1)); |
| assertEquals(75, this.natTable.getRowHeightByPosition(2)); |
| assertEquals(25, this.natTable.getRowHeightByPosition(3)); |
| assertEquals(125, this.natTable.getRowHeightByPosition(4)); |
| } |
| |
| @Test |
| public void shouldHandleMultiRowResizeForSummaryRowAndOthersWithDownScale() { |
| this.natTable.doCommand(new ConfigureScalingCommand(new FixedScalingDpiConverter(120))); |
| |
| this.natTable.doCommand(new MultiRowResizeCommand(this.natTable, new int[] { 0, 2, 4 }, new int[] { 40, 60, 100 }, true)); |
| |
| // resize operation was downscaled |
| assertEquals(40, this.natTable.getRowHeightByPosition(0)); |
| // default value is upscaled |
| assertEquals(25, this.natTable.getRowHeightByPosition(1)); |
| // resize operation was downscaled |
| assertEquals(60, this.natTable.getRowHeightByPosition(2)); |
| // default value is upscaled |
| assertEquals(25, this.natTable.getRowHeightByPosition(3)); |
| // resize operation was downscaled |
| assertEquals(100, this.natTable.getRowHeightByPosition(4)); |
| } |
| |
| @Test |
| public void defaultConfigLabelsAreApplied() { |
| LabelStack configLabels = this.natTable.getConfigLabelsByPosition(0, 4); |
| |
| assertEquals(2, configLabels.size()); |
| assertEquals(SummaryRowLayer.DEFAULT_SUMMARY_COLUMN_CONFIG_LABEL_PREFIX + 0, configLabels.get(0)); |
| assertEquals(SummaryRowLayer.DEFAULT_SUMMARY_ROW_CONFIG_LABEL, configLabels.get(1)); |
| } |
| |
| @Test |
| public void defaultConfigLabelsAreAdded() { |
| ColumnOverrideLabelAccumulator labelAcc = new ColumnOverrideLabelAccumulator(this.layerStackWithSummary); |
| labelAcc.registerColumnOverrides(0, "myLabel"); |
| ((ViewportLayer) this.layerStackWithSummary).setConfigLabelAccumulator(labelAcc); |
| |
| LabelStack configLabels = this.natTable.getConfigLabelsByPosition(0, 4); |
| |
| assertEquals(3, configLabels.size()); |
| assertEquals(SummaryRowLayer.DEFAULT_SUMMARY_COLUMN_CONFIG_LABEL_PREFIX + 0, configLabels.get(0)); |
| assertEquals(SummaryRowLayer.DEFAULT_SUMMARY_ROW_CONFIG_LABEL, configLabels.get(1)); |
| assertEquals("myLabel", configLabels.get(2)); |
| } |
| |
| @Test |
| public void defaultConfigLabelsNotAddedForLayersBelow() throws Exception { |
| // the AbstractOverrider is set on the DataLayer. So on retrieving the |
| this.dataLayer.setConfigLabelAccumulator(new AbstractOverrider() { |
| @Override |
| public void accumulateConfigLabels(LabelStack configLabels, int columnPosition, int rowPosition) { |
| RowDataFixture rowObject = SummaryRowIntegrationTest.this.dataProvider.getRowObject(rowPosition); |
| configLabels.addLabel("myLabel " + rowObject.security_id); |
| } |
| }); |
| |
| LabelStack configLabels = this.natTable.getConfigLabelsByPosition(0, 4); |
| |
| assertEquals(2, configLabels.size()); |
| assertEquals(SummaryRowLayer.DEFAULT_SUMMARY_COLUMN_CONFIG_LABEL_PREFIX + 0, configLabels.get(0)); |
| assertEquals(SummaryRowLayer.DEFAULT_SUMMARY_ROW_CONFIG_LABEL, configLabels.get(1)); |
| |
| configLabels = this.natTable.getConfigLabelsByPosition(0, 3); |
| |
| assertEquals(1, configLabels.size()); |
| assertTrue("Label in default body does not start with myLabel", configLabels.get(0).startsWith("myLabel")); |
| } |
| |
| @Test |
| public void shouldSumUpAllRowsWithAAARating() throws Exception { |
| // Trigger summary calculation |
| Object lotSizeSummary = this.natTable.getDataValueByPosition(this.lotSizeColumnIndex, 4); |
| assertNull(lotSizeSummary); |
| |
| Thread.sleep(100); |
| |
| // Verify calculated summary value |
| lotSizeSummary = this.natTable.getDataValueByPosition(this.lotSizeColumnIndex, 4); |
| assertEquals("10000", lotSizeSummary.toString()); |
| } |
| |
| /** |
| * Sets up: Summary for the ask price No summary for the others |
| */ |
| class TestSummaryRowConfiguration extends DefaultSummaryRowConfiguration { |
| @Override |
| public void addSummaryProviderConfig(IConfigRegistry configRegistry) { |
| // Add summaries to ask price column |
| configRegistry.registerConfigAttribute( |
| SummaryRowConfigAttributes.SUMMARY_PROVIDER, |
| new SummationSummaryProvider(SummaryRowIntegrationTest.this.dataProvider), |
| DisplayMode.NORMAL, |
| SummaryRowLayer.DEFAULT_SUMMARY_COLUMN_CONFIG_LABEL_PREFIX |
| + SummaryRowIntegrationTest.this.askPriceColumnIndex); |
| |
| // Add lot size if the rating is 'AAA' |
| configRegistry.registerConfigAttribute( |
| SummaryRowConfigAttributes.SUMMARY_PROVIDER, |
| getTestLotSizeSummaryProvider(), DisplayMode.NORMAL, |
| SummaryRowLayer.DEFAULT_SUMMARY_COLUMN_CONFIG_LABEL_PREFIX |
| + SummaryRowIntegrationTest.this.lotSizeColumnIndex); |
| |
| // No Summary by default |
| configRegistry.registerConfigAttribute( |
| SummaryRowConfigAttributes.SUMMARY_PROVIDER, |
| ISummaryProvider.NONE, DisplayMode.NORMAL, |
| SummaryRowLayer.DEFAULT_SUMMARY_ROW_CONFIG_LABEL); |
| } |
| |
| private ISummaryProvider getTestLotSizeSummaryProvider() { |
| return new ISummaryProvider() { |
| @Override |
| public Object summarize(int columnIndex) { |
| int lotSizeSummary = 0; |
| int rowCount = SummaryRowIntegrationTest.this.dataProvider.getRowCount(); |
| |
| for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) { |
| String lotSize = SummaryRowIntegrationTest.this.dataProvider |
| .getDataValue(columnIndex, rowIndex).toString(); |
| String rating = SummaryRowIntegrationTest.this.dataProvider |
| .getDataValue(SummaryRowIntegrationTest.this.ratingColumnIndex, rowIndex).toString(); |
| |
| if ("AAA".equals(rating)) { |
| lotSizeSummary = lotSizeSummary + Integer.parseInt(lotSize); |
| } |
| } |
| return lotSizeSummary; |
| } |
| }; |
| } |
| } |
| } |