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