1 /************************************************************************* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * Copyright 2000, 2010 Oracle and/or its affiliates. 5 * 6 * OpenOffice.org - a multi-platform office productivity suite 7 * 8 * This file is part of OpenOffice.org. 9 * 10 * OpenOffice.org is free software: you can redistribute it and/or modify 11 * it under the terms of the GNU Lesser General Public License version 3 12 * only, as published by the Free Software Foundation. 13 * 14 * OpenOffice.org is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU Lesser General Public License version 3 for more details 18 * (a copy is included in the LICENSE file that accompanied this code). 19 * 20 * You should have received a copy of the GNU Lesser General Public License 21 * version 3 along with OpenOffice.org. If not, see 22 * <http://www.openoffice.org/license.html> 23 * for a copy of the LGPLv3 License. 24 * 25 *************************************************************************/ 26 27 package complex.toolkit.awtgrid; 28 29 import java.lang.reflect.Method; 30 import com.sun.star.awt.grid.GridDataEvent; 31 import com.sun.star.awt.grid.XMutableGridDataModel; 32 import com.sun.star.lang.IllegalArgumentException; 33 import com.sun.star.lang.IndexOutOfBoundsException; 34 import static org.junit.Assert.*; 35 import static complex.toolkit.Assert.*; 36 37 /** test for the <code>css.awt.grid.XMutableGridData</code> interface 38 * 39 * @author frank.schoenheit@oracle.com 40 */ 41 public class TMutableGridDataModel 42 { 43 public TMutableGridDataModel( final XMutableGridDataModel i_dataModel ) 44 { 45 m_dataModel = i_dataModel; 46 47 m_listener = new GridDataListener(); 48 m_dataModel.addGridDataListener( m_listener ); 49 } 50 51 /* 52 * tests the XMutableGridDataModel.addRow method 53 */ 54 public void testAddRow() throws IndexOutOfBoundsException 55 { 56 m_dataModel.addRow( m_rowHeadings[0], m_rowValues[0] ); 57 GridDataEvent event = m_listener.assertSingleRowInsertionEvent(); 58 m_listener.reset(); 59 assertEquals( "row insertion: wrong FirstRow (1)", 0, event.FirstRow ); 60 assertEquals( "row insertion: wrong LastRow (1)", 0, event.LastRow ); 61 impl_assertRowData( 0 ); 62 63 m_dataModel.addRow( m_rowHeadings[1], m_rowValues[1] ); 64 event = m_listener.assertSingleRowInsertionEvent(); 65 m_listener.reset(); 66 assertEquals( "row insertion: wrong FirstRow (2)", 1, event.FirstRow ); 67 assertEquals( "row insertion: wrong LastRow (2)", 1, event.LastRow ); 68 impl_assertRowData( 1 ); 69 } 70 71 /** 72 * tests the XMutableGridDataModel.addRows method 73 */ 74 public void testAddRows() throws IndexOutOfBoundsException, IllegalArgumentException 75 { 76 assertEquals( "precondition not met: call this directly after testAddRow, please!", 2, m_dataModel.getRowCount() ); 77 78 m_dataModel.addRows( 79 new Object[] { m_rowHeadings[2], m_rowHeadings[3], m_rowHeadings[4] }, 80 new Object[][] { m_rowValues[2], m_rowValues[3], m_rowValues[4] } ); 81 GridDataEvent event = m_listener.assertSingleRowInsertionEvent(); 82 assertEquals( "row insertion: wrong FirstRow (1)", 2, event.FirstRow ); 83 assertEquals( "row insertion: wrong LastRow (1)", 4, event.LastRow ); 84 m_listener.reset(); 85 86 assertEquals( "data model's row count is not adjusted when adding rows", m_rowValues.length, m_dataModel.getRowCount() ); 87 assertEquals( "data model's column count is not adjusted when adding rows", m_rowValues[0].length, m_dataModel.getColumnCount() ); 88 for ( int row=0; row<m_rowValues.length; ++row ) 89 { 90 for ( int col=0; col<m_rowValues[row].length; ++col ) 91 { 92 assertEquals( "added row values are not preserved", 93 m_rowValues[row][col], m_dataModel.getCellData( col, row ) ); 94 } 95 } 96 97 assertException( "addRows is expected to throw when invoked with different-sized arrays", 98 m_dataModel, "addRows", new Object[] { new Object[0], new Object[1][2] }, IllegalArgumentException.class ); 99 } 100 101 /** 102 * tests the XMutableGridDataModel.insertRow method 103 */ 104 public void testInsertRow() throws IndexOutOfBoundsException 105 { 106 int expectedRowCount = m_rowValues.length; 107 assertEquals( "precondition not met: call this directly after testAddRows, please!", expectedRowCount, m_dataModel.getRowCount() ); 108 109 // inserting some row somewhere between the other rows 110 final Object heading = "inbetweenRow"; 111 final Object[] inbetweenRow = new Object[] { "foo", "bar", 3, 4, 5 }; 112 final int insertionPos = 2; 113 m_dataModel.insertRow( insertionPos, heading, inbetweenRow ); 114 ++expectedRowCount; 115 assertEquals( "inserting a row is expected to increment the row count", 116 expectedRowCount, m_dataModel.getRowCount() ); 117 118 final GridDataEvent event = m_listener.assertSingleRowInsertionEvent(); 119 assertEquals( "inserting a row results in wrong FirstRow being notified", insertionPos, event.FirstRow ); 120 assertEquals( "inserting a row results in wrong LastRow being notified", insertionPos, event.LastRow ); 121 m_listener.reset(); 122 123 for ( int row=0; row<expectedRowCount; ++row ) 124 { 125 final Object[] actualRowData = m_dataModel.getRowData( row ); 126 final Object[] expectedRowData = 127 ( row < insertionPos ) 128 ? m_rowValues[ row ] 129 : ( row == insertionPos ) 130 ? inbetweenRow 131 : m_rowValues[ row - 1 ]; 132 assertArrayEquals( "row number " + row + " has wrong content content after inserting a row", 133 expectedRowData, actualRowData ); 134 135 final Object actualHeading = m_dataModel.getRowHeading(row); 136 final Object expectedHeading = 137 ( row < insertionPos ) 138 ? m_rowHeadings[ row ] 139 : ( row == insertionPos ) 140 ? heading 141 : m_rowHeadings[ row - 1 ]; 142 assertEquals( "row " + row + " has a wrong heading after invoking insertRow", 143 expectedHeading, actualHeading ); 144 } 145 146 // exceptions 147 assertException( "inserting a row at a position > rowCount is expected to throw", 148 m_dataModel, "insertRow", 149 new Class[] { Integer.class, Object.class, Object[].class }, 150 new Object[] { expectedRowCount + 1, "", new Object[] { "1", 2, 3 } }, 151 IndexOutOfBoundsException.class ); 152 assertException( "inserting a row at a position < 0 is expected to throw", 153 m_dataModel, "insertRow", 154 new Class[] { Integer.class, Object.class, Object[].class }, 155 new Object[] { -1, "", new Object[] { "1", 2, 3 } }, 156 IndexOutOfBoundsException.class ); 157 158 // remove the row, to create the situation expected by the next test 159 m_dataModel.removeRow( insertionPos ); 160 m_listener.reset(); 161 } 162 163 /** 164 * tests the XMutableGridDataModel.insertRows method 165 */ 166 public void testInsertRows() throws IndexOutOfBoundsException, IllegalArgumentException 167 { 168 int expectedRowCount = m_rowValues.length; 169 assertEquals( "precondition not met: call this directly after testInsertRow, please!", expectedRowCount, m_dataModel.getRowCount() ); 170 171 // inserting some rows somewhere between the other rows 172 final int insertionPos = 3; 173 final Object[] rowHeadings = new Object[] { "A", "B", "C" }; 174 final Object[][] rowData = new Object[][] { 175 new Object[] { "A", "B", "C", "D", "E" }, 176 new Object[] { "J", "I", "H", "G", "F" }, 177 new Object[] { "K", "L", "M", "N", "O" } 178 }; 179 final int insertedRowCount = rowData.length; 180 assertEquals( "invalid test data", rowHeadings.length, insertedRowCount ); 181 182 m_dataModel.insertRows( insertionPos, rowHeadings, rowData ); 183 expectedRowCount += insertedRowCount; 184 185 final GridDataEvent event = m_listener.assertSingleRowInsertionEvent(); 186 assertEquals( "inserting multiple rows results in wrong FirstRow being notified", 187 insertionPos, event.FirstRow ); 188 assertEquals( "inserting multiple rows results in wrong LastRow being notified", 189 insertionPos + insertedRowCount - 1, event.LastRow ); 190 m_listener.reset(); 191 192 for ( int row=0; row<expectedRowCount; ++row ) 193 { 194 final Object[] actualRowData = m_dataModel.getRowData( row ); 195 final Object[] expectedRowData = 196 ( row < insertionPos ) 197 ? m_rowValues[ row ] 198 : ( row >= insertionPos ) && ( row < insertionPos + insertedRowCount ) 199 ? rowData[ row - insertionPos ] 200 : m_rowValues[ row - insertedRowCount ]; 201 assertArrayEquals( "row number " + row + " has wrong content content after inserting multiple rows", 202 expectedRowData, actualRowData ); 203 204 final Object actualHeading = m_dataModel.getRowHeading(row); 205 final Object expectedHeading = 206 ( row < insertionPos ) 207 ? m_rowHeadings[ row ] 208 : ( row >= insertionPos ) && ( row < insertionPos + insertedRowCount ) 209 ? rowHeadings[ row - insertionPos ] 210 : m_rowHeadings[ row - insertedRowCount ]; 211 assertEquals( "row " + row + " has a wrong heading after invoking insertRows", 212 expectedHeading, actualHeading ); 213 } 214 215 // exceptions 216 assertException( "inserting multiple rows at a position > rowCount is expected to throw an IndexOutOfBoundsException", 217 m_dataModel, "insertRows", 218 new Class[] { Integer.class, Object[].class, Object[][].class }, 219 new Object[] { expectedRowCount + 1, new Object[0], new Object[][] { } }, 220 IndexOutOfBoundsException.class ); 221 assertException( "inserting multiple rows at a position < 0 is expected to throw an IndexOutOfBoundsException", 222 m_dataModel, "insertRows", 223 new Class[] { Integer.class, Object[].class, Object[][].class }, 224 new Object[] { -1, new Object[0], new Object[][] { } }, 225 IndexOutOfBoundsException.class ); 226 assertException( "inserting multiple rows with inconsistent array lengths is expected to throw an IllegalArgumentException", 227 m_dataModel, "insertRows", 228 new Class[] { Integer.class, Object[].class, Object[][].class }, 229 new Object[] { 0, new Object[0], new Object[][] { new Object[0] } }, 230 IllegalArgumentException.class ); 231 232 // remove the row, to create the situation expected by the next test 233 for ( int i=0; i<insertedRowCount; ++i ) 234 { 235 m_dataModel.removeRow( insertionPos ); 236 m_listener.reset(); 237 } 238 } 239 240 /** 241 * tests the XMutableGridDataModel.removeRow method 242 */ 243 public void testRemoveRow() throws IndexOutOfBoundsException 244 { 245 assertEquals( "precondition not met: call this directly after testAddRows, please!", m_rowValues.length, m_dataModel.getRowCount() ); 246 247 final int rowToRemove = 2; 248 m_dataModel.removeRow( rowToRemove ); 249 GridDataEvent event = m_listener.assertSingleRowRemovalEvent(); 250 assertEquals( "incorrect notification of row removal (FirstRow)", rowToRemove, event.FirstRow ); 251 assertEquals( "incorrect notification of row removal (LastRow)", rowToRemove, event.LastRow ); 252 m_listener.reset(); 253 254 assertEquals( "data model's row count does not reflect the removed row", m_rowValues.length - 1, m_dataModel.getRowCount() ); 255 for ( int row = rowToRemove; row<m_rowValues.length-1; ++row ) 256 { 257 for ( int col=0; col<m_rowValues[row].length; ++col ) 258 { 259 assertEquals( "unexpected row values after removing a row (col: " + col + ", row: " + row + ")", 260 m_rowValues[row+1][col], m_dataModel.getCellData( col, row ) ); 261 } 262 } 263 264 assertException( "removeRow silently ignores an invalid index (1)", 265 m_dataModel, "removeRow", new Object[] { -1 }, IndexOutOfBoundsException.class ); 266 assertException( "removeRow silently ignores an invalid index (2)", 267 m_dataModel, "removeRow", new Object[] { m_dataModel.getRowCount() }, IndexOutOfBoundsException.class ); 268 } 269 270 /** 271 * tests the XMutableGridDataModel.removeAllRows method 272 */ 273 public void testRemoveAllRows() 274 { 275 assertEquals( "precondition not met: call this directly after testRemoveRow, please!", m_rowValues.length - 1, m_dataModel.getRowCount() ); 276 277 m_dataModel.removeAllRows(); 278 final GridDataEvent event = m_listener.assertSingleRowRemovalEvent(); 279 if ( event.FirstRow != -1 ) 280 { // notifying "-1" is allowed, this means "all rows affected", by definition 281 assertEquals( "removeAllRows is not notifying properly (1)", 0, event.FirstRow ); 282 assertEquals( "removeAllRows is not notifying properly (2)", m_rowValues.length - 1, event.LastRow ); 283 } 284 m_listener.reset(); 285 } 286 287 /** 288 * tests the XMutableGridDataModel.updateCellData method 289 */ 290 public void testUpdateCellData() throws IndexOutOfBoundsException, IllegalArgumentException 291 { 292 assertEquals( "precondition not met: call this directly after testRemoveAllRows, please!", 0, m_dataModel.getRowCount() ); 293 294 m_dataModel.addRows( m_rowHeadings, m_rowValues ); 295 m_listener.assertSingleRowInsertionEvent(); 296 m_listener.reset(); 297 298 final Object[][] modifyValues = new Object[][] { 299 new Object[] { 2, 1, "text" }, 300 new Object[] { 3, 0, null }, 301 new Object[] { 0, 4, new Double( 33.0 ) } 302 }; 303 for ( int i = 0; i < modifyValues.length; ++i ) 304 { 305 final int row = ((Integer)modifyValues[i][0]).intValue(); 306 final int col = ((Integer)modifyValues[i][1]).intValue(); 307 final Object value = modifyValues[i][2]; 308 m_dataModel.updateCellData( col, row, value ); 309 310 final GridDataEvent event = m_listener.assertSingleDataChangeEvent(); 311 assertEquals( "data change notification: FirstRow is invalid", row, event.FirstRow ); 312 assertEquals( "data change notification: LastRow is invalid", row, event.LastRow ); 313 assertEquals( "data change notification: FirstColumn is invalid", col, event.FirstColumn ); 314 assertEquals( "data change notification: LastColumn is invalid", col, event.LastColumn ); 315 m_listener.reset(); 316 317 assertEquals( "data change at (" + col + ", " + row + ") not successful", value, m_dataModel.getCellData( col, row ) ); 318 } 319 320 assertException( "updateCellData silently ignores an invalid index (1)", 321 m_dataModel, "updateCellData", new Class[] { int.class, int.class, Object.class }, 322 new Object[] { -1, -1, "text" }, IndexOutOfBoundsException.class ); 323 assertException( "updateCellData silently ignores an invalid index (2)", 324 m_dataModel, "updateCellData", new Class[] { int.class, int.class, Object.class }, 325 new Object[] { 0, m_dataModel.getRowCount(), "text" }, IndexOutOfBoundsException.class ); 326 assertException( "updateCellData silently ignores an invalid index (3)", 327 m_dataModel, "updateCellData", new Class[] { int.class, int.class, Object.class }, 328 new Object[] { m_dataModel.getColumnCount(), 0, "text" }, IndexOutOfBoundsException.class ); 329 } 330 331 /** 332 * tests the XMutableGridDataModel.updateRowData method 333 */ 334 public void testUpdateRowData() throws IndexOutOfBoundsException, IllegalArgumentException 335 { 336 assertEquals( "precondition not met: call this directly after testRemoveAllRows, please!", m_rowValues.length, m_dataModel.getRowCount() ); 337 338 // get data from before the update 339 final Object[][] preUpdateValues = impl_getCurrentData(); 340 341 // do the update 342 final int[] colIndexes = new int[] { 343 0, 3, 4 344 }; 345 final Object[] values = new Object[] { 346 13, null, 42.0 347 }; 348 final int rowToUpdate = 2; 349 m_dataModel.updateRowData( colIndexes, rowToUpdate, values ); 350 final GridDataEvent event = m_listener.assertSingleDataChangeEvent(); 351 assertEquals( "row update notification: FirstRow is invalid", rowToUpdate, event.FirstRow ); 352 assertEquals( "row update notification: LastRow is invalid", rowToUpdate, event.LastRow ); 353 assertEquals( "row update notification: FirstColumn is invalid", 0, event.FirstColumn ); 354 assertEquals( "row update notification: LastColumn is invalid", 4, event.LastColumn ); 355 m_listener.reset(); 356 357 // reflect the changes made in the pre-update data 358 for ( int i=0; i<colIndexes.length; ++i ) 359 { 360 preUpdateValues[rowToUpdate][colIndexes[i]] = values[i]; 361 } 362 363 // get data from after the update 364 final Object[][] postUpdateValues = impl_getCurrentData(); 365 366 // ensure both the manually updated pre-update data and the post-update data are identical 367 assertArrayEquals( preUpdateValues, postUpdateValues ); 368 369 370 assertException( "updateRowData silently ignores an invalid index (1)", 371 m_dataModel, "updateRowData", new Class[] { int[].class, int.class, Object[].class }, 372 new Object[] { new int[] { -1 }, 0, new Object[] { "text" } }, IndexOutOfBoundsException.class ); 373 assertException( "updateRowData silently ignores an invalid index (2)", 374 m_dataModel, "updateRowData", new Class[] { int[].class, int.class, Object[].class }, 375 new Object[] { new int[] { 0 }, -1, new Object[] { "" } }, IndexOutOfBoundsException.class ); 376 assertException( "updateRowData silently ignores different-sized arrays", 377 m_dataModel, "updateRowData", new Class[] { int[].class, int.class, Object[].class }, 378 new Object[] { new int[] { 0, 0 }, 0, new Object[] { "" } }, IllegalArgumentException.class ); 379 } 380 381 /** 382 * tests the XMutableGridDataModel.updateRowHeading method 383 */ 384 public void testUpdateRowHeading() throws IndexOutOfBoundsException 385 { 386 assertEquals( "precondition not met: call this directly after testUpdateRowData, please!", m_rowValues.length, m_dataModel.getRowCount() ); 387 388 final Object[] preUpdateHeadings = impl_getCurrentRowHeadings(); 389 390 final int rowToUpdate = 2; 391 final String valueToUpdate = "some text"; 392 m_dataModel.updateRowHeading( rowToUpdate, valueToUpdate ); 393 final GridDataEvent event = m_listener.assertSingleRowHeadingChangeEvent(); 394 assertEquals( "row heading update notification: FirstRow is invalid", rowToUpdate, event.FirstRow ); 395 assertEquals( "row heading update notification: FirstRow is invalid", rowToUpdate, event.LastRow ); 396 m_listener.reset(); 397 398 preUpdateHeadings[rowToUpdate] = valueToUpdate; 399 400 final Object[] postUpdateHeadings = impl_getCurrentRowHeadings(); 401 assertArrayEquals( preUpdateHeadings, postUpdateHeadings ); 402 403 assertException( "updateRowHeading silently ignores an invalid index", 404 m_dataModel, "updateRowHeading", new Class[] { int.class, Object.class }, 405 new Object[] { -1, "" }, IndexOutOfBoundsException.class ); 406 } 407 408 public void cleanup() 409 { 410 m_dataModel.removeGridDataListener( m_listener ); 411 } 412 413 private Object[][] impl_getCurrentData() throws IndexOutOfBoundsException 414 { 415 final int rowCount = m_dataModel.getRowCount(); 416 final int colCount = m_dataModel.getColumnCount(); 417 final Object[][] data = new Object[rowCount][colCount]; 418 for ( int row=0; row<rowCount; ++row ) 419 { 420 for ( int col=0; col<colCount; ++col ) 421 { 422 data[row][col] = m_dataModel.getCellData( col, row ); 423 } 424 } 425 return data; 426 } 427 428 private Object[] impl_getCurrentRowHeadings() throws IndexOutOfBoundsException 429 { 430 final int rowCount = m_dataModel.getRowCount(); 431 final Object[] headings = new Object[rowCount]; 432 for ( int row=0; row<rowCount; ++row ) 433 headings[row] = m_dataModel.getRowHeading( row ); 434 return headings; 435 } 436 437 private void impl_assertRowData( final int i_rowIndex ) throws IndexOutOfBoundsException 438 { 439 for ( int i=0; i<m_rowValues[i_rowIndex].length; ++i ) 440 { 441 assertEquals( m_rowValues[i_rowIndex][i], m_dataModel.getCellData( i, i_rowIndex ) ); 442 } 443 } 444 445 private final XMutableGridDataModel m_dataModel; 446 private final GridDataListener m_listener; 447 448 private final static Object[][] m_rowValues = new Object[][] { 449 new Object[] { 1, 2, "3", 4, 5 }, 450 new Object[] { 2, 3, 4, "5", 6 }, 451 new Object[] { "3", 4, 5, 6, 7 }, 452 new Object[] { 4, 5, 6, 7, "8" }, 453 new Object[] { 5, "6", 7, 8, 9 }, 454 }; 455 456 private final static Object[] m_rowHeadings = new Object[] { 457 "1", 2, 3.0, "4", (float)5.0 458 }; 459 } 460