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 package complex.dbaccess;
24 
25 import com.sun.star.beans.PropertyVetoException;
26 import com.sun.star.beans.UnknownPropertyException;
27 import com.sun.star.beans.XPropertySet;
28 import com.sun.star.container.XIndexAccess;
29 import com.sun.star.lang.WrappedTargetException;
30 import com.sun.star.lang.XComponent;
31 import com.sun.star.sdb.CommandType;
32 import com.sun.star.sdb.XParametersSupplier;
33 import com.sun.star.sdb.XResultSetAccess;
34 import com.sun.star.sdb.XRowSetApproveBroadcaster;
35 import com.sun.star.sdbc.SQLException;
36 import com.sun.star.sdbc.XParameters;
37 import com.sun.star.sdbc.XPreparedStatement;
38 import com.sun.star.sdbc.XResultSet;
39 import com.sun.star.sdbc.XResultSetUpdate;
40 import com.sun.star.sdbc.XRow;
41 import com.sun.star.sdbc.XRowSet;
42 import com.sun.star.sdbc.XRowUpdate;
43 import com.sun.star.sdbcx.XColumnsSupplier;
44 import com.sun.star.sdbcx.XDeleteRows;
45 import com.sun.star.sdbcx.XRowLocate;
46 import com.sun.star.uno.UnoRuntime;
47 
48 import connectivity.tools.CRMDatabase;
49 import connectivity.tools.DataSource;
50 import connectivity.tools.HsqlDatabase;
51 import connectivity.tools.sdb.Connection;
52 import java.lang.reflect.Method;
53 import java.util.Random;
54 
55 // ---------- junit imports -----------------
56 import org.junit.Test;
57 import static org.junit.Assert.*;
58 // ------------------------------------------
59 
60 public class RowSet extends TestCase
61 {
62 
63     static final int MAX_TABLE_ROWS = 100;
64     static final int MAX_FETCH_ROWS = 10;
65     private static final String NEXT = "next";
66     private static final String TEST21 = "Test21";
67     HsqlDatabase m_database;
68     DataSource m_dataSource;
69     XRowSet m_rowSet;
70     XResultSet m_resultSet;
71     XResultSetUpdate m_resultSetUpdate;
72     XRow m_row;
73     XRowLocate m_rowLocate;
74     XPropertySet m_rowSetProperties;
75     XParametersSupplier m_paramsSupplier;
76 
77     // --------------------------------------------------------------------------------------------------------
78     class ResultSetMovementStress implements Runnable
79     {
80 
81         XResultSet m_resultSet;
82         XRow m_row;
83         int m_id;
84 
ResultSetMovementStress(XResultSet _resultSet, int _id)85         ResultSetMovementStress(XResultSet _resultSet, int _id) throws java.lang.Exception
86         {
87             m_resultSet = _resultSet;
88             m_row = UnoRuntime.queryInterface( XRow.class, m_resultSet );
89             m_id = _id;
90         }
91 
run()92         public void run()
93         {
94             try
95             {
96                 m_resultSet.beforeFirst();
97                 for (int i = 0; m_resultSet.next(); ++i)
98                 {
99                     int pos = m_resultSet.getRow();
100                     // final int val = m_row.getInt(1);
101 //                    System.out.println("Clone Move(" + m_id +")  before i: " + (i+1) + " Pos: " + pos + " Val: " + val);
102                     testPosition(m_resultSet, m_row, i + 1, "clone move(" + m_id + ")");
103 //                    val = m_row.getInt(1);
104 //                    System.out.println("Clone Move(" + m_id +") after i: " + (i+1) + " Pos: " + pos + " Val: " + val);
105                     int pos2 = m_resultSet.getRow();
106                     assertTrue("ResultSetMovementStress wrong position: " + i + " Pos1: " + pos + " Pos2: " + pos2, pos == pos2);
107                 }
108             }
109             catch (Exception e)
110             {
111                 fail("ResultSetMovementStress(" + m_id + ") failed: " + e);
112             }
113         }
114     }
115     // --------------------------------------------------------------------------------------------------------
createTestCase(boolean _defaultRowSet)116     private void createTestCase(boolean _defaultRowSet)
117     {
118         if (m_database == null)
119         {
120             try
121             {
122                 final CRMDatabase database = new CRMDatabase( getMSF(), false );
123                 m_database = database.getDatabase();
124                 m_dataSource = m_database.getDataSource();
125             }
126             catch (Exception e)
127             {
128                 fail("could not create the embedded HSQL database: " + e.getMessage());
129             }
130         }
131 
132         try
133         {
134             createStruture();
135         }
136         catch (SQLException e)
137         {
138             fail("could not connect to the database/table structure, error message:\n" + e.getMessage());
139         }
140 
141         if (_defaultRowSet)
142         {
143             createRowSet("TEST1", CommandType.TABLE, true, true);
144         }
145     }
146 
147     // --------------------------------------------------------------------------------------------------------
148     /** creates a com.sun.star.sdb.RowSet to use during the test
149      *  @param command
150      *      the command to use for the RowSet
151      *  @param commandType
152      *      the command type to use for the RowSet
153      *  @param execute
154      *      determines whether the RowSet should be executed
155      */
createRowSet(String command, int commandType, boolean execute)156     private void createRowSet(String command, int commandType, boolean execute)
157     {
158         createRowSet(command, commandType, execute, false);
159     }
160 
161     // --------------------------------------------------------------------------------------------------------
162     /** creates a com.sun.star.sdb.RowSet to use during the test
163      *  @param command
164      *      the command to use for the RowSet
165      *  @param commandType
166      *      the command type to use for the RowSet
167      *  @param limitFetchSize
168      *      determines whether the fetch size of the RowSet should be limited to MAX_FETCH_ROWS
169      *  @param execute
170      *      determines whether the RowSet should be executed
171      */
createRowSet(String command, int commandType, boolean execute, boolean limitFetchSize)172     private void createRowSet(String command, int commandType, boolean execute, boolean limitFetchSize)
173     {
174         try
175         {
176             m_rowSet = UnoRuntime.queryInterface( XRowSet.class, getMSF().createInstance( "com.sun.star.sdb.RowSet" ) );
177             final XPropertySet rowSetProperties = UnoRuntime.queryInterface( XPropertySet.class, m_rowSet );
178             rowSetProperties.setPropertyValue("Command", command);
179             rowSetProperties.setPropertyValue("CommandType", Integer.valueOf(commandType));
180             rowSetProperties.setPropertyValue("ActiveConnection", m_database.defaultConnection().getXConnection());
181             if (limitFetchSize)
182             {
183                 rowSetProperties.setPropertyValue("FetchSize", Integer.valueOf(MAX_FETCH_ROWS));
184             }
185 
186             m_resultSet = UnoRuntime.queryInterface( XResultSet.class, m_rowSet );
187             m_resultSetUpdate = UnoRuntime.queryInterface( XResultSetUpdate.class, m_rowSet );
188             m_row = UnoRuntime.queryInterface( XRow.class, m_rowSet );
189             m_rowLocate = UnoRuntime.queryInterface( XRowLocate.class, m_resultSet );
190             m_rowSetProperties = UnoRuntime.queryInterface( XPropertySet.class, m_rowSet );
191             m_paramsSupplier = UnoRuntime.queryInterface( XParametersSupplier.class, m_rowSet );
192 
193             if (execute)
194             {
195                 m_rowSet.execute();
196             }
197         }
198         catch (Exception e)
199         {
200             fail("caught an exception while creating the RowSet. Type:\n" + e.getClass().toString() + "\nMessage:\n" + e.getMessage());
201         }
202     }
203 
204     // --------------------------------------------------------------------------------------------------------
205     @Test
testRowSet()206     public void testRowSet() throws java.lang.Exception
207     {
208 
209         System.out.println("testing testRowSet");
210         createTestCase(true);
211 
212         // sequential postioning
213         m_resultSet.beforeFirst();
214         testSequentialPositining(m_resultSet, m_row);
215 
216         // absolute positioning
217         testAbsolutePositioning(m_resultSet, m_row);
218 
219         // 3rd test
220         test3(createClone(), m_resultSet);
221         // 4th test
222         test4(m_resultSet);
223 
224         // concurrent (multi threaded) access to the row set and its clones
225         testConcurrentAccess(m_resultSet);
226     }
227 
228     // --------------------------------------------------------------------------------------------------------
createClone()229     XResultSet createClone() throws SQLException
230     {
231         final XResultSetAccess rowAcc = UnoRuntime.queryInterface( XResultSetAccess.class, m_rowSet );
232         return rowAcc.createResultSet();
233     }
234 
235     // --------------------------------------------------------------------------------------------------------
createStruture()236     void createStruture() throws SQLException
237     {
238         m_database.executeSQL("DROP TABLE \"TEST1\" IF EXISTS");
239         m_database.executeSQL("CREATE TABLE \"TEST1\" (\"ID\" integer not null primary key, \"col2\" varchar(50) )");
240 
241         final Connection connection = m_database.defaultConnection();
242         final XPreparedStatement prep = connection.prepareStatement("INSERT INTO \"TEST1\" values (?,?)");
243         final XParameters para = UnoRuntime.queryInterface( XParameters.class, prep );
244         for (int i = 1; i <= MAX_TABLE_ROWS; ++i)
245         {
246             para.setInt(1, i);
247             para.setString(2, "Test" + i);
248             prep.executeUpdate();
249         }
250 
251         connection.refreshTables();
252     }
253 
254     // --------------------------------------------------------------------------------------------------------
testPosition(XResultSet m_resultSet, XRow m_row, int expectedValue, String location)255     void testPosition(XResultSet m_resultSet, XRow m_row, int expectedValue, String location) throws SQLException
256     {
257         final int val = m_row.getInt(1);
258         final int pos = m_resultSet.getRow();
259         assertTrue(location + ": value/position do not match: " + pos + " (pos) != " + val + " (val)", val == pos);
260         assertTrue(location + ": value/position are not as expected: " + val + " (val) != " + expectedValue + " (expected)", val == expectedValue);
261     }
262 
263     // --------------------------------------------------------------------------------------------------------
testSequentialPositining(XResultSet _resultSet, XRow _row)264     void testSequentialPositining(XResultSet _resultSet, XRow _row)
265     {
266         try
267         {
268             // 1st test
269             int i = 1;
270             while (_resultSet.next())
271             {
272                 testPosition(_resultSet, _row, i, "testSequentialPositining");
273                 ++i;
274             }
275         }
276         catch (Exception e)
277         {
278             fail("testSequentialPositining failed: " + e);
279         }
280     }
281 
282     // --------------------------------------------------------------------------------------------------------
testAbsolutePositioning(XResultSet _resultSet, XRow _row)283     void testAbsolutePositioning(XResultSet _resultSet, XRow _row)
284     {
285         try
286         {
287             for (int i = 1; i <= MAX_FETCH_ROWS; ++i)
288             {
289                 final int calcPos = (MAX_TABLE_ROWS % i) + 1;
290                 assertTrue("testAbsolutePositioning failed", _resultSet.absolute(calcPos));
291                 testPosition(_resultSet, _row, calcPos, "testAbsolutePositioning");
292             }
293         }
294         catch (Exception e)
295         {
296             fail("testAbsolutePositioning failed: " + e);
297         }
298     }
299 
300     // --------------------------------------------------------------------------------------------------------
test3(XResultSet clone, XResultSet _resultSet)301     void test3(XResultSet clone, XResultSet _resultSet)
302     {
303         try
304         {
305             final XRow _row = UnoRuntime.queryInterface( XRow.class, _resultSet );
306             final XRow cloneRow = UnoRuntime.queryInterface( XRow.class, clone );
307             for (int i = 1; i <= MAX_FETCH_ROWS; ++i)
308             {
309                 final int calcPos = (MAX_TABLE_ROWS % i) + 1;
310                 if (clone.absolute(calcPos))
311                 {
312                     testPosition(clone, cloneRow, calcPos, "test3");
313                     testAbsolutePositioning(_resultSet, _row);
314                     testAbsolutePositioning(clone, cloneRow);
315                 }
316             }
317         }
318         catch (Exception e)
319         {
320             fail("test3 failed: " + e);
321         }
322     }
323 
324     // --------------------------------------------------------------------------------------------------------
test4(XResultSet _resultSet)325     void test4(XResultSet _resultSet)
326     {
327         try
328         {
329             final XRow _row = UnoRuntime.queryInterface( XRow.class, _resultSet );
330             _resultSet.beforeFirst();
331 
332             for (int i = 1; i <= MAX_TABLE_ROWS; ++i)
333             {
334                 _resultSet.next();
335                 final XResultSet clone = createClone();
336                 final XRow cloneRow = UnoRuntime.queryInterface( XRow.class, clone );
337                 final int calcPos = MAX_TABLE_ROWS - 1;
338                 if (calcPos != 0 && clone.absolute(calcPos))
339                 {
340                     testPosition(clone, cloneRow, calcPos, "test4: clone");
341                     testPosition(_resultSet, _row, i, "test4: rowset");
342                 }
343             }
344         }
345         catch (Exception e)
346         {
347             fail("test4 failed: " + e);
348         }
349     }
350 
351     // --------------------------------------------------------------------------------------------------------
testConcurrentAccess(XResultSet _resultSet)352     void testConcurrentAccess(XResultSet _resultSet)
353     {
354         System.out.println("testing Thread");
355         try
356         {
357             // final XRow _row = (XRow)UnoRuntime.queryInterface(XRow.class,_resultSet);
358             _resultSet.beforeFirst();
359 
360             final int numberOfThreads = 10;
361 
362             final Thread threads[] = new Thread[numberOfThreads];
363             for (int i = 0; i < numberOfThreads; ++i)
364             {
365                 threads[i] = new Thread(new ResultSetMovementStress(createClone(), i));
366                 System.out.println("starting thread " + (i + 1) + " of " + (numberOfThreads));
367                 threads[i].start();
368             }
369 
370             for (int i = 0; i < numberOfThreads; ++i)
371             {
372                 threads[i].join();
373             }
374         }
375         catch (Exception e)
376         {
377             fail("testConcurrentAccess failed: " + e);
378         }
379     }
380     // --------------------------------------------------------------------------------------------------------
381 
382     @Test
testRowSetEvents()383     public void testRowSetEvents() throws java.lang.Exception
384     {
385         System.out.println("testing RowSet Events");
386         createTestCase(true);
387 
388         // first we create our RowSet object
389         final RowSetEventListener pRow = new RowSetEventListener();
390 
391         final XColumnsSupplier colSup = UnoRuntime.queryInterface( XColumnsSupplier.class, m_rowSet );
392         final XPropertySet col = UnoRuntime.queryInterface( XPropertySet.class, colSup.getColumns().getByName( "ID" ) );
393         col.addPropertyChangeListener("Value", pRow);
394         m_rowSetProperties.addPropertyChangeListener("IsModified", pRow);
395         m_rowSetProperties.addPropertyChangeListener("IsNew", pRow);
396         m_rowSetProperties.addPropertyChangeListener("IsRowCountFinal", pRow);
397         m_rowSetProperties.addPropertyChangeListener("RowCount", pRow);
398 
399         final XRowSetApproveBroadcaster xApBroad = UnoRuntime.queryInterface( XRowSetApproveBroadcaster.class, m_resultSet );
400         xApBroad.addRowSetApproveListener(pRow);
401         m_rowSet.addRowSetListener(pRow);
402 
403         // do some movements to check if we got all notifications
404         final Class cResSet = Class.forName("com.sun.star.sdbc.XResultSet");
405         final boolean moves[] = new boolean[9];
406         for (int i = 0; i < moves.length; ++i)
407         {
408             moves[i] = false;
409         }
410         moves[RowSetEventListener.APPROVE_CURSOR_MOVE] = true;
411         moves[RowSetEventListener.COLUMN_VALUE] = true;
412         moves[RowSetEventListener.CURSOR_MOVED] = true;
413         moves[RowSetEventListener.IS_ROW_COUNT_FINAL] = true;
414         moves[RowSetEventListener.ROW_COUNT] = true;
415 
416         testCursorMove(m_resultSet, cResSet.getMethod("afterLast", (Class[]) null), pRow, moves, null);
417 
418         moves[RowSetEventListener.IS_ROW_COUNT_FINAL] = false;
419         moves[RowSetEventListener.ROW_COUNT] = false;
420         testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null);
421         testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null);
422         testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null);
423         testCursorMove(m_resultSet, cResSet.getMethod("last", (Class[]) null), pRow, moves, null);
424         testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null);
425         testCursorMove(m_resultSet, cResSet.getMethod("first", (Class[]) null), pRow, moves, null);
426         testCursorMove(m_resultSet, cResSet.getMethod("previous", (Class[]) null), pRow, moves, null);
427         testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null);
428         moves[RowSetEventListener.IS_MODIFIED] = true;
429         final XRowUpdate updRow = UnoRuntime.queryInterface( XRowUpdate.class, m_resultSet );
430         updRow.updateString(2, TEST21);
431         testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null);
432 
433         moves[RowSetEventListener.IS_MODIFIED] = false;
434         updRow.updateString(2, m_row.getString(2));
435         testCursorMove(m_resultSet, cResSet.getMethod(NEXT, (Class[]) null), pRow, moves, null);
436 
437         moves[RowSetEventListener.IS_MODIFIED] = false;
438         final Class cupd = Class.forName("com.sun.star.sdbc.XResultSetUpdate");
439         final XResultSetUpdate upd = UnoRuntime.queryInterface( XResultSetUpdate.class, m_resultSet );
440         testCursorMove(upd, cupd.getMethod("moveToInsertRow", (Class[]) null), pRow, moves, null);
441 
442         updRow.updateInt(1, MAX_TABLE_ROWS + 2);
443         updRow.updateString(2, "HHHH");
444         moves[RowSetEventListener.APPROVE_CURSOR_MOVE] = false;
445         moves[RowSetEventListener.CURSOR_MOVED] = false;
446         moves[RowSetEventListener.IS_MODIFIED] = true;
447         moves[RowSetEventListener.IS_NEW] = true;
448         moves[RowSetEventListener.ROW_COUNT] = true;
449         moves[RowSetEventListener.APPROVE_ROW_CHANGE] = true;
450         moves[RowSetEventListener.ROW_CHANGED] = true;
451         testCursorMove(upd, cupd.getMethod("insertRow", (Class[]) null), pRow, moves, null);
452 
453         moves[RowSetEventListener.IS_NEW] = false;
454         moves[RowSetEventListener.ROW_COUNT] = false;
455         m_resultSet.first();
456         updRow.updateInt(1, MAX_TABLE_ROWS + 3);
457         updRow.updateString(2, "__");
458         testCursorMove(upd, cupd.getMethod("updateRow", (Class[]) null), pRow, moves, null);
459 
460         moves[RowSetEventListener.IS_NEW] = true;
461         moves[RowSetEventListener.ROW_COUNT] = true;
462         m_resultSet.first();
463         testCursorMove(upd, cupd.getMethod("deleteRow", (Class[]) null), pRow, moves, null);
464 
465         moves[RowSetEventListener.IS_NEW] = false;
466         moves[RowSetEventListener.COLUMN_VALUE] = true;
467         moves[RowSetEventListener.ROW_COUNT] = false;
468         m_resultSet.first();
469         updRow.updateString(2, TEST21);
470         testCursorMove(m_resultSet, cResSet.getMethod("refreshRow", (Class[]) null), pRow, moves, null);
471 
472         m_resultSet.first();
473         updRow.updateString(2, TEST21);
474         testCursorMove(upd, cupd.getMethod("cancelRowUpdates", (Class[]) null), pRow, moves, null);
475 
476         for (int i = 0; i < moves.length; ++i)
477         {
478             moves[i] = false;
479         }
480         moves[RowSetEventListener.APPROVE_CURSOR_MOVE] = true;
481         moves[RowSetEventListener.COLUMN_VALUE] = true;
482         moves[RowSetEventListener.CURSOR_MOVED] = true;
483 
484         final Class cloc = Class.forName("com.sun.star.sdbcx.XRowLocate");
485         m_resultSet.first();
486         final Object bookmark = m_rowLocate.getBookmark();
487         m_resultSet.next();
488         final Object temp[] = new Object[1];
489         temp[0] = bookmark;
490         Class ctemp[] = new Class[1];
491         ctemp[0] = Object.class;
492         testCursorMove(m_rowLocate, cloc.getMethod("moveToBookmark", ctemp), pRow, moves, temp);
493 
494         final Object temp2[] = new Object[2];
495         temp2[0] = bookmark;
496         temp2[1] = Integer.valueOf(1);
497         final Class ctemp2[] = new Class[2];
498         ctemp2[0] = Object.class;
499         ctemp2[1] = int.class;
500         testCursorMove(m_rowLocate, cloc.getMethod("moveRelativeToBookmark", ctemp2), pRow, moves, temp2);
501 
502         for (int i = 0; i < moves.length; ++i)
503         {
504             moves[i] = false;
505         }
506         moves[RowSetEventListener.APPROVE_ROW_CHANGE] = true;
507         moves[RowSetEventListener.ROW_CHANGED] = true;
508         moves[RowSetEventListener.ROW_COUNT] = true;
509         final Class cdelRows = Class.forName("com.sun.star.sdbcx.XDeleteRows");
510         ctemp[0] = Object[].class;
511         final XDeleteRows delRows = UnoRuntime.queryInterface( XDeleteRows.class, m_resultSet );
512         final Object bookmarks[] = new Object[5];
513         m_resultSet.first();
514         for (int i = 0; i < bookmarks.length; ++i)
515         {
516             m_resultSet.next();
517             bookmarks[i] = m_rowLocate.getBookmark();
518         }
519 
520         temp[0] = bookmarks;
521         testCursorMove(delRows, cdelRows.getMethod("deleteRows", ctemp), pRow, moves, temp);
522 
523         // now destroy the RowSet
524         final XComponent xComp = UnoRuntime.queryInterface( XComponent.class, m_resultSet );
525         xComp.dispose();
526     }
527 
528     // --------------------------------------------------------------------------------------------------------
testCursorMove(Object res, Method _method, RowSetEventListener _evt, boolean _must[], Object args[])529     private void testCursorMove(Object res, Method _method, RowSetEventListener _evt, boolean _must[], Object args[]) throws java.lang.Exception
530     {
531         _evt.clearCalling();
532         _method.invoke(res, args);
533 
534         System.out.println("testing events for " + _method.getName());
535         final int calling[] = _evt.getCalling();
536         int pos = 1;
537         assertTrue("Callings are not in the correct order for APPROVE_CURSOR_MOVE ",
538                 (!_must[RowSetEventListener.APPROVE_CURSOR_MOVE] || calling[RowSetEventListener.APPROVE_CURSOR_MOVE] == -1) || calling[RowSetEventListener.APPROVE_CURSOR_MOVE] == pos++);
539         assertTrue("Callings are not in the correct order for APPROVE_ROW_CHANGE",
540                 (!_must[RowSetEventListener.APPROVE_ROW_CHANGE] || calling[RowSetEventListener.APPROVE_ROW_CHANGE] == -1) || calling[RowSetEventListener.APPROVE_ROW_CHANGE] == pos++);
541         assertTrue("Callings are not in the correct order for COLUMN_VALUE",
542                 (!_must[RowSetEventListener.COLUMN_VALUE] || calling[RowSetEventListener.COLUMN_VALUE] == -1) || calling[RowSetEventListener.COLUMN_VALUE] == pos++);
543         assertTrue("Callings are not in the correct order for CURSOR_MOVED",
544                 (!_must[RowSetEventListener.CURSOR_MOVED] || calling[RowSetEventListener.CURSOR_MOVED] == -1) || calling[RowSetEventListener.CURSOR_MOVED] == pos++);
545         assertTrue("Callings are not in the correct order for ROW_CHANGED",
546                 (!_must[RowSetEventListener.ROW_CHANGED] || calling[RowSetEventListener.ROW_CHANGED] == -1) || calling[RowSetEventListener.ROW_CHANGED] == pos++);
547         assertTrue("Callings are not in the correct order for IS_MODIFIED",
548                 (!_must[RowSetEventListener.IS_MODIFIED] || calling[RowSetEventListener.IS_MODIFIED] == -1) || calling[RowSetEventListener.IS_MODIFIED] == pos++);
549         assertTrue("Callings are not in the correct order for IS_NEW",
550                 (!_must[RowSetEventListener.IS_NEW] || calling[RowSetEventListener.IS_NEW] == -1) || calling[RowSetEventListener.IS_NEW] == pos++);
551         assertTrue("Callings are not in the correct order for ROW_COUNT",
552                 (!_must[RowSetEventListener.ROW_COUNT] || calling[RowSetEventListener.ROW_COUNT] == -1) || calling[RowSetEventListener.ROW_COUNT] == pos++);
553         assertTrue("Callings are not in the correct order for IS_ROW_COUNT_FINAL",
554                 (!_must[RowSetEventListener.IS_ROW_COUNT_FINAL] || calling[RowSetEventListener.IS_ROW_COUNT_FINAL] == -1) || calling[RowSetEventListener.IS_ROW_COUNT_FINAL] == pos);
555 
556         _evt.clearCalling();
557     }
558 
559     // --------------------------------------------------------------------------------------------------------
560     /** returns the current row count of the RowSet
561      */
currentRowCount()562     private int currentRowCount() throws UnknownPropertyException, WrappedTargetException
563     {
564         final Integer rowCount = (Integer) m_rowSetProperties.getPropertyValue("RowCount");
565         return rowCount.intValue();
566     }
567 
568     // --------------------------------------------------------------------------------------------------------
569     /** positions the row set at an arbitrary position between 2 and (current row count - 1)
570      */
positionRandom()571     private int positionRandom() throws SQLException, UnknownPropertyException, WrappedTargetException
572     {
573         final int position = (new Random()).nextInt(currentRowCount() - 2) + 2;
574         assertTrue("sub task failed: could not position to row no. " + (Integer.valueOf(position)).toString(),
575                 m_resultSet.absolute(position));
576         return m_resultSet.getRow();
577     }
578 
579     // --------------------------------------------------------------------------------------------------------
580     /** moves the result set to a random record between 2 and (current row count - 1), and deletes this record
581      *
582      *  After returning from this method, the row set is still positioned at the deleted record
583      *  @return
584      *      the number/position of the record which has been deleted
585      */
deleteRandom()586     private int deleteRandom() throws SQLException, UnknownPropertyException, WrappedTargetException
587     {
588         // check if the current position and the row count in the result set is changed by a deletion (it should not)
589         final int positionBefore = positionRandom();
590         final int rowCountBefore = currentRowCount();
591 
592         m_resultSetUpdate.deleteRow();
593 
594         final int positionAfter = m_resultSet.getRow();
595         final int rowCountAfter = currentRowCount();
596         assertTrue("position changed during |deleteRow| (it should not)", positionAfter == positionBefore);
597         assertTrue("row count changed with a |deleteRow| (it should not)", rowCountBefore == rowCountAfter);
598         assertTrue("RowSet does not report the current row as deleted after |deleteRow|", m_resultSet.rowDeleted());
599 
600         return positionBefore;
601     }
602 
603     // --------------------------------------------------------------------------------------------------------
604     @Test
testDeleteBehavior()605     public void testDeleteBehavior() throws Exception
606     {
607         createTestCase(true);
608 
609         // ensure that all records are known
610         m_resultSet.last();
611         final int initialRowCount = currentRowCount();
612 
613         // delete a random row
614         int deletedRow = deleteRandom();
615 
616         // .....................................................................................................
617         // asking for the bookmark of a deleted row should fail
618         boolean caughtException = false;
619         try
620         {
621             m_rowLocate.getBookmark();
622         }
623         catch (SQLException e)
624         {
625             caughtException = true;
626         }
627         assertTrue("asking for the bookmark of a deleted row should throw an exception", caughtException);
628 
629         // .....................................................................................................
630         // isXXX methods should return |false| on a deleted row
631         assertTrue("one of the isFoo failed after |deleteRow|", !m_resultSet.isBeforeFirst() && !m_resultSet.isAfterLast() && !m_resultSet.isFirst() && !m_resultSet.isLast());
632         // note that we can assume that isFirst / isLast also return |false|, since deleteRandom did
633         // not position on the first or last record, but inbetween
634 
635         // .....................................................................................................
636         // check if moving away from this row in either direction yields the expected results
637         assertTrue("|previous| after |deleteRow| failed", m_resultSet.previous());
638         final int positionPrevious = m_resultSet.getRow();
639         assertTrue("position after |previous| after |deleteRow| is not as expected", positionPrevious == deletedRow - 1);
640 
641         deletedRow = deleteRandom();
642         assertTrue("|next| after |deleteRow| failed", m_resultSet.next());
643         final int positionAfter = m_resultSet.getRow();
644         assertTrue("position after |next| after |deleteRow| is not as expected", positionAfter == deletedRow);
645         // since the deleted record "vanishs" as soon as the cursor is moved away from it, the absolute position does
646         // not change with a |next| call here
647 
648         // .....................................................................................................
649         // check if the deleted rows really vanished after moving away from them
650         assertTrue("row count did not change as expected after two deletions", initialRowCount - 2 == currentRowCount());
651 
652         // .....................................................................................................
653         // check if the deleted row vanishes after moving to the insertion row
654         final int rowCountBefore = currentRowCount();
655         final int deletedPos = deleteRandom();
656         m_resultSetUpdate.moveToInsertRow();
657         assertTrue("moving to the insertion row immediately after |deleteRow| does not adjust the row count", rowCountBefore == currentRowCount() + 1);
658 
659         m_resultSetUpdate.moveToCurrentRow();
660         assertTrue("|moveToCurrentRow| after |deleteRow| + |moveToInsertRow| results in unexpected position",
661                 (m_resultSet.getRow() == deletedPos) && !m_resultSet.rowDeleted());
662 
663         // the same, but this time with deleting the first row (which is not covered by deleteRandom)
664         m_resultSet.last();
665         m_resultSetUpdate.deleteRow();
666         m_resultSetUpdate.moveToInsertRow();
667         m_resultSetUpdate.moveToCurrentRow();
668         assertTrue("|last| + |deleteRow| + |moveToInsertRow| + |moveToCurrentRow| results in wrong state", m_resultSet.isAfterLast());
669 
670         // .....................................................................................................
671         // check if deleting a deleted row fails as expected
672         deleteRandom();
673         caughtException = false;
674         try
675         {
676             m_resultSetUpdate.deleteRow();
677         }
678         catch (SQLException e)
679         {
680             caughtException = true;
681         }
682         assertTrue("deleting a deleted row succeeded - it shouldn't", caughtException);
683 
684         // .....................................................................................................
685         // check if deleteRows fails if it contains the bookmark of a previously-deleted row
686         m_resultSet.first();
687         final Object firstBookmark = m_rowLocate.getBookmark();
688         positionRandom();
689         final Object deleteBookmark = m_rowLocate.getBookmark();
690         m_resultSetUpdate.deleteRow();
691         final XDeleteRows multiDelete = UnoRuntime.queryInterface( XDeleteRows.class, m_resultSet );
692         final int[] deleteSuccess = multiDelete.deleteRows(new Object[]
693                 {
694                     firstBookmark, deleteBookmark
695                 });
696         assertTrue("XDeleteRows::deleteRows with the bookmark of an already-deleted row failed",
697                 (deleteSuccess.length == 2) && (deleteSuccess[0] != 0) && (deleteSuccess[1] == 0));
698 
699         // .....................................................................................................
700         // check if refreshing a deleted row fails as expected
701         deleteRandom();
702         caughtException = false;
703         try
704         {
705             m_resultSet.refreshRow();
706         }
707         catch (SQLException e)
708         {
709             caughtException = true;
710         }
711         assertTrue("refreshing a deleted row succeeded - it shouldn't", caughtException);
712 
713         // .....................................................................................................
714         // rowUpdated/rowDeleted
715         deleteRandom();
716         assertTrue("rowDeleted and/or rowUpdated are wrong on a deleted row", !m_resultSet.rowUpdated() && !m_resultSet.rowInserted());
717 
718         // .....................................................................................................
719         // updating values in a deleted row should fail
720         deleteRandom();
721         final XRowUpdate rowUpdated = UnoRuntime.queryInterface( XRowUpdate.class, m_resultSet );
722         caughtException = false;
723         try
724         {
725             rowUpdated.updateString(2, TEST21);
726         }
727         catch (SQLException e)
728         {
729             caughtException = true;
730         }
731         assertTrue("updating values in a deleted row should not succeed", caughtException);
732     }
733 
734     // --------------------------------------------------------------------------------------------------------
735     /** checks whether deletions on the main RowSet properly interfere (or don't interfere) with the movement
736      *  on a clone of the RowSet
737      */
738     @Test
testCloneMovesPlusDeletions()739     public void testCloneMovesPlusDeletions() throws SQLException, UnknownPropertyException, WrappedTargetException
740     {
741         createTestCase(true);
742         // ensure that all records are known
743         m_resultSet.last();
744 
745         final XResultSet clone = createClone();
746         final XRowLocate cloneRowLocate = UnoRuntime.queryInterface( XRowLocate.class, clone );
747 
748         positionRandom();
749 
750         // .....................................................................................................
751         // move the clone to the same record as the RowSet, and delete this record
752         cloneRowLocate.moveToBookmark(m_rowLocate.getBookmark());
753         final int clonePosition = clone.getRow();
754         m_resultSetUpdate.deleteRow();
755 
756         assertTrue("clone doesn't know that its current row has been deleted via the RowSet", clone.rowDeleted());
757         assertTrue("clone's position changed somehow during deletion", clonePosition == clone.getRow());
758 
759         // .....................................................................................................
760         // move the row set away from the deleted record. This should still not touch the state of the clone
761         m_resultSet.previous();
762 
763         assertTrue("clone doesn't know (anymore) that its current row has been deleted via the RowSet", clone.rowDeleted());
764         assertTrue("clone's position changed somehow during deletion and RowSet-movement", clonePosition == clone.getRow());
765 
766         // .....................................................................................................
767         // move the clone away from the deleted record
768         clone.next();
769         assertTrue("clone still assumes that its row is deleted - but we already moved it", !clone.rowDeleted());
770 
771         // .....................................................................................................
772         // check whether deleting the extremes (first / last) work
773         m_resultSet.first();
774         cloneRowLocate.moveToBookmark(m_rowLocate.getBookmark());
775         m_resultSetUpdate.deleteRow();
776         clone.previous();
777         assertTrue("deleting the first record left the clone in a strange state (after |previous|)", clone.isBeforeFirst());
778         clone.next();
779         assertTrue("deleting the first record left the clone in a strange state (after |previous| + |next|)", clone.isFirst());
780 
781         m_resultSet.last();
782         cloneRowLocate.moveToBookmark(m_rowLocate.getBookmark());
783         m_resultSetUpdate.deleteRow();
784         clone.next();
785         assertTrue("deleting the last record left the clone in a strange state (after |next|)", clone.isAfterLast());
786         clone.previous();
787         assertTrue("deleting the first record left the clone in a strange state (after |next| + |previous|)", clone.isLast());
788 
789         // .....................................................................................................
790         // check whether movements of the clone interfere with movements of the RowSet, if the latter is on a deleted row
791         final int positionBefore = positionRandom();
792         m_resultSetUpdate.deleteRow();
793         assertTrue("|deleteRow|, but no |rowDeleted| (this should have been found much earlier!)", m_resultSet.rowDeleted());
794         clone.beforeFirst();
795         while (clone.next());
796         assertTrue("row set forgot that the current row is deleted", m_resultSet.rowDeleted());
797 
798         assertTrue("moving to the next record after |deleteRow| and clone moves failed", m_resultSet.next());
799         assertTrue("wrong position after |deleteRow| and clone movement", !m_resultSet.isAfterLast() && !m_resultSet.isBeforeFirst());
800         assertTrue("wrong absolute position after |deleteRow| and clone movement", m_resultSet.getRow() == positionBefore);
801     }
802 
803     // --------------------------------------------------------------------------------------------------------
804     /** checks whether insertions on the main RowSet properly interfere (or don't interfere) with the movement
805      *  on a clone of the RowSet
806      */
807     @Test
testCloneMovesPlusInsertions()808     public void testCloneMovesPlusInsertions() throws SQLException, UnknownPropertyException, WrappedTargetException, PropertyVetoException, com.sun.star.lang.IllegalArgumentException
809     {
810         createTestCase(true);
811         // ensure that all records are known
812         m_rowSetProperties.setPropertyValue("FetchSize", Integer.valueOf(10));
813 
814         final XResultSet clone = createClone();
815         final XRow cloneRow = UnoRuntime.queryInterface( XRow.class, clone );
816 
817         // .....................................................................................................
818         // first check the basic scenario without the |moveToInsertRow| |moveToCurrentRow|, to ensure that
819         // really those are broken, if at all
820         m_resultSet.last();
821         clone.first();
822         clone.absolute(11);
823         clone.first();
824 
825         final int rowValue1 = m_row.getInt(1);
826         final int rowPos = m_resultSet.getRow();
827         final int rowValue2 = m_row.getInt(1);
828         assertTrue("repeated query for the same column value delivers different values (" + rowValue1 + " and " + rowValue2 + ") on row: " + rowPos,
829                 rowValue1 == rowValue2);
830 
831         testPosition(clone, cloneRow, 1, "mixed clone/rowset move: clone check");
832         testPosition(m_resultSet, m_row, MAX_TABLE_ROWS, "mixed clone/rowset move: rowset check");
833 
834         // .....................................................................................................
835         // now the complete scenario
836         m_resultSet.last();
837         m_resultSetUpdate.moveToInsertRow();
838         clone.first();
839         clone.absolute(11);
840         clone.first();
841         m_resultSetUpdate.moveToCurrentRow();
842 
843         testPosition(clone, cloneRow, 1, "mixed clone/rowset move/insertion: clone check");
844         testPosition(m_resultSet, m_row, 100, "mixed clone/rowset move/insertion: rowset check");
845     }
846 
847     // --------------------------------------------------------------------------------------------------------
testTableParameters()848     private void testTableParameters()
849     {
850         // for a row set simply based on a table, there should be not parameters at all
851         createRowSet("products", CommandType.TABLE, false);
852         try
853         {
854             verifyParameters(new String[]
855                     {
856                     }, "testTableParameters");
857         }
858         catch (Exception e)
859         {
860             fail("testing the parameters of a table failed" + e.getMessage());
861         }
862     }
863     // --------------------------------------------------------------------------------------------------------
864 
testParametersAfterNormalExecute()865     private void testParametersAfterNormalExecute()
866     {
867         try
868         {
869             createRowSet("SELECT * FROM \"customers\"", CommandType.COMMAND, true);
870             m_rowSetProperties.setPropertyValue("Command", "SELECT * FROM \"customers\" WHERE \"City\" = :city");
871             final XParameters rowsetParams = UnoRuntime.queryInterface( XParameters.class, m_rowSet );
872             rowsetParams.setString(1, "London");
873             m_rowSet.execute();
874         }
875         catch (Exception e)
876         {
877             fail("testing the parameters of a table failed" + e.getMessage());
878         }
879     }
880 
881     // --------------------------------------------------------------------------------------------------------
verifyParameters(String[] _paramNames, String _context)882     private void verifyParameters(String[] _paramNames, String _context) throws com.sun.star.uno.Exception
883     {
884         final XIndexAccess params = m_paramsSupplier.getParameters();
885         final int expected = _paramNames.length;
886         final int found = params != null ? params.getCount() : 0;
887 
888         assertTrue("wrong number of parameters (expected: " + expected + ", found: " + found + ") in " + _context,
889                 found == expected);
890 
891         if (found == 0)
892         {
893             return;
894         }
895 
896         for (int i = 0; i < expected; ++i)
897         {
898             final XPropertySet parameter = UnoRuntime.queryInterface( XPropertySet.class, params.getByIndex( i ) );
899 
900             final String expectedName = _paramNames[i];
901             final String foundName = (String) parameter.getPropertyValue("Name");
902             assertTrue("wrong parameter name (expected: " + expectedName + ", found: " + foundName + ") in" + _context,
903                     expectedName.equals(foundName));
904         }
905     }
906 
907     // --------------------------------------------------------------------------------------------------------
testParametrizedQuery()908     private void testParametrizedQuery()
909     {
910         try
911         {
912             // for a row set based on a parametrized query, those parameters should be properly
913             // recognized
914             m_dataSource.createQuery("products like", "SELECT * FROM \"products\" WHERE \"Name\" LIKE :product_name");
915             createRowSet("products like", CommandType.QUERY, false);
916             verifyParameters(new String[]
917                     {
918                         "product_name"
919                     }, "testParametrizedQuery");
920         }
921         catch (Exception e)
922         {
923             fail("testing the parameters of a parametrized query failed" + e.getMessage());
924         }
925     }
926 
927     // --------------------------------------------------------------------------------------------------------
testParametersInteraction()928     private void testParametersInteraction()
929     {
930         try
931         {
932             createRowSet("products like", CommandType.QUERY, false);
933 
934             // let's fill in a parameter value via XParameters, and see whether it is respected by the parameters container
935             final XParameters rowsetParams = UnoRuntime.queryInterface(XParameters.class, m_rowSet);
936             rowsetParams.setString(1, "Apples");
937 
938             XIndexAccess params = m_paramsSupplier.getParameters();
939             XPropertySet firstParam = UnoRuntime.queryInterface( XPropertySet.class, params.getByIndex( 0 ) );
940             Object firstParamValue = firstParam.getPropertyValue("Value");
941 
942             assertTrue("XParameters and the parameters container do not properly interact",
943                     "Apples".equals(firstParamValue));
944 
945             // let's see whether this also survices an execute of the row set
946             rowsetParams.setString(1, "Oranges");
947             m_rowSet.execute();
948             {
949                 // TODO: the following would not be necessary if the parameters container would *survive*
950                 // the execution of the row set. It currently doesn't (though the values it represents do).
951                 // It would be nice, but not strictly necessary, if it would.
952                 params = m_paramsSupplier.getParameters();
953                 firstParam = UnoRuntime.queryInterface( XPropertySet.class, params.getByIndex( 0 ) );
954             }
955             firstParamValue = firstParam.getPropertyValue("Value");
956             assertTrue("XParameters and the parameters container do not properly interact, after the row set has been executed",
957                     "Oranges".equals(firstParamValue));
958         }
959         catch (Exception e)
960         {
961             fail("could not test the relationship between XParameters and XParametersSupplier" + e.getMessage());
962         }
963     }
964 
965     // --------------------------------------------------------------------------------------------------------
testParametersInFilter()966     private void testParametersInFilter()
967     {
968         try
969         {
970             createRowSet("SELECT * FROM \"customers\"", CommandType.COMMAND, false);
971             m_rowSetProperties.setPropertyValue("Filter", "\"City\" = :city");
972 
973             m_rowSetProperties.setPropertyValue("ApplyFilter", Boolean.TRUE);
974             verifyParameters(new String[]
975                     {
976                         "city"
977                     }, "testParametersInFilter");
978 
979             m_rowSetProperties.setPropertyValue("ApplyFilter", Boolean.FALSE);
980             verifyParameters(new String[]
981                     {
982                     }, "testParametersInFilter");
983         }
984         catch (Exception e)
985         {
986             fail("testing the parameters within a WHERE clause failed" + e.getMessage());
987         }
988     }
989 
990     // --------------------------------------------------------------------------------------------------------
991     /** checks the XParametersSupplier functionality of a RowSet
992      */
993     @Test
testParameters()994     public void testParameters()
995     {
996         createTestCase(false);
997         // use an own RowSet instance, not the one which is also used for the other cases
998 
999         testTableParameters();
1000         testParametrizedQuery();
1001         testParametersInFilter();
1002 
1003         testParametersAfterNormalExecute();
1004 
1005         testParametersInteraction();
1006     }
1007 }
1008 
1009