Skip to content

Commit 9ccbaf3

Browse files
committed
adding VARIANT marshalling with multidimensional SAFEARRAY inside
1 parent 6d3150f commit 9ccbaf3

File tree

3 files changed

+298
-17
lines changed

3 files changed

+298
-17
lines changed

native/safearray.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ pair<SafeArrayXducer::NativeType,VARTYPE> SafeArrayXducer::toNative2(
4747
if(env->IsSameObject(clz,*(e->clazz)))
4848
return pair<SAFEARRAY*,VARTYPE>( (e->toNative)(env,a), e->vt );
4949
}
50+
51+
// Try as multi dimensinal array
52+
if (env->IsInstanceOf(a, objectArray)) {
53+
return pair<SAFEARRAY*,VARTYPE>(
54+
MultiDimArrayXducer<xducer::VariantXducer>::toNative(env, a),
55+
VT_VARIANT);
56+
}
57+
5058
return pair<SAFEARRAY*,VARTYPE>(NULL,VT_EMPTY);
5159
}
5260

native/safearray.h

Lines changed: 206 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ namespace safearray {
4444
bounds.cElements=length;
4545
bounds.lLbound=0;
4646
SAFEARRAY* psa = SafeArrayCreate(itemType,1,&bounds);
47-
47+
4848

4949
XDUCER::JavaType* pSrc = JARRAY::lock(env,static_cast<JARRAY::ARRAY>(javaArray));
5050
XDUCER::NativeType* pDst;
@@ -60,31 +60,220 @@ namespace safearray {
6060
}
6161

6262
static JavaType toJava( JNIEnv* env, NativeType psa ) {
63-
XDUCER::NativeType* pSrc;
63+
ToJavaMultiDimlArrayMarshaller<itemType, XDUCER> m(env, psa);
64+
return m.getJava();
65+
}
6466

65-
long lbound,ubound; HRESULT hr;
66-
hr = SafeArrayGetLBound(psa,1,&lbound);
67-
hr = SafeArrayGetUBound(psa,1,&ubound);
68-
// sometimes SafeArrayGetUBound returns -1 with S_OK. I haven't figured out what that means
69-
int size = max(0,ubound-lbound+1); // the range of index is [lbound,ubound]
67+
};
7068

