diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/qa/complex/toolkit/GridControl.java | 746 |
1 files changed, 746 insertions, 0 deletions
diff --git a/toolkit/qa/complex/toolkit/GridControl.java b/toolkit/qa/complex/toolkit/GridControl.java new file mode 100644 index 000000000..b2338ee68 --- /dev/null +++ b/toolkit/qa/complex/toolkit/GridControl.java @@ -0,0 +1,746 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +package complex.toolkit; + +import com.sun.star.awt.XControl; +import com.sun.star.awt.XControlContainer; +import com.sun.star.awt.XControlModel; +import com.sun.star.awt.XToolkit; +import com.sun.star.awt.grid.DefaultGridDataModel; +import com.sun.star.awt.grid.XGridColumn; +import com.sun.star.awt.grid.XGridColumnModel; +import com.sun.star.awt.grid.XGridControl; +import com.sun.star.awt.grid.XGridDataModel; +import com.sun.star.awt.grid.XMutableGridDataModel; +import com.sun.star.awt.grid.XSortableMutableGridDataModel; +import com.sun.star.beans.Pair; +import com.sun.star.beans.XPropertySet; +import com.sun.star.container.ContainerEvent; +import com.sun.star.container.XContainerListener; +import com.sun.star.container.XNameContainer; +import com.sun.star.lang.EventObject; +import com.sun.star.lang.IndexOutOfBoundsException; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XEventListener; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.Exception; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +import com.sun.star.uno.XInterface; +import com.sun.star.util.XCloneable; +import complex.toolkit.awtgrid.DummyColumn; +import complex.toolkit.awtgrid.TMutableGridDataModel; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; +import org.openoffice.test.OfficeConnection; + +/** is a unit test for the grid control related implementations + */ +public class GridControl +{ + + public GridControl() + { + m_context = m_connection.getComponentContext(); + } + + + private static void impl_dispose( final Object... i_components ) + { + for ( int i=0; i<i_components.length; ++i ) + { + if ( i_components[i] != null ) + { + final XComponent component = UnoRuntime.queryInterface( XComponent.class, i_components[i] ); + component.dispose(); + } + } + } + + + private void impl_recreateGridModel() throws Exception + { + impl_dispose( m_gridControlModel, m_columnModel, m_dataModel ); + + // create a grid control model, and ensure it has a proper data and column model already + m_gridControlModel = UnoRuntime.queryInterface( XPropertySet.class, + createInstance( "com.sun.star.awt.grid.UnoControlGridModel" ) ); + assertNotNull( "grid control model does not provide XPropertySet interface", m_gridControlModel ); + + // ensure that the model has default column/data models + m_columnModel = UnoRuntime.queryInterface( XGridColumnModel.class, m_gridControlModel.getPropertyValue( "ColumnModel" ) ); + assertNotNull( "the control model is expected to have an initial column model", m_columnModel ); + final XGridDataModel dataModel = UnoRuntime.queryInterface( XGridDataModel.class, m_gridControlModel.getPropertyValue( "GridDataModel" ) ); + assertNotNull( "the control model is expected to have an initial data model", dataModel ); + m_dataModel = UnoRuntime.queryInterface( XSortableMutableGridDataModel.class, + dataModel ); + assertNotNull( "the out-of-the-box data model should be mutable and sortable", m_dataModel ); + } + + + @Test + public void testGridControlCloning() throws Exception + { + impl_recreateGridModel(); + + // give the test something to compare, actually + XGridColumnModel columnModel = UnoRuntime.queryInterface( XGridColumnModel.class, + m_gridControlModel.getPropertyValue( "ColumnModel" ) ); + columnModel.setDefaultColumns( 10 ); + + // clone the grid model + final XCloneable cloneable = UnoRuntime.queryInterface( XCloneable.class, m_gridControlModel ); + assertNotNull( "all UnoControlModel's are expected to be clonable", cloneable ); + + final XInterface clone = cloneable.createClone(); + final XPropertySet clonedProps = UnoRuntime.queryInterface( XPropertySet.class, clone ); + + // TODO: check all those generic properties for equality + + // the data model and the column model should have been cloned, too + // in particular, the clone should not share the sub models with the original + final XMutableGridDataModel originalDataModel = UnoRuntime.queryInterface( XMutableGridDataModel.class, + m_gridControlModel.getPropertyValue( "GridDataModel" ) ); + final XMutableGridDataModel clonedDataModel = UnoRuntime.queryInterface( XMutableGridDataModel.class, + clonedProps.getPropertyValue( "GridDataModel" ) ); + assertFalse( "data model should not be shared after cloning", UnoRuntime.areSame( originalDataModel, clonedDataModel ) ); + impl_assertEquality( originalDataModel, clonedDataModel ); + + final XGridColumnModel originalColumnModel = columnModel; + final XGridColumnModel clonedColumnModel = UnoRuntime.queryInterface( XGridColumnModel.class, + clonedProps.getPropertyValue( "ColumnModel" ) ); + assertFalse( "column model should not be shared after cloning", UnoRuntime.areSame( originalColumnModel, clonedColumnModel ) ); + impl_assertEquality( originalColumnModel, clonedColumnModel ); + } + + + @Test + public void testDisposal() throws Exception + { + impl_recreateGridModel(); + + final int columnCount = 3; + m_columnModel.setDefaultColumns( columnCount ); + + // add disposal listeners to all columns so far + final XGridColumn[] columns = m_columnModel.getColumns(); + assertEquals( "creating default columns resulted in unexpected column count", columnCount, columns.length ); + final DisposeListener[] columnListeners = new DisposeListener[columnCount]; + for ( int i=0; i<columnCount; ++i ) + columnListeners[i] = new DisposeListener( columns[i] ); + + // add another column, and check that upon removal, it is disposed + final int newColumnIndex = m_columnModel.addColumn( m_columnModel.createColumn() ); + final DisposeListener columnListener = new DisposeListener( m_columnModel.getColumn( newColumnIndex ) ); + m_columnModel.removeColumn( newColumnIndex ); + assertTrue( "explicit column removal is expected to dispose the column", columnListener.isDisposed() ); + + // by definition, the grid control model is the owner of both the column and the data model. So, setting + // a new column/data model should implicitly dispose the old models + final DisposeListener oldDataModelListener = new DisposeListener( m_dataModel ); + final DisposeListener oldColumnModelListener = new DisposeListener( m_columnModel ); + + final Object newDataModel = createInstance( "com.sun.star.awt.grid.DefaultGridDataModel" ); + final Object newColumnModel = createInstance( "com.sun.star.awt.grid.DefaultGridColumnModel" ); + final DisposeListener newDataModelListener = new DisposeListener( newDataModel ); + final DisposeListener newColumnModelListener = new DisposeListener( newColumnModel ); + + m_gridControlModel.setPropertyValue( "GridDataModel", newDataModel ); + assertTrue( "setting a new data model failed", impl_areSameInterface( newDataModel, m_gridControlModel.getPropertyValue( "GridDataModel" ) ) ); + m_gridControlModel.setPropertyValue( "ColumnModel", newColumnModel ); + assertTrue( "setting a new column model failed", impl_areSameInterface( newColumnModel, m_gridControlModel.getPropertyValue( "ColumnModel" ) ) ); + + assertTrue( "old data model has not been disposed", oldDataModelListener.isDisposed() ); + assertTrue( "old column model has not been disposed", oldColumnModelListener.isDisposed() ); + for ( int i=0; i<columnCount; ++i ) + assertTrue( "column no. " + i + " has not been disposed", columnListeners[i].isDisposed() ); + + // the same holds if the grid control model itself is disposed - it should dispose the depending models, too + assertFalse( "new data model is already disposed - this is unexpected", newDataModelListener.isDisposed() ); + assertFalse( "new column model is already disposed - this is unexpected", newColumnModelListener.isDisposed() ); + impl_dispose( m_gridControlModel ); + assertTrue( "new data model is not disposed after disposing the grid column model", newDataModelListener.isDisposed() ); + assertTrue( "new column model is not disposed after disposing the grid column model", newColumnModelListener.isDisposed() ); + } + + + /** + * tests various aspects of the <code>XMutableGridDataModel</code> interface + */ + @Test + public void testMutableGridDataModel() throws Exception + { + impl_recreateGridModel(); + + TMutableGridDataModel test = new TMutableGridDataModel( m_dataModel ); + test.testAddRow(); + test.testAddRows(); + test.testInsertRow(); + test.testInsertRows(); + test.testRemoveRow(); + test.testRemoveAllRows(); + test.testUpdateCellData(); + test.testUpdateRowData(); + test.testUpdateRowHeading(); + test.cleanup(); + + // a somewhat less straight-forward test: the data model is expected to implicitly increase its column count + // when you add a row which has more columns than currently known + final XMutableGridDataModel dataModel = DefaultGridDataModel.create( m_context ); + dataModel.addRow( 0, new Object[] { 1 } ); + assertEquals( "unexpected column count after adding the most simple row", 1, dataModel.getColumnCount() ); + dataModel.addRow( 1, new Object[] { 1, 2 } ); + assertEquals( "implicit extension of the column count doesn't work", 2, dataModel.getColumnCount() ); + } + + + @Test + public void testGridColumnModel() throws Exception + { + impl_recreateGridModel(); + + ColumnModelListener listener = new ColumnModelListener(); + m_columnModel.addContainerListener( listener ); + + // insert default columns into the previously empty model, ensure we get the right notifications + final int defaultColumnsCount = 3; + m_columnModel.setDefaultColumns( defaultColumnsCount ); + impl_assertColumnModelConsistency(); + List< ContainerEvent > events = listener.assertExclusiveInsertionEvents(); + listener.reset(); + assertEquals( "wrong number of events fired by setDefaultColumns", defaultColumnsCount, events.size() ); + for ( int i=0; i<defaultColumnsCount; ++i ) + { + final ContainerEvent event = events.get(i); + final int index = impl_assertInteger( event.Accessor ); + assertEquals( "unexpected Accessor value in insert notification", i, index ); + assertTrue( "wrong column object notified in insert notification", + impl_areSameInterface( event.Element, m_columnModel.getColumn(i) ) ); + } + + // insert some more default columns, ensure that all previously existing columns are removed + final int moreDefaultColumnsCount = 5; + m_columnModel.setDefaultColumns( moreDefaultColumnsCount ); + impl_assertColumnModelConsistency(); + assertEquals( "setting default columns is expected to remove all previously existing columns", + moreDefaultColumnsCount, m_columnModel.getColumnCount() ); + + // in this situation, both removal and insertion events have been notified + final List< ContainerEvent > removalEvents = listener.getRemovalEvents(); + final List< ContainerEvent > insertionEvents = listener.getInsertionEvents(); + listener.reset(); + + // for the removal events, check the indexes + assertEquals( "wrong number of columns removed (or notified) upon setting default columns", + defaultColumnsCount, removalEvents.size() ); + for ( int i=0; i<removalEvents.size(); ++i ) + { + final ContainerEvent event = removalEvents.get(i); + final int removedIndex = impl_assertInteger( event.Accessor ); + + // The implementation is allowed to remove the columns from the beginning, in which case the + // index of the removed column must always be 0, since e.g. the second column has index 0 + // after the first column (which previously had index 0) had been removed. + // Alternatively, the implementation is allowed to remove columns from the end, which means + // that the column index given in the event is steadily increasing. + assertTrue( "unexpected column removal event column index", + ( removedIndex == 0 ) || ( removedIndex == removalEvents.size() - 1 - i ) ); + } + + // for the insertion events, check the indexes as well + assertEquals( "wrong number of insertion events when setting default columns over existing columns", + moreDefaultColumnsCount, insertionEvents.size() ); + for ( int i=0; i<insertionEvents.size(); ++i ) + { + final ContainerEvent event = insertionEvents.get(i); + final int index = impl_assertInteger( event.Accessor ); + assertEquals( i, index ); + } + + // okay, remove all those columns + while ( m_columnModel.getColumnCount() != 0 ) + { + final int columnCount = m_columnModel.getColumnCount(); + final int removeColumnIndex = m_randomGenerator.nextInt( columnCount ); + m_columnModel.removeColumn( removeColumnIndex ); + events = listener.assertExclusiveRemovalEvents(); + listener.reset(); + assertEquals( "removing a single column should notify a single event", 1, events.size() ); + final ContainerEvent event = events.get(0); + final int removalIndex = impl_assertInteger( event.Accessor ); + assertEquals( "removing an arbitrary column does not notify the proper accessor", + removeColumnIndex, removalIndex ); + } + + // calling addColumn with a column not created by the given model/implementation should not succeed + boolean caughtExpected = false; + try + { + m_columnModel.addColumn( new DummyColumn() ); + } + catch( final com.sun.star.lang.IllegalArgumentException e ) + { + assertTrue( impl_areSameInterface( e.Context, m_columnModel ) ); + caughtExpected = true; + } + assertTrue( "adding a dummy (self-implemented) grid column to the model should not succeed", caughtExpected ); + + // adding a single column to the end should succeed, properly notify, and still be consistent + final XGridColumn newColumn = m_columnModel.createColumn(); + m_columnModel.addColumn( newColumn ); + impl_assertColumnModelConsistency(); + events = listener.assertExclusiveInsertionEvents(); + listener.reset(); + assertEquals( "addColumn notifies the wrong number of insertion events", 1, events.size() ); + final int insertionIndex = impl_assertInteger( events.get(0).Accessor ); + assertEquals( insertionIndex, newColumn.getIndex() ); + } + + + @Test + public void testDataModel() throws Exception + { + impl_recreateGridModel(); + + // ensure that getCellData and getRowData have the same opinion on the data they deliver + final Object[][] data = new Object[][] { + new Object[] { 15, 17, 0 }, + new Object[] { 9, 8, 14 }, + new Object[] { 17, 2, 16 }, + new Object[] { 0, 7, 14 }, + new Object[] { 10, 16, 16 }, + }; + m_dataModel.addRows( new Object[ data.length ], data ); + + for ( int row = 0; row < data.length; ++row ) + { + assertArrayEquals( "getRowData delivers wrong data in row " + row, data[row], m_dataModel.getRowData( row ) ); + for ( int col = 0; col < data[row].length; ++col ) + { + assertEquals( "getCellData delivers wrong data at position (" + col + ", " + row + ")", + data[row][col], m_dataModel.getCellData( col, row ) ); + } + } + } + + + @Test + public void testSortableDataModel() throws Exception + { + impl_recreateGridModel(); + + final int colCount = 3; + final int rowCount = 10; + // initialize with some data + final Object[][] data = new Object[][] { + new Object[] { 15, 17, 0 }, + new Object[] { 9, 8, 14 }, + new Object[] { 17, 2, 16 }, + new Object[] { 0, 7, 14 }, + new Object[] { 10, 16, 16 }, + new Object[] { 2, 8, 10 }, + new Object[] { 4, 8, 3 }, + new Object[] { 7, 9, 0 }, + new Object[] { 15, 6, 19 }, + new Object[] { 2, 14, 19 } + }; + final Object[] rowHeadings = new Object[] { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 + }; + // ensure consistency of the test data + assertEquals( rowHeadings.length, rowCount ); + assertEquals( data.length, rowCount ); + for ( Object[] rowData : data ) + assertEquals( rowData.length, colCount ); + + // add the test data + m_dataModel.addRows( rowHeadings, data ); + assertEquals( rowCount, m_dataModel.getRowCount() ); + assertEquals( colCount, m_dataModel.getColumnCount() ); + + // sort by each column + for ( int colIndex = 0; colIndex < colCount; ++colIndex ) + { + for ( boolean ascending : new boolean[] { true, false } ) + { + m_dataModel.sortByColumn( colIndex, ascending ); + Pair<Integer,Boolean> currentSortOrder = m_dataModel.getCurrentSortOrder(); + assertEquals( "invalid current sort column (column " + colIndex + ")", currentSortOrder.First.intValue(), colIndex ); + assertEquals( "invalid current sort direction", currentSortOrder.Second.booleanValue(), ascending ); + + /*for ( int i=0; i<rowCount; ++i ) + { + for ( int j=0; j<colCount; ++j ) + System.out.print( m_dataModel.getCellData( j, i ).toString() + ", " ); + System.out.println(); + }*/ + + // verify the data is actually sorted by this column + for ( int rowIndex = 0; rowIndex < rowCount - 1; ++rowIndex ) + { + final Object currentValue = m_dataModel.getCellData( colIndex, rowIndex ); + final int currentIntValue = impl_assertInteger( currentValue ); + final Object nextValue = m_dataModel.getCellData( colIndex, rowIndex + 1 ); + final int nextIntValue = impl_assertInteger( nextValue ); + assertTrue( "data in row " + rowIndex + " is actually not sorted " + ( ascending ? "ascending" : "descending" ), + ascending ? currentIntValue <= nextIntValue + : currentIntValue >= nextIntValue ); + + // ensure the data in the other columns, and the row headings, are sorted as well + final Object rowHeading = m_dataModel.getRowHeading( rowIndex ); + final int unsortedRowIndex = impl_assertInteger( rowHeading ); + for ( int innerColIndex = 0; innerColIndex < colCount; ++innerColIndex ) + { + assertEquals( "sorted row " + rowIndex + ", unsorted row " + unsortedRowIndex + ", col " + innerColIndex + + ": wrong data", + data[unsortedRowIndex][innerColIndex], m_dataModel.getCellData( innerColIndex, rowIndex ) ); + } + } + } + } + } + + + @Test + public void testView() throws Exception + { + final XControl control = impl_createDialogWithGridControl(); + final XPropertySet gridModelProps = + UnoRuntime.queryInterface( XPropertySet.class, control.getModel() ); + + // in the current implementation (not sure this is a good idea at all), the control (more precise: the peer) + // ensures that if there are no columns in the column model, but in the data model, then the column model + // will implicitly have the needed columns added. + // To ensure that clients which rely on this do not break in the future, check this here. + final XMutableGridDataModel dataModel = UnoRuntime.queryInterface( XMutableGridDataModel.class, + gridModelProps.getPropertyValue( "GridDataModel" ) ); + assertNotNull( dataModel ); + assertEquals( 0, dataModel.getColumnCount() ); + + final XGridColumnModel columnModel = UnoRuntime.queryInterface( XGridColumnModel.class, + gridModelProps.getPropertyValue( "ColumnModel" ) ); + assertNotNull( columnModel ); + assertEquals( 0, columnModel.getColumnCount() ); + + final int columnCount = 3; + final int rowCount = 2; + dataModel.addRow( null, new Object[] { 1, 2, 3 } ); + dataModel.addRow( null, new Object[] { 6, 5, 4 } ); + + assertEquals( columnCount, dataModel.getColumnCount() ); + assertEquals( columnCount, columnModel.getColumnCount() ); + + // some cursor traveling + final XGridControl gridControl = UnoRuntime.queryInterface( XGridControl.class, control ); + gridControl.goToCell( 0, 0 ); + assertEquals( "wrong 'current column' (1)", 0, gridControl.getCurrentColumn() ); + assertEquals( "wrong 'current row' (1)", 0, gridControl.getCurrentRow() ); + gridControl.goToCell( columnCount - 1, rowCount - 1 ); + assertEquals( "wrong 'current column' (2)", dataModel.getColumnCount() - 1, gridControl.getCurrentColumn() ); + assertEquals( "wrong 'current row' (2)", dataModel.getRowCount() - 1, gridControl.getCurrentRow() ); + + // removing the last column, while the active cell is in this very last column, is expected to adjust + // the active cell + columnModel.removeColumn( columnCount - 1 ); + assertEquals( "removed the last and active column, active column was not adjusted!", + columnCount - 2, gridControl.getCurrentColumn() ); + // same holds for rows + dataModel.removeRow( rowCount - 1 ); + assertEquals( "removed the last and active row, active row was not adjusted!", + rowCount - 2, gridControl.getCurrentRow() ); + } + + + private XControl impl_createDialogWithGridControl() throws Exception + { + // create a simple dialog model/control/peer trinity + final XControlModel dialogModel = createInstance( XControlModel.class, "com.sun.star.awt.UnoControlDialogModel" ); + m_disposables.add( dialogModel ); + final XPropertySet dialogProps = UnoRuntime.queryInterface( XPropertySet.class, dialogModel ); + dialogProps.setPropertyValue( "Width", 200 ); + dialogProps.setPropertyValue( "Height", 100 ); + dialogProps.setPropertyValue( "Title", "Grid Control Unit Test" ); + final XControl dialogControl = createInstance( XControl.class, "com.sun.star.awt.UnoControlDialog" ); + m_disposables.add( dialogControl ); + dialogControl.setModel( dialogModel ); + dialogControl.createPeer( createInstance( XToolkit.class, "com.sun.star.awt.Toolkit" ), null ); + + // insert a grid control model + final XMultiServiceFactory controlModelFactory = UnoRuntime.queryInterface( XMultiServiceFactory.class, + dialogModel ); + final XPropertySet gridModelProps = UnoRuntime.queryInterface( XPropertySet.class, + controlModelFactory.createInstance( "com.sun.star.awt.grid.UnoControlGridModel" ) ); + m_disposables.add( gridModelProps ); + gridModelProps.setPropertyValue( "PositionX", 6 ); + gridModelProps.setPropertyValue( "PositionY", 6 ); + gridModelProps.setPropertyValue( "Width", 188 ); + gridModelProps.setPropertyValue( "Height", 88 ); + final XNameContainer modelContainer = UnoRuntime.queryInterface( XNameContainer.class, dialogModel ); + modelContainer.insertByName( "grid", gridModelProps ); + + // check the respective control has been created + final XControlContainer controlContainer = UnoRuntime.queryInterface( XControlContainer.class, dialogControl ); + final XControl gridControl = controlContainer.getControl( "grid" ); + assertNotNull( "no grid control created in the dialog", gridControl ); + + return gridControl; + } + + + private int impl_assertInteger( final Object i_object ) + { + assertTrue( i_object instanceof Integer ); + return ((Integer)i_object).intValue(); + } + + + private void impl_assertColumnModelConsistency() throws IndexOutOfBoundsException + { + for ( int col = 0; col < m_columnModel.getColumnCount(); ++col ) + { + final XGridColumn column = m_columnModel.getColumn( col ); + assertNotNull( column ); + assertEquals( "column/model inconsistency: column " + col + " has a wrong index!", col, column.getIndex() ); + } + + final XGridColumn[] allColumns = m_columnModel.getColumns(); + assertEquals( "getColumns returns the wrong number of column objects", + m_columnModel.getColumnCount(), allColumns.length ); + for ( int col = 0; col < m_columnModel.getColumnCount(); ++col ) + { + assertTrue( "getColumns inconsistency", impl_areSameInterface( allColumns[col], m_columnModel.getColumn(col) ) ); + } + } + + + private void impl_assertEquality( final XGridDataModel i_reference, final XGridDataModel i_compare ) throws IndexOutOfBoundsException + { + assertNotNull( i_reference ); + assertNotNull( i_compare ); + + assertEquals( "data model comparison: wrong column counts", i_reference.getColumnCount(), i_compare.getColumnCount() ); + assertEquals( "data model comparison: wrong row counts", i_reference.getRowCount(), i_compare.getRowCount() ); + + for ( int row = 0; row < i_reference.getRowCount(); ++row ) + { + assertEquals( "data model comparison: wrong row heading content in row " + row, + i_reference.getRowHeading( row ) ); + for ( int col = 0; col < i_reference.getRowCount(); ++col ) + { + assertEquals( "data model comparison: wrong cell content in cell (" + col + ", " + row + ")", + i_reference.getCellData( col, row ) ); + assertEquals( "data model comparison: wrong tooltip content in cell (" + col + ", " + row + ")", + i_reference.getCellToolTip( col, row ) ); + } + } + } + + + private void impl_assertEquality( final XGridColumnModel i_reference, final XGridColumnModel i_compare ) throws IndexOutOfBoundsException + { + assertEquals( "column model comparison: wrong column counts", i_reference.getColumnCount(), i_compare.getColumnCount() ); + for ( int col = 0; col < i_reference.getColumnCount(); ++col ) + { + final XGridColumn referenceColumn = i_reference.getColumn( col ); + final XGridColumn compareColumn = i_compare.getColumn( col ); + impl_assertEquality( referenceColumn, compareColumn ); + } + } + + + private void impl_assertEquality( final XGridColumn i_reference, final XGridColumn i_compare ) + { + final Method[] methods = XGridColumn.class.getMethods(); + for ( int m=0; m<methods.length; ++m ) + { + if ( !methods[m].getName().startsWith( "get" ) ) + continue; + try + { + final Object referenceValue = methods[m].invoke( i_reference ); + final Object compareValue = methods[m].invoke( i_compare ); + assertEquals( "grid column comparison: column attribute '" + methods[m].getName().substring(3) + "' does not match", + referenceValue, compareValue ); + } + catch ( java.lang.Exception ex ) + { + fail( " could not retrieve object attributes: " + ex.toString() ); + } + } + } + + + private boolean impl_areSameInterface( final Object i_lhs, final Object i_rhs ) + { + final XInterface lhs = UnoRuntime.queryInterface( XInterface.class, i_lhs ); + final XInterface rhs = UnoRuntime.queryInterface( XInterface.class, i_rhs ); + return UnoRuntime.areSame( lhs, rhs ); + } + + + @Before + public void initTestCase() + { + m_disposables.clear(); + } + + + @After + public void cleanupTestCase() + { + impl_dispose( m_disposables.toArray() ); + } + + + @BeforeClass + public static void setUpConnection() throws java.lang.Exception + { + System.out.println( "--------------------------------------------------------------------------------" ); + System.out.println( "starting class: " + GridControl.class.getName() ); + System.out.print( "connecting ... " ); + m_connection.setUp(); + System.out.println( "done."); + + final long seed = m_randomGenerator.nextLong(); + m_randomGenerator.setSeed( seed ); + System.out.println( "seeding random number generator with " + seed ); + } + + + @AfterClass + public static void tearDownConnection() + throws InterruptedException, com.sun.star.uno.Exception + { + System.out.println(); + System.out.println( "tearing down connection" ); + m_connection.tearDown(); + System.out.println( "finished class: " + GridControl.class.getName() ); + System.out.println( "--------------------------------------------------------------------------------" ); + } + + + public <T> T createInstance( Class<T> i_interfaceClass, final String i_serviceIndentifier ) throws Exception + { + return UnoRuntime.queryInterface( i_interfaceClass, createInstance( i_serviceIndentifier ) ); + } + + + private Object createInstance( final String i_serviceName ) throws Exception + { + Object instance = m_context.getServiceManager().createInstanceWithContext( i_serviceName, m_context ); + assertNotNull( "could not create an instance of '" + i_serviceName + "'", instance ); + return instance; + } + + private static final class DisposeListener implements XEventListener + { + DisposeListener( final Object i_component ) + { + m_component = UnoRuntime.queryInterface( XComponent.class, i_component ); + assertNotNull( m_component ); + m_component.addEventListener( this ); + } + + public void disposing( EventObject i_event ) + { + assertTrue( UnoRuntime.areSame( i_event.Source, m_component ) ); + m_isDisposed = true; + } + + final boolean isDisposed() { return m_isDisposed; } + + private final XComponent m_component; + private boolean m_isDisposed; + } + + + private static final class ColumnModelListener implements XContainerListener + { + ColumnModelListener() + { + } + + public void elementInserted( ContainerEvent i_event ) + { + m_insertionEvents.add( i_event ); + } + + public void elementRemoved( ContainerEvent i_event ) + { + m_removalEvents.add( i_event ); + } + + public void elementReplaced( ContainerEvent i_event ) + { + m_replacementEvents.add( i_event ); + } + + public void disposing( EventObject eo ) + { + } + + private List< ContainerEvent > assertExclusiveInsertionEvents() + { + assertFalse( m_insertionEvents.isEmpty() ); + assertTrue( m_removalEvents.isEmpty() ); + assertTrue( m_replacementEvents.isEmpty() ); + return m_insertionEvents; + } + + private List< ContainerEvent > assertExclusiveRemovalEvents() + { + assertTrue( m_insertionEvents.isEmpty() ); + assertFalse( m_removalEvents.isEmpty() ); + assertTrue( m_replacementEvents.isEmpty() ); + return m_removalEvents; + } + + private void reset() + { + m_insertionEvents = new ArrayList< ContainerEvent >(); + m_removalEvents = new ArrayList< ContainerEvent >(); + m_replacementEvents = new ArrayList< ContainerEvent >(); + } + + private List< ContainerEvent > getInsertionEvents() { return m_insertionEvents; } + private List< ContainerEvent > getRemovalEvents() { return m_removalEvents; } + + private List< ContainerEvent > m_insertionEvents = new ArrayList< ContainerEvent >(); + private List< ContainerEvent > m_removalEvents = new ArrayList< ContainerEvent >(); + private List< ContainerEvent > m_replacementEvents = new ArrayList< ContainerEvent >(); + } + + + private static final OfficeConnection m_connection = new OfficeConnection(); + private static Random m_randomGenerator = new Random(); + private final XComponentContext m_context; + + private XPropertySet m_gridControlModel; + private XGridColumnModel m_columnModel; + private XSortableMutableGridDataModel m_dataModel; + private final List< Object > m_disposables = new ArrayList< Object >(); +} |