1 /* Copyright (c) 2013 Tobias Wolf, All Rights Reserved
2  *
3  * The contents of this file is dual-licensed under 2
4  * alternative Open Source/Free licenses: LGPL 2.1 or later and
5  * Apache License 2.0. (starting with JNA version 4.0.0).
6  *
7  * You can freely decide which license you want to apply to
8  * the project.
9  *
10  * You may obtain a copy of the LGPL License at:
11  *
12  * http://www.gnu.org/licenses/licenses.html
13  *
14  * A copy is also included in the downloadable source code package
15  * containing JNA, in file "LGPL2.1".
16  *
17  * You may obtain a copy of the Apache License at:
18  *
19  * http://www.apache.org/licenses/
20  *
21  * A copy is also included in the downloadable source code package
22  * containing JNA, in file "AL2.0".
23  */
24 package com.sun.jna.platform.win32;
25 
26 import com.sun.jna.Pointer;
27 import com.sun.jna.Structure;
28 import static com.sun.jna.platform.win32.AbstractWin32TestSupport.checkCOMRegistered;
29 import com.sun.jna.platform.win32.COM.COMException;
30 import com.sun.jna.platform.win32.COM.COMUtils;
31 import com.sun.jna.platform.win32.COM.util.ObjectFactory;
32 import com.sun.jna.platform.win32.COM.util.IComEnum;
33 import com.sun.jna.platform.win32.COM.util.IConnectionPoint;
34 import com.sun.jna.platform.win32.COM.util.IUnknown;
35 import com.sun.jna.platform.win32.COM.util.annotation.ComInterface;
36 import com.sun.jna.platform.win32.COM.util.annotation.ComMethod;
37 import com.sun.jna.platform.win32.COM.util.annotation.ComObject;
38 import com.sun.jna.platform.win32.COM.util.annotation.ComProperty;
39 import com.sun.jna.platform.win32.OaIdl.DATE;
40 import com.sun.jna.platform.win32.OaIdl.SAFEARRAY;
41 import com.sun.jna.platform.win32.Variant.VARIANT;
42 import static com.sun.jna.platform.win32.Variant.VT_BOOL;
43 import static com.sun.jna.platform.win32.Variant.VT_BSTR;
44 import static com.sun.jna.platform.win32.Variant.VT_DATE;
45 import static com.sun.jna.platform.win32.Variant.VT_ERROR;
46 import static com.sun.jna.platform.win32.Variant.VT_I1;
47 import static com.sun.jna.platform.win32.Variant.VT_I2;
48 import static com.sun.jna.platform.win32.Variant.VT_I4;
49 import static com.sun.jna.platform.win32.Variant.VT_INT;
50 import static com.sun.jna.platform.win32.Variant.VT_R4;
51 import static com.sun.jna.platform.win32.Variant.VT_R8;
52 import static com.sun.jna.platform.win32.Variant.VT_UI1;
53 import static com.sun.jna.platform.win32.Variant.VT_UI2;
54 import static com.sun.jna.platform.win32.Variant.VT_UI4;
55 import static com.sun.jna.platform.win32.Variant.VT_UINT;
56 import com.sun.jna.platform.win32.WTypes.BSTR;
57 import com.sun.jna.platform.win32.WinDef.LONGByReference;
58 import com.sun.jna.platform.win32.WinDef.SCODE;
59 import com.sun.jna.platform.win32.WinDef.UINT;
60 import java.util.ArrayList;
61 import java.util.Date;
62 import java.util.List;
63 
64 import org.junit.Assert;
65 import org.junit.Ignore;
66 import org.junit.Test;
67 import static junit.framework.Assert.assertTrue;
68 import static org.hamcrest.CoreMatchers.equalTo;
69 import static org.hamcrest.CoreMatchers.is;
70 import org.junit.After;
71 import static org.junit.Assert.assertEquals;
72 import static org.junit.Assert.assertThat;
73 import org.junit.Before;
74 import static com.sun.jna.platform.win32.OaIdlUtil.toPrimitiveArray;
75 import com.sun.jna.platform.win32.WTypes.VARTYPE;
76 import com.sun.jna.platform.win32.WinDef.LONG;
77 import java.lang.reflect.Field;
78 import org.junit.Assume;
79 
80 public class SAFEARRAYTest {
81     static {
82         ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true);
83     }
84 
85     @Before
setup()86     public void setup() {
87         Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED);
88     }
89 
90     @After
teardown()91     public void teardown() {
92         Ole32.INSTANCE.CoUninitialize();
93     }
94 
95     @Test
testCreateVarArray()96     public void testCreateVarArray() {
97         SAFEARRAY varArray = SAFEARRAY.createSafeArray(1);
98         Assert.assertTrue(varArray != null);
99     }
100 
101     @Test
testCreateEmpty()102     public void testCreateEmpty() throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
103         Field f = Structure.class.getDeclaredField("PLACEHOLDER_MEMORY");
104         f.setAccessible(true);
105         Pointer PLACEHOLDER_MEMORY = (Pointer) f.get(null);
106         SAFEARRAY sa = Structure.newInstance(SAFEARRAY.class, PLACEHOLDER_MEMORY);
107         Assert.assertTrue(sa != null);
108     }
109 
110     @Test
testSafeArrayPutGetElement()111     public void testSafeArrayPutGetElement() throws Exception {
112         int rowCount = 2;
113         int colCount = 10;
114 
115         SAFEARRAY varArray = SAFEARRAY.createSafeArray(rowCount, colCount);
116 
117         assertThat(varArray.getDimensionCount(), is(2));
118 
119         assertThat(varArray.getUBound(0), equalTo(rowCount - 1));
120         assertThat(varArray.getUBound(1), equalTo(colCount - 1));
121 
122         for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) {
123             for (int colIdx = 0; colIdx < colCount; colIdx++) {
124                 VARIANT variant = new VARIANT(rowIdx + "#" + colIdx);
125                 varArray.putElement(variant, rowIdx, colIdx);
126             }
127         }
128 
129         for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) {
130             for (int colIdx = 0; colIdx < colCount; colIdx++) {
131                 VARIANT element = (VARIANT) varArray.getElement(rowIdx, colIdx);
132                 assertEquals(rowIdx + "#" + colIdx, element.stringValue());
133                 OleAuto.INSTANCE.VariantClear(element);
134             }
135         }
136     }
137 
138     @Ignore("Only for live testing")
139     @Test
testPerformance()140     public void testPerformance() {
141         ObjectFactory fact = new ObjectFactory();
142 
143         // Open a record set with a sample search (basicly get the first five
144         // entries from the search index
145         Connection conn = fact.createObject(Connection.class);
146         conn.Open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';", "", "", -1);
147 
148         Recordset recordset = fact.createObject(Recordset.class);
149         recordset.Open("SELECT TOP 500 System.ItemPathDisplay, System.ItemName, System.ItemUrl, System.DateCreated FROM SYSTEMINDEX ORDER BY System.ItemUrl", conn, CursorTypeEnum.adOpenUnspecified, LockTypeEnum.adLockUnspecified, -1);
150 
151         SAFEARRAY wrap = recordset.GetRows();
152 
153         assertThat(wrap.getDimensionCount(), is(2));
154 
155         long timeDirect = 0;
156         long timeGetElement = 0;
157         long timePointer = 0;
158         long timeHelper = 0;
159 
160         long start, end;
161 
162         for (int i = 0; i < 4 * 10; i++) {
163             if (i % 4 == 0) {
164                 start = System.currentTimeMillis();
165                 toArrayPtrToElement(wrap);
166                 end = System.currentTimeMillis();
167                 timePointer += (end - start);
168             } else if (i % 4 == 1) {
169                 start = System.currentTimeMillis();
170                 toArrayGetElement(wrap);
171                 end = System.currentTimeMillis();
172                 timeGetElement += (end - start);
173             } else if (i % 4 == 2) {
174                 start = System.currentTimeMillis();
175                 toArrayDirect(wrap);
176                 end = System.currentTimeMillis();
177                 timeDirect += (end - start);
178             } else if (i % 4 == 3) {
179                 start = System.currentTimeMillis();
180                 OaIdlUtil.toPrimitiveArray(wrap, false);
181                 end = System.currentTimeMillis();
182                 timeHelper += (end - start);
183             }
184         }
185 
186         System.out.println("Direct: " + timeDirect + " ms");
187         System.out.println("GetElement: " + timeGetElement + " ms");
188         System.out.println("Pointer: " + timePointer + " ms");
189         System.out.println("Helper: " + timeHelper + " ms");
190 
191         recordset.Close();
192         conn.Close();
193 
194         fact.disposeAll();
195     }
196 
toArrayGetElement(SAFEARRAY wrap)197     private Object[] toArrayGetElement(SAFEARRAY wrap) {
198         wrap.lock();
199         int rowMax = wrap.getUBound(2);
200         int columnMax = wrap.getUBound(1);
201         Object[][] result = new Object[rowMax + 1][columnMax + 1];
202         for(int i = 0; i <= rowMax; i++) {
203             for(int j = 0; j <= columnMax; j++) {
204                 VARIANT cell = (VARIANT) wrap.getElement(i, j);
205                 result[i][j] = cell.getValue();
206                 OleAuto.INSTANCE.VariantClear(cell);
207             }
208         }
209         wrap.unlock();
210         return result;
211     }
212 
toArrayPtrToElement(SAFEARRAY wrap)213     private Object[] toArrayPtrToElement(SAFEARRAY wrap) {
214         wrap.lock();
215         int rowMax = wrap.getUBound(2);
216         int columnMax = wrap.getUBound(1);
217         Object[][] result = new Object[rowMax + 1][columnMax + 1];
218         for(int i = 0; i <= rowMax; i++) {
219             for(int j = 0; j <= columnMax; j++) {
220                 VARIANT cell = new VARIANT(wrap.ptrOfIndex(i, j));
221                 result[i][j] = cell.getValue();
222             }
223         }
224         wrap.unlock();
225         return result;
226     }
227 
toArrayDirect(SAFEARRAY wrap)228     private Object[] toArrayDirect(SAFEARRAY wrap) {
229         Pointer dataPointer = wrap.accessData();
230         long rowMax = wrap.getUBound(2);
231         long columnMax = wrap.getUBound(1);
232         VARIANT[] variantData = (VARIANT[]) new VARIANT(dataPointer).toArray((int) ((rowMax + 1) * (columnMax + 1)));
233         Object[][] result = new Object[(int) (rowMax + 1)][(int) (columnMax + 1)];
234         for(long i = 0; i <= rowMax; i++) {
235             long rowOffset = i * columnMax;
236             for(long j = 0; j <= columnMax; j++) {
237                 VARIANT cell = variantData[(int) (rowOffset + j)];
238                 result[(int)i][(int) j] = cell.getValue();
239             }
240         }
241         wrap.unaccessData();
242         return result;
243     }
244 
245     @Test
testMultidimensionalNotNullBased()246     public void testMultidimensionalNotNullBased() {
247         // create a basic SAFEARRAY
248         SAFEARRAY sa = SAFEARRAY.createSafeArray(new VARTYPE(VT_I4), 2, 2);
249         sa.putElement(1, 0, 0);
250         sa.putElement(2, 0, 1);
251         sa.putElement(3, 1, 0);
252         sa.putElement(4, 1, 1);
253 
254         // query the plain SAFEARRAY
255         Object[][] basic = (Object[][]) OaIdlUtil.toPrimitiveArray(sa, false);
256 
257         // Virtually move the bounds
258         sa.rgsabound[0].lLbound = new LONG(2);
259         sa.rgsabound[1].lLbound = new LONG(5);
260         sa.write();
261 
262         // Validate new bounds
263         Assert.assertEquals(2, sa.getLBound(0));
264         Assert.assertEquals(3, sa.getUBound(0));
265         Assert.assertEquals(5, sa.getLBound(1));
266         Assert.assertEquals(6, sa.getUBound(1));
267 
268         // requery the moved array and compare with basic array
269         Object[][] relocated = (Object[][]) OaIdlUtil.toPrimitiveArray(sa, false);
270         Assert.assertArrayEquals( basic, relocated);
271     }
272 
273     @Test
testDataTypes()274     public void testDataTypes() {
275         int idx = 1;
276         Pointer dataPointer;
277         SAFEARRAY sa;
278         long elementSize;
279 
280         Object[] objectResult;
281 
282         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_BOOL), 2);
283         elementSize = sa.getElemsize();
284         assertThat(elementSize, equalTo(2L));
285         dataPointer = sa.accessData();
286         sa.putElement(true, idx);
287         short[] shortResult = dataPointer.getShortArray(0, 2);
288         objectResult = (Object[]) toPrimitiveArray(sa, false);
289         assertThat((Boolean) sa.getElement(idx), equalTo(true));
290         assertThat(shortResult[idx], equalTo((short) 0xFFFF));
291         assertThat((Short) dataPointer.getShort(idx * elementSize), equalTo((short) 0xFFFF));
292         assertThat((Boolean) objectResult[idx], equalTo(true));
293         sa.unaccessData();
294         sa.destroy();
295 
296         byte testByte = 67;
297         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_UI1), 2);
298         elementSize = sa.getElemsize();
299         assertThat(elementSize, equalTo(1L));
300         dataPointer = sa.accessData();
301         sa.putElement(testByte, idx);
302         byte[] byteResult = dataPointer.getByteArray(0, 2);
303         objectResult = (Object[]) toPrimitiveArray(sa, false);
304         assertThat((Byte) sa.getElement(idx), equalTo(testByte));
305         assertThat(dataPointer.getByte(idx * elementSize), equalTo(testByte));
306         assertThat(byteResult[idx], equalTo(testByte));
307         assertThat((Byte) objectResult[idx], equalTo(testByte));
308         sa.unaccessData();
309         sa.destroy();
310 
311         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_I1), 2);
312         elementSize = sa.getElemsize();
313         assertThat(elementSize, equalTo(1L));
314         dataPointer = sa.accessData();
315         sa.putElement(testByte, idx);
316         byteResult = dataPointer.getByteArray(0, 2);
317         objectResult = (Object[]) toPrimitiveArray(sa, false);
318         assertThat((Byte) sa.getElement(idx), equalTo(testByte));
319         assertThat(dataPointer.getByte(idx * elementSize), equalTo(testByte));
320         assertThat(byteResult[idx], equalTo(testByte));
321         assertThat((Byte) objectResult[idx], equalTo(testByte));
322         sa.unaccessData();
323         sa.destroy();
324 
325         short testShort = Short.MAX_VALUE - 1;
326         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_UI2), 2);
327         elementSize = sa.getElemsize();
328         assertThat(elementSize, equalTo(2L));
329         dataPointer = sa.accessData();
330         sa.putElement(testShort, idx);
331         shortResult = dataPointer.getShortArray(0, 2);
332         objectResult = (Object[]) toPrimitiveArray(sa, false);
333         assertThat((Short) sa.getElement(idx), equalTo(testShort));
334         assertThat(dataPointer.getShort(idx * elementSize), equalTo(testShort));
335         assertThat(shortResult[idx], equalTo(testShort));
336         assertThat((Short) objectResult[idx], equalTo(testShort));
337         sa.unaccessData();
338         sa.destroy();
339 
340         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_I2), 2);
341         elementSize = sa.getElemsize();
342         assertThat(elementSize, equalTo(2L));
343         dataPointer = sa.accessData();
344         sa.putElement(testShort, idx);
345         shortResult = dataPointer.getShortArray(0, 2);
346         objectResult = (Object[]) toPrimitiveArray(sa, false);
347         assertThat((Short) sa.getElement(idx), equalTo(testShort));
348         assertThat(dataPointer.getShort(idx * elementSize), equalTo(testShort));
349         assertThat(shortResult[idx], equalTo(testShort));
350         assertThat((Short) objectResult[idx], equalTo(testShort));
351         sa.unaccessData();
352         sa.destroy();
353 
354         int testInt = Integer.MAX_VALUE - 1;
355         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_UI4), 2);
356         elementSize = sa.getElemsize();
357         assertThat(elementSize, equalTo(4L));
358         dataPointer = sa.accessData();
359         sa.putElement(testInt, idx);
360         int[] intResult = dataPointer.getIntArray(0, 2);
361         objectResult = (Object[]) toPrimitiveArray(sa, false);
362         assertThat((Integer) sa.getElement(idx), equalTo(testInt));
363         assertThat(dataPointer.getInt(idx * elementSize), equalTo(testInt));
364         assertThat(intResult[idx], equalTo(testInt));
365         assertThat((Integer) objectResult[idx], equalTo(testInt));
366         sa.unaccessData();
367         sa.destroy();
368 
369         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_I4), 2);
370         elementSize = sa.getElemsize();
371         assertThat(elementSize, equalTo(4L));
372         dataPointer = sa.accessData();
373         sa.putElement(testInt, idx);
374         intResult = dataPointer.getIntArray(0, 2);
375         objectResult = (Object[]) toPrimitiveArray(sa, false);
376         assertThat((Integer) sa.getElement(idx), equalTo(testInt));
377         assertThat(dataPointer.getInt(idx * elementSize), equalTo(testInt));
378         assertThat(intResult[idx], equalTo(testInt));
379         assertThat((Integer) objectResult[idx], equalTo(testInt));
380         sa.unaccessData();
381         sa.destroy();
382 
383         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_UINT), 2);
384         elementSize = sa.getElemsize();
385         assertThat(elementSize, equalTo(4L));
386         dataPointer = sa.accessData();
387         sa.putElement(testInt, idx);
388         intResult = dataPointer.getIntArray(0, 2);
389         objectResult = (Object[]) toPrimitiveArray(sa, false);
390         assertThat((Integer) sa.getElement(idx), equalTo(testInt));
391         assertThat(dataPointer.getInt(idx * elementSize), equalTo(testInt));
392         assertThat(intResult[idx], equalTo(testInt));
393         assertThat((Integer) objectResult[idx], equalTo(testInt));
394         sa.unaccessData();
395         sa.destroy();
396 
397         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_INT), 2);
398         elementSize = sa.getElemsize();
399         assertThat(elementSize, equalTo(4L));
400         dataPointer = sa.accessData();
401         sa.putElement(testInt, idx);
402         intResult = dataPointer.getIntArray(0, 2);
403         objectResult = (Object[]) toPrimitiveArray(sa, false);
404         assertThat((Integer) sa.getElement(idx), equalTo(testInt));
405         assertThat(dataPointer.getInt(idx * elementSize), equalTo(testInt));
406         assertThat(intResult[idx], equalTo(testInt));
407         assertThat((Integer) objectResult[idx], equalTo(testInt));
408         sa.unaccessData();
409         sa.destroy();
410 
411         SCODE testSCODE = new SCODE(47);
412         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_ERROR), 2);
413         elementSize = sa.getElemsize();
414         assertThat(elementSize, equalTo(4L));
415         dataPointer = sa.accessData();
416         sa.putElement(testSCODE, idx);
417         intResult = dataPointer.getIntArray(0, 2);
418         objectResult = (Object[]) toPrimitiveArray(sa, false);
419         assertThat((SCODE) sa.getElement(idx), equalTo(testSCODE));
420         assertThat(dataPointer.getInt(idx * elementSize), equalTo(47));
421         assertThat(intResult[idx], equalTo(47));
422         assertThat((SCODE) objectResult[idx], equalTo(testSCODE));
423         sa.unaccessData();
424         sa.destroy();
425 
426         float testFloat = 42.23f;
427         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_R4), 2);
428         elementSize = sa.getElemsize();
429         assertThat(elementSize, equalTo(4L));
430         dataPointer = sa.accessData();
431         sa.putElement(testFloat, idx);
432         float[] floatResult = dataPointer.getFloatArray(0, 2);
433         objectResult = (Object[]) toPrimitiveArray(sa, false);
434         assertThat((Float) sa.getElement(idx), equalTo(testFloat));
435         assertThat((Float) dataPointer.getFloat(idx * elementSize), equalTo(testFloat));
436         assertThat(floatResult[idx], equalTo(testFloat));
437         assertThat((Float) objectResult[idx], equalTo(testFloat));
438         sa.unaccessData();
439         sa.destroy();
440 
441         double testDouble = 42.23d;
442         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_R8), 2);
443         elementSize = sa.getElemsize();
444         assertThat(elementSize, equalTo(8L));
445         dataPointer = sa.accessData();
446         sa.putElement(testDouble, idx);
447         double[] doubleResult = dataPointer.getDoubleArray(0, 2);
448         objectResult = (Object[]) toPrimitiveArray(sa, false);
449         assertThat((Double) sa.getElement(idx), equalTo(testDouble));
450         assertThat((Double) dataPointer.getDouble(idx * elementSize), equalTo(testDouble));
451         assertThat(doubleResult[idx], equalTo(testDouble));
452         assertThat((Double) objectResult[idx], equalTo(testDouble));
453         sa.unaccessData();
454         sa.destroy();
455 
456         Date testDate = new Date(1923, 1, 1, 5, 0, 0);
457         DATE testDATE = new DATE(testDate);
458         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_DATE), 2);
459         elementSize = sa.getElemsize();
460         assertThat(elementSize, equalTo(8L));
461         dataPointer = sa.accessData();
462         sa.putElement(testDATE, idx);
463         doubleResult = dataPointer.getDoubleArray(0, 2);
464         objectResult = (Object[]) toPrimitiveArray(sa, false);
465         assertThat(((DATE) sa.getElement(idx)).date, equalTo(testDATE.date));
466         assertThat((Double) dataPointer.getDouble(idx * elementSize), equalTo(testDATE.date));
467         assertThat(((DATE) sa.getElement(idx)).getAsJavaDate(), equalTo(testDate));
468         assertThat(doubleResult[idx], equalTo(testDATE.date));
469         assertThat((Date) objectResult[idx], equalTo(testDate));
470         sa.unaccessData();
471         sa.destroy();
472 
473         String testString = "äöüßAE!";
474         sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_BSTR), 2);
475         elementSize = sa.getElemsize();
476         dataPointer = sa.accessData();
477         sa.putElement(testString, idx);
478         Pointer[] pointerResult = dataPointer.getPointerArray(0, 2);
479         objectResult = (Object[]) toPrimitiveArray(sa, false);
480         assertThat(((String) sa.getElement(idx)), equalTo(testString));
481         assertThat(new BSTR(dataPointer.getPointer(idx * elementSize)).getValue(), equalTo(testString));
482         assertThat(new BSTR(pointerResult[idx]).getValue(), equalTo(testString));
483         assertThat((String) objectResult[idx], equalTo(testString));
484         sa.unaccessData();
485         sa.destroy();
486 
487         // VT_VARIANT is tested in testADODB
488 
489         // untested: VT_UNKNOWN
490         // untested: VT_DISPATCH
491         // untested: VT_CY
492         // untested: VT_DECIMAL
493         // unsupported: VT_RECORD
494     }
495 
496 
497     /**
498      * Test assumption: The windows search provider is present and holds at least
499      * five entries. If this assumption is not met, this test fails.
500      */
501     @Ignore("Assumes windows search provider present with five entries")
502     @Test
testADODB()503     public void testADODB() {
504         ObjectFactory fact = new ObjectFactory();
505 
506         // Open a record set with a sample search (basicly get the first five
507         // entries from the search index
508         Connection conn = fact.createObject(Connection.class);
509         try {
510             conn.Open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';", "", "", -1);
511         } catch (COMException ex) {
512             Assume.assumeNoException(ex);
513         }
514 
515         Recordset recordset = fact.createObject(Recordset.class);
516         recordset.Open("SELECT TOP 5 System.ItemPathDisplay, System.ItemName, System.ItemUrl, System.DateCreated FROM SYSTEMINDEX ORDER BY System.ItemUrl", conn, CursorTypeEnum.adOpenUnspecified, LockTypeEnum.adLockUnspecified, -1);
517 
518         // Save complete list for comparison with subscript list
519         List<String> urls = new ArrayList<String>(5);
520         List<String> names = new ArrayList<String>(5);
521 
522         while (!recordset.getEOF()) {
523             WinNT.HRESULT hr;
524 
525              // Fetch (all) five rows and extract SAFEARRAY
526             SAFEARRAY sa = recordset.GetRows(5);
527 
528             assertThat(sa.getDimensionCount(), is(2));
529 
530             // Test getting bounds via automation functions SafeArrayGetLBound
531             // and SafeArrayGetUBound
532 
533             // 5 rows (dimension 1) and 4 (dimension 2) columns should be
534             // returned, the indices are zero-based, the lower bounds are
535             // always zero
536             //
537             // Dimensions are inverted between SafeArray and java, so
538             // in this case row dimension 2 retrieves row count,
539             // dimension 1 retrieves column count
540             WinDef.LONGByReference res = new WinDef.LONGByReference();
541 
542             hr = OleAuto.INSTANCE.SafeArrayGetLBound(sa, new UINT(2), res);
543             assert COMUtils.SUCCEEDED(hr);
544             assertThat(res.getValue().intValue(), is(0));
545 
546             hr = OleAuto.INSTANCE.SafeArrayGetUBound(sa, new UINT(2), res);
547             assert COMUtils.SUCCEEDED(hr);
548             assertThat(res.getValue().intValue(), is(4));
549 
550             hr = OleAuto.INSTANCE.SafeArrayGetLBound(sa, new UINT(1), res);
551             assert COMUtils.SUCCEEDED(hr);
552             assertThat(res.getValue().intValue(), is(0));
553 
554             hr = OleAuto.INSTANCE.SafeArrayGetUBound(sa, new UINT(1), res);
555             assert COMUtils.SUCCEEDED(hr);
556             assertThat(res.getValue().intValue(), is(3));
557 
558             // Get dimensions directly from structure
559             // lLbound contains lowerBound (first index)
560             // cElements contains count of elements
561             int row_lower = sa.rgsabound[0].lLbound.intValue();
562             int row_count = sa.rgsabound[0].cElements.intValue();
563             int column_lower = sa.rgsabound[1].lLbound.intValue();
564             int column_count = sa.rgsabound[1].cElements.intValue();
565             assertThat(row_lower, is(0));
566             assertThat(row_count, is(5));
567             assertThat(column_lower, is(0));
568             assertThat(column_count, is(4));
569 
570             // Use Wrapper methods
571             assertThat(sa.getLBound(0), is(0));
572             assertThat(sa.getUBound(0), is(4));
573             assertThat(sa.getLBound(1), is(0));
574             assertThat(sa.getUBound(1), is(3));
575 
576             // Iterate over resultset and fetch via SafeArrayGetElement
577             // Columns 1 - 3 return Strings, Column 4 returns a date
578             for (int rowIdx = row_lower; rowIdx < row_lower + row_count; rowIdx++) {
579                 for (int colIdx = column_lower; colIdx < column_lower + column_count; colIdx++) {
580                     VARIANT result = (VARIANT) sa.getElement(rowIdx, colIdx);
581                     Pointer pv = sa.ptrOfIndex(rowIdx, colIdx);
582                     VARIANT result2 = new VARIANT(pv);
583                     COMUtils.checkRC(hr);
584                     if(colIdx == 3) {
585                         assert (result.getVarType().intValue() & Variant.VT_DATE) > 0;
586                         assert (result2.getVarType().intValue() & Variant.VT_DATE) > 0;
587                     } else {
588                         assert (result.getVarType().intValue() & Variant.VT_BSTR) > 0;
589                         assert (result2.getVarType().intValue() & Variant.VT_BSTR) > 0;
590                     }
591                     // Only clear result, as SafeArrayGetElement creates a copy
592                     // result2 is a view into the SafeArray
593                     OleAuto.INSTANCE.VariantClear(result);
594                 }
595             }
596 
597             // Access SafeArray directly
598             sa.lock();
599             try {
600                 // Map the returned array to java
601                 VARIANT[] variantArray = (VARIANT[]) (new VARIANT(sa.pvData.getPointer()).toArray(row_count * column_count));
602 
603                 for (int rowIdx = 0; rowIdx < row_count; rowIdx++) {
604                     for (int colIdx = 0; colIdx < column_count; colIdx++) {
605                         int index = rowIdx * column_count + colIdx;
606                         VARIANT result = variantArray[index];
607                         if (colIdx == 3) {
608                             assert (result.getVarType().intValue() & Variant.VT_DATE) > 0;
609                         } else {
610                             assert (result.getVarType().intValue() & Variant.VT_BSTR) > 0;
611                         }
612                         // see comment for urls
613                         if(colIdx == 2) {
614                             urls.add(result.stringValue());
615                         } else if (colIdx == 1) {
616                             names.add(result.stringValue());
617                         }
618                     }
619                 }
620             } finally {
621                 sa.unlock();
622             }
623 
624             // Access SafeArray directly - Variant 2
625             Pointer data = sa.accessData();
626             try {
627                 // Map the returned array to java
628                 VARIANT[] variantArray = (VARIANT[]) (new VARIANT(data).toArray(row_count * column_count));
629 
630                 for (int rowIdx = 0; rowIdx < row_count; rowIdx++) {
631                     for (int colIdx = 0; colIdx < column_count; colIdx++) {
632                         int index = rowIdx * column_count + colIdx;
633                         VARIANT result = variantArray[index];
634                         if (colIdx == 3) {
635                             assert (result.getVarType().intValue() & Variant.VT_DATE) > 0;
636                         } else {
637                             assert (result.getVarType().intValue() & Variant.VT_BSTR) > 0;
638                         }
639                         // see comment for urls
640                         if(colIdx == 2) {
641                             urls.add(result.stringValue());
642                         } else if (colIdx == 1) {
643                             names.add(result.stringValue());
644                         }
645                     }
646                 }
647             } finally {
648                 sa.unaccessData();
649             }
650 
651             sa.destroy();
652         }
653 
654         recordset.Close();
655 
656         // Requery and fetch only columns "System.ItemUrl", "System.ItemName" and "System.ItemUrl"
657         recordset = fact.createObject(Recordset.class);
658         recordset.Open("SELECT TOP 5 System.ItemPathDisplay, System.ItemName, System.ItemUrl FROM SYSTEMINDEX ORDER BY System.ItemUrl", conn, CursorTypeEnum.adOpenUnspecified, LockTypeEnum.adLockUnspecified, -1);
659 
660 
661         // Create SAFEARRAY and wrap it into a VARIANT
662         // Array is initialized to one element and then redimmed. This is done
663         // to test SafeArrayRedim, in normal usage it is more efficient to
664         // intitialize directly to the correct size
665         SAFEARRAY arr = SAFEARRAY.createSafeArray(1);
666         arr.putElement(new VARIANT("System.ItemUrl"), 0);
667         boolean exceptionCaught = false;
668         try {
669             arr.putElement(new VARIANT("System.ItemName"), 1);
670         } catch (COMException ex) {
671             exceptionCaught = true;
672         }
673         assertTrue("Array is initialized to a size of one - it can't hold a second item.", exceptionCaught);
674         arr.redim(2, 0);
675         arr.putElement(new VARIANT("System.ItemName"), 1);
676 
677         assertThat(arr.getDimensionCount(), is(1));
678 
679         VARIANT columnList = new VARIANT();
680         columnList.setValue(Variant.VT_ARRAY | Variant.VT_VARIANT, arr);
681 
682         assert !(recordset.getEOF());
683 
684         while (!recordset.getEOF()) {
685             SAFEARRAY sa = recordset.GetRows(5, VARIANT.VARIANT_MISSING, columnList);
686 
687             assertThat(sa.getDimensionCount(), is(2));
688 
689             assertThat(sa.getVarType().intValue(), is(Variant.VT_VARIANT));
690             LONGByReference longRef = new LONGByReference();
691 
692             OleAuto.INSTANCE.SafeArrayGetLBound(sa, new UINT(2), longRef);
693             int lowerBound = longRef.getValue().intValue();
694             assertThat(sa.getLBound(0), equalTo(lowerBound));
695 
696             OleAuto.INSTANCE.SafeArrayGetUBound(sa, new UINT(2), longRef);
697             int upperBound = longRef.getValue().intValue();
698             assertThat(sa.getUBound(0), equalTo(upperBound));
699 
700             // 5 rows are expected
701             assertThat(upperBound - lowerBound + 1, is(5));
702 
703             for (int rowIdx = lowerBound; rowIdx <= upperBound; rowIdx++) {
704                 VARIANT variantItemUrl = (VARIANT) sa.getElement(rowIdx, 0);
705                 VARIANT variantItemName = (VARIANT) sa.getElement(rowIdx, 1);
706                 assertThat(variantItemUrl.stringValue(), is(urls.get(rowIdx)));
707                 assertThat(variantItemName.stringValue(), is(names.get(rowIdx)));
708                 OleAuto.INSTANCE.VariantClear(variantItemUrl);
709                 OleAuto.INSTANCE.VariantClear(variantItemName);
710             }
711 
712             sa.destroy();
713         }
714 
715         recordset.Close();
716 
717         // Requery and fetch only columns "System.ItemUrl", "System.ItemName" and "System.ItemUrl"
718         recordset = fact.createObject(Recordset.class);
719         recordset.Open("SELECT TOP 5 System.ItemPathDisplay, System.ItemName, System.ItemUrl FROM SYSTEMINDEX ORDER BY System.ItemUrl", conn, CursorTypeEnum.adOpenUnspecified, LockTypeEnum.adLockUnspecified, -1);
720 
721         assert !(recordset.getEOF());
722 
723         while (!recordset.getEOF()) {
724             Object[][] data = (Object[][]) OaIdlUtil.toPrimitiveArray(recordset.GetRows(5, VARIANT.VARIANT_MISSING, columnList), true);
725 
726             assertThat(data.length, is(5));
727             assertThat(data[0].length, is(2));
728 
729             for (int rowIdx = 0; rowIdx < data.length; rowIdx++) {
730                 assertThat((String) data[rowIdx][0], is(urls.get(rowIdx)));
731                 assertThat((String) data[rowIdx][1], is(names.get(rowIdx)));
732             }
733         }
734 
735         recordset.Close();
736 
737         conn.Close();
738 
739         fact.disposeAll();
740     }
741 
742     // -------------  Helper classes / interfaces
743 
744     /**
745      * <p>
746      * guid({00000514-0000-0010-8000-00AA006D2EA4})</p>
747      * <p>
748      * source(ConnectionEvents)</p>
749      */
750     @ComObject(clsId = "{00000514-0000-0010-8000-00AA006D2EA4}", progId = "{B691E011-1797-432E-907A-4D8C69339129}")
751     public static interface Connection extends
752             _Connection,
753             IConnectionPoint,
754             IUnknown {
755 
756     }
757 
758     /**
759      * <p>
760      * guid({0000051B-0000-0010-8000-00AA006D2EA4})</p>
761      */
762     public static enum CursorTypeEnum implements IComEnum {
763         adOpenUnspecified(-1),
764         adOpenForwardOnly(0),
765         adOpenKeyset(1),
766         adOpenDynamic(2),
767         adOpenStatic(3),;
768 
CursorTypeEnum(long value)769         private CursorTypeEnum(long value) {
770             this.value = value;
771         }
772         private long value;
773 
774         @Override
getValue()775         public long getValue() {
776             return this.value;
777         }
778     }
779 
780     /**
781      * <p>
782      * guid({0000051D-0000-0010-8000-00AA006D2EA4})</p>
783      */
784     public static enum LockTypeEnum implements IComEnum {
785         adLockUnspecified(-1),
786         adLockReadOnly(1),
787         adLockPessimistic(2),
788         adLockOptimistic(3),
789         adLockBatchOptimistic(4),;
790 
LockTypeEnum(long value)791         private LockTypeEnum(long value) {
792             this.value = value;
793         }
794         private long value;
795 
796         @Override
getValue()797         public long getValue() {
798             return this.value;
799         }
800     }
801 
802     /**
803      * <p>
804      * guid({00000535-0000-0010-8000-00AA006D2EA4})</p>
805      */
806     @ComObject(clsId = "{00000535-0000-0010-8000-00AA006D2EA4}", progId = "{00000300-0000-0010-8000-00AA006D2EA4}")
807     public static interface Recordset extends
808             _Recordset {
809 
810     }
811 
812     /**
813      * <p>
814      * guid({00001550-0000-0010-8000-00AA006D2EA4})</p>
815      */
816     @ComInterface(iid = "{00001550-0000-0010-8000-00AA006D2EA4}")
817     public static interface _Connection {
818 
819         /**
820          * <p>
821          * memberId(5)</p>
822          */
823         @ComMethod(name = "Close")
Close()824         void Close();
825 
826         /**
827          * <p>
828          * memberId(10)</p>
829          */
830         @ComMethod(name = "Open")
Open(String ConnectionString, String UserID, String Password, int Options)831         void Open(String ConnectionString,
832                 String UserID,
833                 String Password,
834                 int Options);
835     }
836 
837     /**
838      *
839      *
840      * <p>
841      * guid({00000556-0000-0010-8000-00AA006D2EA4})</p>
842      */
843     @ComInterface(iid = "{00000556-0000-0010-8000-00AA006D2EA4}")
844     public static interface _Recordset {
845 
846         /**
847          * <p>
848          * memberId(1006)</p>
849          */
850         @ComProperty(name = "EOF")
getEOF()851         Boolean getEOF();
852 
853         /**
854          * <p>
855          * memberId(1016)</p>
856          */
857         @ComMethod(name = "GetRows")
GetRows(int Rows, Object Start, Object Fields)858         SAFEARRAY GetRows(int Rows,
859                 Object Start,
860                 Object Fields);
861 
862         /**
863          * <p>
864          * memberId(1016)</p>
865          */
866         @ComMethod(name = "GetRows")
GetRows(int Rows)867         SAFEARRAY GetRows(int Rows);
868 
869         /**
870          * <p>
871          * memberId(1016)</p>
872          */
873         @ComMethod(name = "GetRows")
GetRows()874         SAFEARRAY GetRows();
875 
876         /**
877          * <p>
878          * memberId(1014)</p>
879          */
880         @ComMethod(name = "Close")
Close()881         void Close();
882 
883         /**
884          * <p>
885          * memberId(1022)</p>
886          */
887         @ComMethod(name = "Open")
Open(Object Source, Object ActiveConnection, CursorTypeEnum CursorType, LockTypeEnum LockType, int Options)888         void Open(Object Source,
889                 Object ActiveConnection,
890                 CursorTypeEnum CursorType,
891                 LockTypeEnum LockType,
892                 int Options);
893     }
894 }
895