Skip to content

Commit

Permalink
adding VARIANT marshalling with multidimensional SAFEARRAY inside
Browse files Browse the repository at this point in the history
  • Loading branch information
kllbzz committed Aug 3, 2014
1 parent 6d3150f commit 9ccbaf3
Show file tree
Hide file tree
Showing 3 changed files with 298 additions and 17 deletions.
8 changes: 8 additions & 0 deletions native/safearray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ pair<SafeArrayXducer::NativeType,VARTYPE> SafeArrayXducer::toNative2(
if(env->IsSameObject(clz,*(e->clazz)))
return pair<SAFEARRAY*,VARTYPE>( (e->toNative)(env,a), e->vt );
}

// Try as multi dimensinal array
if (env->IsInstanceOf(a, objectArray)) {
return pair<SAFEARRAY*,VARTYPE>(
MultiDimArrayXducer<xducer::VariantXducer>::toNative(env, a),
VT_VARIANT);
}

return pair<SAFEARRAY*,VARTYPE>(NULL,VT_EMPTY);
}

Expand Down
223 changes: 206 additions & 17 deletions native/safearray.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ namespace safearray {
bounds.cElements=length;
bounds.lLbound=0;
SAFEARRAY* psa = SafeArrayCreate(itemType,1,&bounds);


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

static JavaType toJava( JNIEnv* env, NativeType psa ) {
XDUCER::NativeType* pSrc;
ToJavaMultiDimlArrayMarshaller<itemType, XDUCER> m(env, psa);
return m.getJava();
}

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

JARRAY::ARRAY a = JARRAY::newArray(env,size);
XDUCER::JavaType* pDst = JARRAY::lock(env,a);
// convert between SAFEARRAY and Java primitive array
template < VARTYPE vt, class NT, class JT >
class PrimitiveArrayXducer : public BasicArrayXducer< vt, xducer::IdentityXducer<NT,JT> > {
};



// Class to marshal SAFEARRAY to Java multi dimensional array
//
// itemType : array item type
// XDUCER : converter for each array item
template < VARTYPE itemType, class XDUCER >
class ToJavaMultiDimlArrayMarshaller {
typedef array::Array<typename XDUCER::JavaType> JARRAY;
typedef SAFEARRAY* NativeType;
typedef jarray JavaType;


private:
JNIEnv* env;
NativeType psa;
JavaType javaArray;
int dim;
int* dimSizes;
typename XDUCER::NativeType* pSrc;

public:
ToJavaMultiDimlArrayMarshaller(JNIEnv* _env, NativeType _psa)
: env(_env)
, psa(_psa)
, dim(0)
, dimSizes(NULL)
, pSrc(NULL)
{
dim = SafeArrayGetDim(psa);
dimSizes = new int[dim];
fillDimSizes(psa, dim, dimSizes);
SafeArrayAccessData( psa, reinterpret_cast<void**>(&pSrc) );
}

~ToJavaMultiDimlArrayMarshaller() {
SafeArrayUnaccessData( psa );
delete[] dimSizes;
}

for( int i=0; i<size; i++ ) {
pDst[i] = XDUCER::toJava(env,pSrc[i]);
JavaType getJava() {
return toJavaRec(pSrc, dim - 1);
}

private:

JavaType toJavaRec(typename XDUCER::NativeType* &pSrc, int curDim) {
if (curDim == 0) {
JARRAY::ARRAY a = JARRAY::newArray(env, dimSizes[curDim]);
XDUCER::JavaType* const pDst = JARRAY::lock(env, a);

for( int i=0; i < dimSizes[curDim]; i++ ) {
pDst[i] = XDUCER::toJava(env, *(pSrc++));
}
JARRAY::unlock(env, a, pDst);
return a;
}
else {
jobjectArray a = array::Array<jobject>::newArray(env, dimSizes[curDim]);
jobject* const pDst = array::Array<jobject>::lock(env, a);

SafeArrayUnaccessData( psa );
JARRAY::unlock(env,a,pDst);
for( int i = 0; i < dimSizes[curDim]; i++ ) {
pDst[i] = toJavaRec(pSrc, curDim - 1);
}
array::Array<jobject>::unlock(env, a, pDst);
return a;
}

return a;
}

static void fillDimSizes(NativeType psa, int dim, int *dimSizes) {
for (int i = 1; i <= dim; ++i) {
long lbound,ubound;
SafeArrayGetLBound(psa,i,&lbound);
SafeArrayGetUBound(psa,i,&ubound);
// sometimes SafeArrayGetUBound returns -1 with S_OK. I haven't figured out what that means
dimSizes[i-1] = max(0,ubound-lbound+1); // the range of index is [lbound,ubound]
}
}


};

// convert between SAFEARRAY and Java primitive array
template < VARTYPE vt, class NT, class JT >
class PrimitiveArrayXducer : public BasicArrayXducer< vt, xducer::IdentityXducer<NT,JT> > {



// Transducer that turns a Java multi dimensinal array into SAFEARRAY
//
// itemType : array item type
// XDUCER : converter for each array item
template < class XDUCER >
class MultiDimArrayXducer {
public:
typedef SAFEARRAY* NativeType;
typedef jarray JavaType;

static NativeType toNative( JNIEnv* env, JavaType javaArray ) {
ToNativeMultiDimlArrayMarshaller<VT_VARIANT, XDUCER> m(env, javaArray);
return m.getNative();
}

};



// Class to marshall Java multi dimensional arrays to SAFEARRAY
//
// itemType : array item type
// XDUCER : converter for each array item
template < VARTYPE itemType, class XDUCER >
class ToNativeMultiDimlArrayMarshaller {
typedef array::Array<typename XDUCER::JavaType> JARRAY;
typedef SAFEARRAY* NativeType;
typedef jarray JavaType;


private:
JNIEnv* env;
JavaType javaArray;
int dim;
SAFEARRAYBOUND* bounds;
SAFEARRAY* psa;
typename XDUCER::NativeType* pDst;

public:
ToNativeMultiDimlArrayMarshaller(JNIEnv* _env, JavaType _javaArray)
: env(_env)
, javaArray(_javaArray)
, dim(0)
, bounds(NULL)
, psa(NULL)
, pDst(NULL)
{
dim = getArrayDimension(env, javaArray);
bounds = new SAFEARRAYBOUND[dim];
fillBounds(env, javaArray, dim, bounds);
psa = SafeArrayCreate(itemType, dim, bounds);
SafeArrayAccessData( psa, reinterpret_cast<void**>(&pDst) );
}

~ToNativeMultiDimlArrayMarshaller() {
SafeArrayUnaccessData( psa );
delete[] bounds;
}

SAFEARRAY* getNative() {
toNativeRec(javaArray, dim - 1);
return psa;
}

private:
void toNativeRec(JavaType _array, int curDim) {

if (curDim == 0) {
XDUCER::JavaType* pSrc = JARRAY::lock(env,static_cast<JARRAY::ARRAY>(_array));

for(size_t i = 0; i < bounds[curDim].cElements; i++)
*(pDst++) = XDUCER::toNative(env, pSrc[i]);

JARRAY::unlock(env,static_cast<JARRAY::ARRAY>(_array), pSrc);

}
else {
JavaType* pSrc = array::Array<JavaType>::lock(env, static_cast<JARRAY::ARRAY>(_array));

for(size_t i = 0; i < bounds[curDim].cElements; i++)
toNativeRec(pSrc[i], curDim - 1);

array::Array<JavaType>::unlock(env,static_cast<JARRAY::ARRAY>(_array), pSrc);
}
}

// gets dimensions of java multi index array
static int getArrayDimension(JNIEnv* env, jobject a ) {
int dim = 0;
while(env->IsInstanceOf(a, objectArray)) {
dim++;

if (env->GetArrayLength(static_cast<jobjectArray>(a)) > 0)
a = env->GetObjectArrayElement(static_cast<jobjectArray>(a), 0);
else
return dim;
};

return dim;
}

// fills SAFEARRAYBOUND structure base on java array
static void fillBounds(JNIEnv* env, jobject a, int n, SAFEARRAYBOUND* bounds) {
int i = n - 1;
while(true) {
bounds[i].lLbound = 0;
bounds[i].cElements = env->GetArrayLength(static_cast<jobjectArray>(a));

if (i <= 0)
break;

a = env->GetObjectArrayElement(static_cast<jobjectArray>(a), 0);
i--;
}
}

};


}
84 changes: 84 additions & 0 deletions test/src/test/java/VariantTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.math.BigDecimal;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;

/**
* @author Kohsuke Kawaguchi
Expand Down Expand Up @@ -75,4 +76,87 @@ public void testCurrency() throws Exception {
bd = new BigDecimal("1.99");
assertTrue(bd.compareTo(t.testCurrency(new Holder<BigDecimal>(bd),const199))==0);
}

public void testEmptyArray() throws Exception {
ITestObject t = ClassFactory.createTestObject();
Object[] a = {};
Object[] b = (Object[]) t.testVariant(Variant.getMissing(), a);
assertTrue(Arrays.deepEquals(a, b));
}

public void testEmpty2DArray() throws Exception {
ITestObject t = ClassFactory.createTestObject();
Object[][] a = {{},{},{}};
Object[] b = (Object[]) t.testVariant(Variant.getMissing(), a);
assertTrue(Arrays.deepEquals(a, b));
}

/**
* Tests the currency type conversion.
*/
public void testArray() throws Exception {
ITestObject t = ClassFactory.createTestObject();
Object[] a = {"a1", "a2", "a3"};
Object[] b = (Object[]) t.testVariant(Variant.getMissing(), a);
assertTrue(Arrays.deepEquals(a, b));
}

public void test2DArrays() throws Exception {
ITestObject t = ClassFactory.createTestObject();

Object[][] a = {
{"a11","a12"},
{"a21","a22"},
{"a31","a32"}
};

Object[] b = (Object[]) t.testVariant(Variant.getMissing(), a);

assertTrue(Arrays.deepEquals(a, b));

}

public void test3DArrays() throws Exception {
ITestObject t = ClassFactory.createTestObject();
Object[][][] a = {
{{"a111", "a112"},
{"a121", "a122"}},
{{"a211", "a212"},
{"a221", "a222"}},
{{"a311", "a312"},
{"a321", "a322"}}
};

Object b = t.testVariant(Variant.getMissing(), a);
assertTrue(b instanceof Object[]);
assertTrue(Arrays.deepEquals(a, (Object[])b));
}

public void testDoubleArrays() throws Exception {
ITestObject t = ClassFactory.createTestObject();

Object[][] a = {
{1.1,1.2},
{2.1,2.2},
{3.1,3.2}
};

Object[] b = (Object[]) t.testVariant(Variant.getMissing(), a);
assertTrue(Arrays.deepEquals(a, b));
}

public void testPrimitiveArrays() throws Exception {
ITestObject t = ClassFactory.createTestObject();

double[][] a = {
{1.1,1.2},
{2.1,2.2},
{3.1,3.2}
};

Object[] b = (Object[]) t.testVariant(Variant.getMissing(), a);
assertTrue(Arrays.deepEquals(a, b));
}


}

0 comments on commit 9ccbaf3

Please sign in to comment.