71-
JARRAY::ARRAY a = JARRAY::newArray(env,size);
72-
XDUCER::JavaType* pDst = JARRAY::lock(env,a);
69+
// convert between SAFEARRAY and Java primitive array
70+
template < VARTYPE vt, class NT, class JT >
71+
class PrimitiveArrayXducer : public BasicArrayXducer< vt, xducer::IdentityXducer<NT,JT> > {
72+
};
73+
74+
75+
76+
// Class to marshal SAFEARRAY to Java multi dimensional array
77+
//
78+
// itemType : array item type
79+
// XDUCER : converter for each array item
80+
template < VARTYPE itemType, class XDUCER >
81+
class ToJavaMultiDimlArrayMarshaller {
82+
typedef array::Array<typename XDUCER::JavaType> JARRAY;
83+
typedef SAFEARRAY* NativeType;
84+
typedef jarray JavaType;
85+
86+
87+
private:
88+
JNIEnv* env;
89+
NativeType psa;
90+
JavaType javaArray;
91+
int dim;
92+
int* dimSizes;
93+
typename XDUCER::NativeType* pSrc;
94+
95+
public:
96+
ToJavaMultiDimlArrayMarshaller(JNIEnv* _env, NativeType _psa)
97+
: env(_env)
98+
, psa(_psa)
99+
, dim(0)
100+
, dimSizes(NULL)
101+
, pSrc(NULL)
102+
{
103+
dim = SafeArrayGetDim(psa);
104+
dimSizes = new int[dim];
105+
fillDimSizes(psa, dim, dimSizes);
73106
SafeArrayAccessData( psa, reinterpret_cast<void**>(&pSrc) );
107+
}
108+
109+
~ToJavaMultiDimlArrayMarshaller() {
110+
SafeArrayUnaccessData( psa );
111+
delete[] dimSizes;
112+
}
74113

75-
for( int i=0; i<size; i++ ) {
76-
pDst[i] = XDUCER::toJava(env,pSrc[i]);
114+
JavaType getJava() {
115+
return toJavaRec(pSrc, dim - 1);
116+
}
117+
118+
private:
119+
120+
JavaType toJavaRec(typename XDUCER::NativeType* &pSrc, int curDim) {
121+
if (curDim == 0) {
122+
JARRAY::ARRAY a = JARRAY::newArray(env, dimSizes[curDim]);
123+
XDUCER::JavaType* const pDst = JARRAY::lock(env, a);
124+
125+
for( int i=0; i < dimSizes[curDim]; i++ ) {
126+
pDst[i] = XDUCER::toJava(env, *(pSrc++));
127+
}
128+
JARRAY::unlock(env, a, pDst);
129+
return a;
77130
}
131+
else {
132+
jobjectArray a = array::Array<jobject>::newArray(env, dimSizes[curDim]);
133+
jobject* const pDst = array::Array<jobject>::lock(env, a);
78134

79-
SafeArrayUnaccessData( psa );
80-
JARRAY::unlock(env,a,pDst);
135+
for( int i = 0; i < dimSizes[curDim]; i++ ) {
136+
pDst[i] = toJavaRec(pSrc, curDim - 1);
137+
}
138+
array::Array<jobject>::unlock(env, a, pDst);
139+
return a;
140+
}
81141

82-
return a;
83142
}
143+
144+
static void fillDimSizes(NativeType psa, int dim, int *dimSizes) {
145+
for (int i = 1; i <= dim; ++i) {
146+
long lbound,ubound;
147+
SafeArrayGetLBound(psa,i,&lbound);
148+
SafeArrayGetUBound(psa,i,&ubound);
149+
// sometimes SafeArrayGetUBound returns -1 with S_OK. I haven't figured out what that means
150+
dimSizes[i-1] = max(0,ubound-lbound+1); // the range of index is [lbound,ubound]
151+
}
152+
}
153+
154+
84155
};
85156

86-
// convert between SAFEARRAY and Java primitive array
87-
template < VARTYPE vt, class NT, class JT >
88-
class PrimitiveArrayXducer : public BasicArrayXducer< vt, xducer::IdentityXducer<NT,JT> > {
157+
158+
159+
160+
// Transducer that turns a Java multi dimensinal array into SAFEARRAY
161+
//
162+
// itemType : array item type
163+
// XDUCER : converter for each array item
164+
template < class XDUCER >
165+
class MultiDimArrayXducer {
166+
public:
167+
typedef SAFEARRAY* NativeType;
168+
typedef jarray JavaType;
169+
170+
static NativeType toNative( JNIEnv* env, JavaType javaArray ) {
171+
ToNativeMultiDimlArrayMarshaller<VT_VARIANT, XDUCER> m(env, javaArray);
172+
return m.getNative();
173+
}
174+
175+
};
176+
177+
178+
179+
// Class to marshall Java multi dimensional arrays to SAFEARRAY
180+
//
181+
// itemType : array item type
182+
// XDUCER : converter for each array item
183+
template < VARTYPE itemType, class XDUCER >
184+
class ToNativeMultiDimlArrayMarshaller {
185+
typedef array::Array<typename XDUCER::JavaType> JARRAY;
186+
typedef SAFEARRAY* NativeType;
187+
typedef jarray JavaType;
188+
189+
190+
private:
191+
JNIEnv* env;
192+
JavaType javaArray;
193+
int dim;
194+
SAFEARRAYBOUND* bounds;
195+
SAFEARRAY* psa;
196+
typename XDUCER::NativeType* pDst;
197+
198+
public:
199+
ToNativeMultiDimlArrayMarshaller(JNIEnv* _env, JavaType _javaArray)
200+
: env(_env)
201+
, javaArray(_javaArray)
202+
, dim(0)
203+
, bounds(NULL)
204+
, psa(NULL)
205+
, pDst(NULL)
206+
{
207+
dim = getArrayDimension(env, javaArray);
208+
bounds = new SAFEARRAYBOUND[dim];
209+
fillBounds(env, javaArray, dim, bounds);
210+
psa = SafeArrayCreate(itemType, dim, bounds);
211+
SafeArrayAccessData( psa, reinterpret_cast<void**>(&pDst) );
212+
}
213+
214+
~ToNativeMultiDimlArrayMarshaller() {
215+
SafeArrayUnaccessData( psa );
216+
delete[] bounds;
217+
}
218+
219+
SAFEARRAY* getNative() {
220+
toNativeRec(javaArray, dim - 1);
221+
return psa;
222+
}
223+
224+
private:
225+
void toNativeRec(JavaType _array, int curDim) {
226+
227+
if (curDim == 0) {
228+
XDUCER::JavaType* pSrc = JARRAY::lock(env,static_cast<JARRAY::ARRAY>(_array));
229+
230+
for(size_t i = 0; i < bounds[curDim].cElements; i++)
231+
*(pDst++) = XDUCER::toNative(env, pSrc[i]);
232+
233+
JARRAY::unlock(env,static_cast<JARRAY::ARRAY>(_array), pSrc);
234+
235+
}
236+
else {
237+
JavaType* pSrc = array::Array<JavaType>::lock(env, static_cast<JARRAY::ARRAY>(_array));
238+
239+
for(size_t i = 0; i < bounds[curDim].cElements; i++)
240+
toNativeRec(pSrc[i], curDim - 1);
241+
242+
array::Array<JavaType>::unlock(env,static_cast<JARRAY::ARRAY>(_array), pSrc);
243+
}
244+
}
245+
246+
// gets dimensions of java multi index array
247+
static int getArrayDimension(JNIEnv* env, jobject a ) {
248+
int dim = 0;
249+
while(env->IsInstanceOf(a, objectArray)) {
250+
dim++;
251+
252+
if (env->GetArrayLength(static_cast<jobjectArray>(a)) > 0)
253+
a = env->GetObjectArrayElement(static_cast<jobjectArray>(a), 0);
254+
else
255+
return dim;
256+
};
257+
258+
return dim;
259+
}
260+
261+
// fills SAFEARRAYBOUND structure base on java array
262+
static void fillBounds(JNIEnv* env, jobject a, int n, SAFEARRAYBOUND* bounds) {
263+
int i = n - 1;
264+
while(true) {
265+
bounds[i].lLbound = 0;
266+
bounds[i].cElements = env->GetArrayLength(static_cast<jobjectArray>(a));
267+
268+
if (i <= 0)
269+
break;
270+
271+
a = env->GetObjectArrayElement(static_cast<jobjectArray>(a), 0);
272+
i--;
273+
}
274+
}
275+
89276
};
277+
278+
90279
}

test/src/test/java/VariantTest.java

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.math.BigDecimal;
99
import java.io.BufferedReader;
1010
import java.io.InputStreamReader;
11+
import java.util.Arrays;
1112

1213
/**
1314
* @author Kohsuke Kawaguchi
@@ -75,4 +76,87 @@ public void testCurrency() throws Exception {
7576
bd = new BigDecimal("1.99");
7677
assertTrue(bd.compareTo(t.testCurrency(new Holder<BigDecimal>(bd),const199))==0);
7778
}
79+
80+
public void testEmptyArray() throws Exception {
81+
ITestObject t = ClassFactory.createTestObject();
82+
Object[] a = {};
83+
Object[] b = (Object[]) t.testVariant(Variant.getMissing(), a);
84+
assertTrue(Arrays.deepEquals(a, b));
85+
}
86+
87+
public void testEmpty2DArray() throws Exception {
88+
ITestObject t = ClassFactory.createTestObject();
89+
Object[][] a = {{},{},{}};
90+
Object[] b = (Object[]) t.testVariant(Variant.getMissing(), a);
91+
assertTrue(Arrays.deepEquals(a, b));
92+
}
93+
94+
/**
95+
* Tests the currency type conversion.
96+
*/
97+
public void testArray() throws Exception {
98+
ITestObject t = ClassFactory.createTestObject();
99+
Object[] a = {"a1", "a2", "a3"};
100+
Object[] b = (Object[]) t.testVariant(Variant.getMissing(), a);
101+
assertTrue(Arrays.deepEquals(a, b));
102+
}
103+
104+
public void test2DArrays() throws Exception {
105+
ITestObject t = ClassFactory.createTestObject();
106+
107+
Object[][] a = {
108+
{"a11","a12"},
109+
{"a21","a22"},
110+
{"a31","a32"}
111+
};
112+
113+
Object[] b = (Object[]) t.testVariant(Variant.getMissing(), a);
114+
115+
assertTrue(Arrays.deepEquals(a, b));
116+
117+
}
118+
119+
public void test3DArrays() throws Exception {
120+
ITestObject t = ClassFactory.createTestObject();
121+
Object[][][] a = {
122+
{{"a111", "a112"},
123+
{"a121", "a122"}},
124+
{{"a211", "a212"},
125+
{"a221", "a222"}},
126+
{{"a311", "a312"},
127+
{"a321", "a322"}}
128+
};
129+
130+
Object b = t.testVariant(Variant.getMissing(), a);
131+
assertTrue(b instanceof Object[]);
132+
assertTrue(Arrays.deepEquals(a, (Object[])b));
133+
}
134+
135+
public void testDoubleArrays() throws Exception {
136+
ITestObject t = ClassFactory.createTestObject();
137+
138+
Object[][] a = {
139+
{1.1,1.2},
140+
{2.1,2.2},
141+
{3.1,3.2}
142+
};
143+
144+
Object[] b = (Object[]) t.testVariant(Variant.getMissing(), a);
145+
assertTrue(Arrays.deepEquals(a, b));
146+
}
147+
148+
public void testPrimitiveArrays() throws Exception {
149+
ITestObject t = ClassFactory.createTestObject();
150+
151+
double[][] a = {
152+
{1.1,1.2},
153+
{2.1,2.2},
154+
{3.1,3.2}
155+
};
156+
157+
Object[] b = (Object[]) t.testVariant(Variant.getMissing(), a);
158+
assertTrue(Arrays.deepEquals(a, b));
159+
}
160+
161+
78162
}

0 commit comments

Comments
 (0)