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