blob: 870e066c8cfd49cafc5c7cad18db824a6ee794de [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
package complex.toolkit.awtgrid;
import java.lang.reflect.Method;
import com.sun.star.awt.grid.GridDataEvent;
import com.sun.star.awt.grid.XMutableGridDataModel;
import com.sun.star.lang.IllegalArgumentException;
import com.sun.star.lang.IndexOutOfBoundsException;
import static org.junit.Assert.*;
import static complex.toolkit.Assert.*;
/** test for the <code>css.awt.grid.XMutableGridData</code> interface
*
* @author frank.schoenheit@oracle.com
*/
public class TMutableGridDataModel
{
public TMutableGridDataModel( final XMutableGridDataModel i_dataModel )
{
m_dataModel = i_dataModel;
m_listener = new GridDataListener();
m_dataModel.addGridDataListener( m_listener );
}
/*
* tests the XMutableGridDataModel.addRow method
*/
public void testAddRow() throws IndexOutOfBoundsException
{
m_dataModel.addRow( m_rowHeadings[0], m_rowValues[0] );
GridDataEvent event = m_listener.assertSingleRowInsertionEvent();
m_listener.reset();
assertEquals( "row insertion: wrong FirstRow (1)", 0, event.FirstRow );
assertEquals( "row insertion: wrong LastRow (1)", 0, event.LastRow );
impl_assertRowData( 0 );
m_dataModel.addRow( m_rowHeadings[1], m_rowValues[1] );
event = m_listener.assertSingleRowInsertionEvent();
m_listener.reset();
assertEquals( "row insertion: wrong FirstRow (2)", 1, event.FirstRow );
assertEquals( "row insertion: wrong LastRow (2)", 1, event.LastRow );
impl_assertRowData( 1 );
}
/**
* tests the XMutableGridDataModel.addRows method
*/
public void testAddRows() throws IndexOutOfBoundsException, IllegalArgumentException
{
assertEquals( "precondition not met: call this directly after testAddRow, please!", 2, m_dataModel.getRowCount() );
m_dataModel.addRows(
new Object[] { m_rowHeadings[2], m_rowHeadings[3], m_rowHeadings[4] },
new Object[][] { m_rowValues[2], m_rowValues[3], m_rowValues[4] } );
GridDataEvent event = m_listener.assertSingleRowInsertionEvent();
assertEquals( "row insertion: wrong FirstRow (1)", 2, event.FirstRow );
assertEquals( "row insertion: wrong LastRow (1)", 4, event.LastRow );
m_listener.reset();
assertEquals( "data model's row count is not adjusted when adding rows", m_rowValues.length, m_dataModel.getRowCount() );
assertEquals( "data model's column count is not adjusted when adding rows", m_rowValues[0].length, m_dataModel.getColumnCount() );
for ( int row=0; row<m_rowValues.length; ++row )
{
for ( int col=0; col<m_rowValues[row].length; ++col )
{
assertEquals( "added row values are not preserved",
m_rowValues[row][col], m_dataModel.getCellData( col, row ) );
}
}
assertException( "addRows is expected to throw when invoked with different-sized arrays",
m_dataModel, "addRows", new Object[] { new Object[0], new Object[1][2] }, IllegalArgumentException.class );
}
/**
* tests the XMutableGridDataModel.insertRow method
*/
public void testInsertRow() throws IndexOutOfBoundsException
{
int expectedRowCount = m_rowValues.length;
assertEquals( "precondition not met: call this directly after testAddRows, please!", expectedRowCount, m_dataModel.getRowCount() );
// inserting some row somewhere between the other rows
final Object heading = "inbetweenRow";
final Object[] inbetweenRow = new Object[] { "foo", "bar", 3, 4, 5 };
final int insertionPos = 2;
m_dataModel.insertRow( insertionPos, heading, inbetweenRow );
++expectedRowCount;
assertEquals( "inserting a row is expected to increment the row count",
expectedRowCount, m_dataModel.getRowCount() );
final GridDataEvent event = m_listener.assertSingleRowInsertionEvent();
assertEquals( "inserting a row results in wrong FirstRow being notified", insertionPos, event.FirstRow );
assertEquals( "inserting a row results in wrong LastRow being notified", insertionPos, event.LastRow );
m_listener.reset();
for ( int row=0; row<expectedRowCount; ++row )
{
final Object[] actualRowData = m_dataModel.getRowData( row );
final Object[] expectedRowData =
( row < insertionPos )
? m_rowValues[ row ]
: ( row == insertionPos )
? inbetweenRow
: m_rowValues[ row - 1 ];
assertArrayEquals( "row number " + row + " has wrong content content after inserting a row",
expectedRowData, actualRowData );
final Object actualHeading = m_dataModel.getRowHeading(row);
final Object expectedHeading =
( row < insertionPos )
? m_rowHeadings[ row ]
: ( row == insertionPos )
? heading
: m_rowHeadings[ row - 1 ];
assertEquals( "row " + row + " has a wrong heading after invoking insertRow",
expectedHeading, actualHeading );
}
// exceptions
assertException( "inserting a row at a position > rowCount is expected to throw",
m_dataModel, "insertRow",
new Class[] { Integer.class, Object.class, Object[].class },
new Object[] { expectedRowCount + 1, "", new Object[] { "1", 2, 3 } },
IndexOutOfBoundsException.class );
assertException( "inserting a row at a position < 0 is expected to throw",
m_dataModel, "insertRow",
new Class[] { Integer.class, Object.class, Object[].class },
new Object[] { -1, "", new Object[] { "1", 2, 3 } },
IndexOutOfBoundsException.class );
// remove the row, to create the situation expected by the next test
m_dataModel.removeRow( insertionPos );
m_listener.reset();
}
/**
* tests the XMutableGridDataModel.insertRows method
*/
public void testInsertRows() throws IndexOutOfBoundsException, IllegalArgumentException
{
int expectedRowCount = m_rowValues.length;
assertEquals( "precondition not met: call this directly after testInsertRow, please!", expectedRowCount, m_dataModel.getRowCount() );
// inserting some rows somewhere between the other rows
final int insertionPos = 3;
final Object[] rowHeadings = new Object[] { "A", "B", "C" };
final Object[][] rowData = new Object[][] {
new Object[] { "A", "B", "C", "D", "E" },
new Object[] { "J", "I", "H", "G", "F" },
new Object[] { "K", "L", "M", "N", "O" }
};
final int insertedRowCount = rowData.length;
assertEquals( "invalid test data", rowHeadings.length, insertedRowCount );
m_dataModel.insertRows( insertionPos, rowHeadings, rowData );
expectedRowCount += insertedRowCount;
final GridDataEvent event = m_listener.assertSingleRowInsertionEvent();
assertEquals( "inserting multiple rows results in wrong FirstRow being notified",
insertionPos, event.FirstRow );
assertEquals( "inserting multiple rows results in wrong LastRow being notified",
insertionPos + insertedRowCount - 1, event.LastRow );
m_listener.reset();
for ( int row=0; row<expectedRowCount; ++row )
{
final Object[] actualRowData = m_dataModel.getRowData( row );
final Object[] expectedRowData =
( row < insertionPos )
? m_rowValues[ row ]
: ( row >= insertionPos ) && ( row < insertionPos + insertedRowCount )
? rowData[ row - insertionPos ]
: m_rowValues[ row - insertedRowCount ];
assertArrayEquals( "row number " + row + " has wrong content content after inserting multiple rows",
expectedRowData, actualRowData );
final Object actualHeading = m_dataModel.getRowHeading(row);
final Object expectedHeading =
( row < insertionPos )
? m_rowHeadings[ row ]
: ( row >= insertionPos ) && ( row < insertionPos + insertedRowCount )
? rowHeadings[ row - insertionPos ]
: m_rowHeadings[ row - insertedRowCount ];
assertEquals( "row " + row + " has a wrong heading after invoking insertRows",
expectedHeading, actualHeading );
}
// exceptions
assertException( "inserting multiple rows at a position > rowCount is expected to throw an IndexOutOfBoundsException",
m_dataModel, "insertRows",
new Class[] { Integer.class, Object[].class, Object[][].class },
new Object[] { expectedRowCount + 1, new Object[0], new Object[][] { } },
IndexOutOfBoundsException.class );
assertException( "inserting multiple rows at a position < 0 is expected to throw an IndexOutOfBoundsException",
m_dataModel, "insertRows",
new Class[] { Integer.class, Object[].class, Object[][].class },
new Object[] { -1, new Object[0], new Object[][] { } },
IndexOutOfBoundsException.class );
assertException( "inserting multiple rows with inconsistent array lengths is expected to throw an IllegalArgumentException",
m_dataModel, "insertRows",
new Class[] { Integer.class, Object[].class, Object[][].class },
new Object[] { 0, new Object[0], new Object[][] { new Object[0] } },
IllegalArgumentException.class );
// remove the row, to create the situation expected by the next test
for ( int i=0; i<insertedRowCount; ++i )
{
m_dataModel.removeRow( insertionPos );
m_listener.reset();
}
}
/**
* tests the XMutableGridDataModel.removeRow method
*/
public void testRemoveRow() throws IndexOutOfBoundsException
{
assertEquals( "precondition not met: call this directly after testAddRows, please!", m_rowValues.length, m_dataModel.getRowCount() );
final int rowToRemove = 2;
m_dataModel.removeRow( rowToRemove );
GridDataEvent event = m_listener.assertSingleRowRemovalEvent();
assertEquals( "incorrect notification of row removal (FirstRow)", rowToRemove, event.FirstRow );
assertEquals( "incorrect notification of row removal (LastRow)", rowToRemove, event.LastRow );
m_listener.reset();
assertEquals( "data model's row count does not reflect the removed row", m_rowValues.length - 1, m_dataModel.getRowCount() );
for ( int row = rowToRemove; row<m_rowValues.length-1; ++row )
{
for ( int col=0; col<m_rowValues[row].length; ++col )
{
assertEquals( "unexpected row values after removing a row (col: " + col + ", row: " + row + ")",
m_rowValues[row+1][col], m_dataModel.getCellData( col, row ) );
}
}
assertException( "removeRow silently ignores an invalid index (1)",
m_dataModel, "removeRow", new Object[] { -1 }, IndexOutOfBoundsException.class );
assertException( "removeRow silently ignores an invalid index (2)",
m_dataModel, "removeRow", new Object[] { m_dataModel.getRowCount() }, IndexOutOfBoundsException.class );
}
/**
* tests the XMutableGridDataModel.removeAllRows method
*/
public void testRemoveAllRows()
{
assertEquals( "precondition not met: call this directly after testRemoveRow, please!", m_rowValues.length - 1, m_dataModel.getRowCount() );
m_dataModel.removeAllRows();
final GridDataEvent event = m_listener.assertSingleRowRemovalEvent();
if ( event.FirstRow != -1 )
{ // notifying "-1" is allowed, this means "all rows affected", by definition
assertEquals( "removeAllRows is not notifying properly (1)", 0, event.FirstRow );
assertEquals( "removeAllRows is not notifying properly (2)", m_rowValues.length - 1, event.LastRow );
}
m_listener.reset();
}
/**
* tests the XMutableGridDataModel.updateCellData method
*/
public void testUpdateCellData() throws IndexOutOfBoundsException, IllegalArgumentException
{
assertEquals( "precondition not met: call this directly after testRemoveAllRows, please!", 0, m_dataModel.getRowCount() );
m_dataModel.addRows( m_rowHeadings, m_rowValues );
m_listener.assertSingleRowInsertionEvent();
m_listener.reset();
final Object[][] modifyValues = new Object[][] {
new Object[] { 2, 1, "text" },
new Object[] { 3, 0, null },
new Object[] { 0, 4, new Double( 33.0 ) }
};
for ( int i = 0; i < modifyValues.length; ++i )
{
final int row = ((Integer)modifyValues[i][0]).intValue();
final int col = ((Integer)modifyValues[i][1]).intValue();
final Object value = modifyValues[i][2];
m_dataModel.updateCellData( col, row, value );
final GridDataEvent event = m_listener.assertSingleDataChangeEvent();
assertEquals( "data change notification: FirstRow is invalid", row, event.FirstRow );
assertEquals( "data change notification: LastRow is invalid", row, event.LastRow );
assertEquals( "data change notification: FirstColumn is invalid", col, event.FirstColumn );
assertEquals( "data change notification: LastColumn is invalid", col, event.LastColumn );
m_listener.reset();
assertEquals( "data change at (" + col + ", " + row + ") not successful", value, m_dataModel.getCellData( col, row ) );
}
assertException( "updateCellData silently ignores an invalid index (1)",
m_dataModel, "updateCellData", new Class[] { int.class, int.class, Object.class },
new Object[] { -1, -1, "text" }, IndexOutOfBoundsException.class );
assertException( "updateCellData silently ignores an invalid index (2)",
m_dataModel, "updateCellData", new Class[] { int.class, int.class, Object.class },
new Object[] { 0, m_dataModel.getRowCount(), "text" }, IndexOutOfBoundsException.class );
assertException( "updateCellData silently ignores an invalid index (3)",
m_dataModel, "updateCellData", new Class[] { int.class, int.class, Object.class },
new Object[] { m_dataModel.getColumnCount(), 0, "text" }, IndexOutOfBoundsException.class );
}
/**
* tests the XMutableGridDataModel.updateRowData method
*/
public void testUpdateRowData() throws IndexOutOfBoundsException, IllegalArgumentException
{
assertEquals( "precondition not met: call this directly after testRemoveAllRows, please!", m_rowValues.length, m_dataModel.getRowCount() );
// get data from before the update
final Object[][] preUpdateValues = impl_getCurrentData();
// do the update
final int[] colIndexes = new int[] {
0, 3, 4
};
final Object[] values = new Object[] {
13, null, 42.0
};
final int rowToUpdate = 2;
m_dataModel.updateRowData( colIndexes, rowToUpdate, values );
final GridDataEvent event = m_listener.assertSingleDataChangeEvent();
assertEquals( "row update notification: FirstRow is invalid", rowToUpdate, event.FirstRow );
assertEquals( "row update notification: LastRow is invalid", rowToUpdate, event.LastRow );
assertEquals( "row update notification: FirstColumn is invalid", 0, event.FirstColumn );
assertEquals( "row update notification: LastColumn is invalid", 4, event.LastColumn );
m_listener.reset();
// reflect the changes made in the pre-update data
for ( int i=0; i<colIndexes.length; ++i )
{
preUpdateValues[rowToUpdate][colIndexes[i]] = values[i];
}
// get data from after the update
final Object[][] postUpdateValues = impl_getCurrentData();
// ensure both the manually updated pre-update data and the post-update data are identical
assertArrayEquals( preUpdateValues, postUpdateValues );
assertException( "updateRowData silently ignores an invalid index (1)",
m_dataModel, "updateRowData", new Class[] { int[].class, int.class, Object[].class },
new Object[] { new int[] { -1 }, 0, new Object[] { "text" } }, IndexOutOfBoundsException.class );
assertException( "updateRowData silently ignores an invalid index (2)",
m_dataModel, "updateRowData", new Class[] { int[].class, int.class, Object[].class },
new Object[] { new int[] { 0 }, -1, new Object[] { "" } }, IndexOutOfBoundsException.class );
assertException( "updateRowData silently ignores different-sized arrays",
m_dataModel, "updateRowData", new Class[] { int[].class, int.class, Object[].class },
new Object[] { new int[] { 0, 0 }, 0, new Object[] { "" } }, IllegalArgumentException.class );
}
/**
* tests the XMutableGridDataModel.updateRowHeading method
*/
public void testUpdateRowHeading() throws IndexOutOfBoundsException
{
assertEquals( "precondition not met: call this directly after testUpdateRowData, please!", m_rowValues.length, m_dataModel.getRowCount() );
final Object[] preUpdateHeadings = impl_getCurrentRowHeadings();
final int rowToUpdate = 2;
final String valueToUpdate = "some text";
m_dataModel.updateRowHeading( rowToUpdate, valueToUpdate );
final GridDataEvent event = m_listener.assertSingleRowHeadingChangeEvent();
assertEquals( "row heading update notification: FirstRow is invalid", rowToUpdate, event.FirstRow );
assertEquals( "row heading update notification: FirstRow is invalid", rowToUpdate, event.LastRow );
m_listener.reset();
preUpdateHeadings[rowToUpdate] = valueToUpdate;
final Object[] postUpdateHeadings = impl_getCurrentRowHeadings();
assertArrayEquals( preUpdateHeadings, postUpdateHeadings );
assertException( "updateRowHeading silently ignores an invalid index",
m_dataModel, "updateRowHeading", new Class[] { int.class, Object.class },
new Object[] { -1, "" }, IndexOutOfBoundsException.class );
}
public void cleanup()
{
m_dataModel.removeGridDataListener( m_listener );
}
private Object[][] impl_getCurrentData() throws IndexOutOfBoundsException
{
final int rowCount = m_dataModel.getRowCount();
final int colCount = m_dataModel.getColumnCount();
final Object[][] data = new Object[rowCount][colCount];
for ( int row=0; row<rowCount; ++row )
{
for ( int col=0; col<colCount; ++col )
{
data[row][col] = m_dataModel.getCellData( col, row );
}
}
return data;
}
private Object[] impl_getCurrentRowHeadings() throws IndexOutOfBoundsException
{
final int rowCount = m_dataModel.getRowCount();
final Object[] headings = new Object[rowCount];
for ( int row=0; row<rowCount; ++row )
headings[row] = m_dataModel.getRowHeading( row );
return headings;
}
private void impl_assertRowData( final int i_rowIndex ) throws IndexOutOfBoundsException
{
for ( int i=0; i<m_rowValues[i_rowIndex].length; ++i )
{
assertEquals( m_rowValues[i_rowIndex][i], m_dataModel.getCellData( i, i_rowIndex ) );
}
}
private final XMutableGridDataModel m_dataModel;
private final GridDataListener m_listener;
private final static Object[][] m_rowValues = new Object[][] {
new Object[] { 1, 2, "3", 4, 5 },
new Object[] { 2, 3, 4, "5", 6 },
new Object[] { "3", 4, 5, 6, 7 },
new Object[] { 4, 5, 6, 7, "8" },
new Object[] { 5, "6", 7, 8, 9 },
};
private final static Object[] m_rowHeadings = new Object[] {
"1", 2, 3.0, "4", (float)5.0
};
}