diff --git a/docs/ReleaseNotes.html b/docs/ReleaseNotes.html
index b8fbba9..2d5c25d 100644
--- a/docs/ReleaseNotes.html
+++ b/docs/ReleaseNotes.html
@@ -28,6 +28,10 @@
1775889 |
(M4) Fixed leak setString(int[],value) and other setString() methods |
+
+ | 1772783 |
+ (M4) Added VT_DECIMAL support for BigDecimals whose scale less than 28 |
+
| |
|
diff --git a/jni/Variant.cpp b/jni/Variant.cpp
index a63c79c..80af4f1 100644
--- a/jni/Variant.cpp
+++ b/jni/Variant.cpp
@@ -1062,8 +1062,156 @@ JNIEXPORT jint JNICALL Java_com_jacob_com_Variant_getVariantVariant
}
return NULL;
-
}
+ /**
+ * puts a VT_DECIMAL by reference
+ * Added 1.13M4
+ * */
+ JNIEXPORT void JNICALL Java_com_jacob_com_Variant_putVariantDecRef
+ (JNIEnv *env, jobject _this, jint signum, jint scale, jint lo, jint mid, jint hi)
+ {
+ VARIANT *v = extractVariant(env, _this);
+ if (v) {
+ VariantClear(v); // whatever was there before
+ DECIMAL *pd = (DECIMAL *)CoTaskMemAlloc(sizeof(DECIMAL));
+ pd->scale = scale;
+ pd->sign = signum == 1?0:0x80;
+ pd->Hi32 = hi;
+ pd->Mid32 = mid;
+ pd->Lo32 = lo;
+ V_VT(v) = VT_DECIMAL | VT_BYREF;
+ V_DECIMALREF(v) = pd;
+ }
+ }
-}
\ No newline at end of file
+
+ /**
+ * puts a VT_DECIMAL
+ * Added 1.13M4
+ * */
+ JNIEXPORT void JNICALL Java_com_jacob_com_Variant_putVariantDec
+ (JNIEnv *env, jobject _this, jint signum, jint scale, jint lo, jint mid, jint hi)
+ {
+ VARIANT *v = extractVariant(env, _this);
+ DECIMAL *d;
+ if (v) {
+ VariantClear(v); // whatever was there before
+ d = (DECIMAL*)v;
+ d->scale = scale;
+ d->sign = signum == 1?0:0x80;
+ d->Hi32 = hi;
+ d->Mid32 = mid;
+ d->Lo32 = lo;
+ V_VT(v) = VT_DECIMAL;
+ }
+ }
+
+/**
+ * utility method used by the getVariantXXX() methods to convert VT_DECIMAL to BigDecimal
+ * */
+jobject extractDecimal
+ (JNIEnv *env, DECIMAL* d)
+ {
+ jclass bigIntegerClass;
+ jclass bigDecimalClass;
+ jobject integer;
+ jmethodID bigIntegerConstructor;
+ jmethodID bigDecimalConstructor;
+ jbyteArray bArray;
+ jobject result = NULL;
+ jbyte* buffer;
+
+ bigIntegerClass = env->FindClass("java/math/BigInteger");
+ if (bigIntegerClass == NULL)
+ return NULL;
+ bigDecimalClass = env->FindClass("java/math/BigDecimal");
+ if (bigDecimalClass == NULL) {
+ env->DeleteLocalRef(bigIntegerClass);
+ return NULL;
+ }
+
+ bigIntegerConstructor = env->GetMethodID(bigIntegerClass, "", "(I[B)V");
+ if (bigIntegerConstructor == NULL) {
+ env->DeleteLocalRef(bigIntegerClass);
+ env->DeleteLocalRef(bigDecimalClass);
+ return NULL;
+ }
+ bigDecimalConstructor = env->GetMethodID(bigDecimalClass, "", "(Ljava/math/BigInteger;I)V");
+ if (bigIntegerConstructor == NULL) {
+ env->DeleteLocalRef(bigIntegerClass);
+ env->DeleteLocalRef(bigDecimalClass);
+ return NULL;
+ }
+ bArray = env->NewByteArray(12);
+ if (bArray == NULL) {
+ env->DeleteLocalRef(bigIntegerClass);
+ env->DeleteLocalRef(bigDecimalClass);
+ return NULL;
+ }
+ /* Unfortunately the byte ordering is completely wrong, so we remap it into buffer */
+ buffer = (jbyte*)malloc(12);
+ buffer[11] = d->Lo32 & 255;
+ buffer[10] = (d->Lo32 >> 8) & 255;
+ buffer[9] = (d->Lo32 >> 16) & 255;;
+ buffer[8] = (d->Lo32 >> 24) & 255;;
+ buffer[7] = (d->Mid32) & 255;;
+ buffer[6] = (d->Mid32 >> 8) & 255;
+ buffer[5] = (d->Mid32 >> 16) & 255;
+ buffer[4] = (d->Mid32 >> 24) & 255;
+ buffer[3] = (d->Hi32) & 255;
+ buffer[2] = (d->Hi32 >> 8) & 255;
+ buffer[1] = (d->Hi32 >> 16) & 255;
+ buffer[0] = (d->Hi32 >> 24) & 255;
+ /* Load buffer into the actual array */
+ env->SetByteArrayRegion(bArray, 0, 12, buffer);
+ /* then clean up the C array */
+ free(buffer);
+
+ /* instantiate the BigInteger */
+ integer = env->NewObject(bigIntegerClass, bigIntegerConstructor, d->sign == 0x80?-1:1, bArray);
+
+ result = env->NewObject(bigDecimalClass, bigDecimalConstructor, integer, (jint)(d->scale));
+
+ /* Clean up the Java global references */
+ env->DeleteLocalRef(bArray);
+ env->DeleteLocalRef(integer);
+ env->DeleteLocalRef(bigIntegerClass);
+ return result;
+ }
+
+/**
+ * gets a VT_DECIMAL by ref as a BigDecimal
+ * Added 1.13M4
+ * */
+JNIEXPORT jobject JNICALL Java_com_jacob_com_Variant_getVariantDecRef
+ (JNIEnv *env, jobject _this)
+ {
+ VARIANT *v = extractVariant(env, _this);
+ if (v) {
+ if (V_VT(v) != (VT_DECIMAL|VT_BYREF)) {
+ return NULL;
+ }
+ return extractDecimal(env, v->pdecVal);
+ }
+ return NULL;
+ }
+
+/**
+ * gets a VT_DECIMAL as a BigDecimal
+ * Added 1.13M4
+ * */
+JNIEXPORT jobject JNICALL Java_com_jacob_com_Variant_getVariantDec
+ (JNIEnv *env, jobject _this)
+ {
+ VARIANT *v = extractVariant(env, _this);
+ if (v) {
+ if (V_VT(v) != VT_DECIMAL) {
+ return NULL;
+ }
+ return extractDecimal(env, (DECIMAL*)v);
+ }
+ return NULL;
+ }
+
+}
diff --git a/jni/Variant.h b/jni/Variant.h
index cd68885..a70e3b3 100644
--- a/jni/Variant.h
+++ b/jni/Variant.h
@@ -513,6 +513,40 @@ JNIEXPORT void JNICALL Java_com_jacob_com_Variant_putVariantVariant
JNIEXPORT jint JNICALL Java_com_jacob_com_Variant_getVariantVariant
(JNIEnv *, jobject);
+/*
+ * Class: com_jacob_com_Variant
+ * Method: putVariantDecRef
+ * Signature: (Ljava.math.BigDecimal;)V
+ */
+JNIEXPORT void JNICALL Java_com_jacob_com_Variant_putVariantDecRef
+ (JNIEnv *env, jobject _this, jint signum, jint scale, jint lo, jint mid, jint hi);
+
+
+/*
+ * Class: com_jacob_com_Variant
+ * Method: putVariantDec
+ * Signature: (Ljava.math.BigDecimal;)V
+ */
+JNIEXPORT void JNICALL Java_com_jacob_com_Variant_putVariantDec
+ (JNIEnv *env, jobject _this, jint signum, jint scale, jint lo, jint mid, jint hi);
+
+
+/*
+ * Class: com_jacob_com_Variant
+ * Method: getVariantDecRef
+ * Signature: ()Ljava.math.BigDecimal
+ */
+JNIEXPORT jobject JNICALL Java_com_jacob_com_Variant_getVariantDecRef
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_jacob_com_Variant
+ * Method: getVariantDec
+ * Signature: ()Ljava.math.BigDecimal
+ */
+JNIEXPORT jobject JNICALL Java_com_jacob_com_Variant_getVariantDec
+ (JNIEnv *, jobject);
+
/*
* Class: com_jacob_com_Variant
* Method: isVariantConsideredNull
diff --git a/src/com/jacob/com/Variant.java b/src/com/jacob/com/Variant.java
index f11d97f..b2e8afb 100644
--- a/src/com/jacob/com/Variant.java
+++ b/src/com/jacob/com/Variant.java
@@ -20,6 +20,8 @@
package com.jacob.com;
import java.util.Date;
+import java.math.BigDecimal;
+import java.math.BigInteger;
/**
* The multi-format data type used for all call backs and most communications
@@ -118,6 +120,9 @@ public class Variant extends JacobObject {
/** variant's type is object VT_UNKNOWN*/
public static final short VariantObject = 13;
+ /** variant's type is object VT_DECIMAL*/
+ public static final short VariantDecimal = 14;
+
/** variant's type is byte VT_UI1 */
public static final short VariantByte = 17;
@@ -295,6 +300,40 @@ public class Variant extends JacobObject {
putVariantIntRef(in);
}
+ /**
+ * private JNI method called by putDecimalRef
+ * @param signum sign
+ * @param scale BigDecimal's scale
+ * @param lo low 32 bits
+ * @param mid middle 32 bits
+ * @param hi high 32 bits
+ */
+ private native void putVariantDecRef(int signum, int scale, int lo, int mid, int hi);
+
+ /**
+ * Set the content of this variant to an int (VT_DECIMAL|VT_BYREF)
+ * This may throw exceptions more often than the caller expects because
+ * most callers don't manage the scale of their BigDecimal objects.
+ * @param in the BigDecimal that will be converted to VT_DECIMAL
+ * @throws IllegalArgumentException if the scale is > 28, the maximum for VT_DECIMAL
+ */
+ public void putDecimalRef(BigDecimal in){
+ // verify we aren't released
+ getvt();
+ int scale = in.scale();
+ if (scale > 28) {
+ // should this really cast to a string and call putStringRef()?
+ throw new IllegalArgumentException("VT_DECIMAL only supports a scale of 28 and the passed"+
+ " in value has a scale of "+scale);
+ }
+ else {
+ BigInteger unscaled = in.unscaledValue();
+ BigInteger shifted = unscaled.shiftRight(32);
+ putVariantDecRef(in.signum(), scale, unscaled.intValue(), shifted.intValue(), shifted.shiftRight(32).intValue());
+ }
+ }
+
+
/**
* set the content of this variant to a double (VT_R8|VT_BYREF)
* @param in
@@ -744,6 +783,81 @@ public class Variant extends JacobObject {
putVariantInt(in);
}
+
+ /**
+ * @return the value in this Variant as a decimal, null if not a decimal
+ */
+ private native Object getVariantDec();
+
+
+ /**
+ * @return the value in this Variant (byref) as a decimal, null if not a decimal
+ */
+ private native Object getVariantDecRef();
+
+
+ /**
+ * return the BigDecimal value held in this variant (fails on other types)
+ * @return BigDecimal
+ * @throws IllegalStateException if variant is not of the requested type
+ */
+ public BigDecimal getDecimal(){
+ if (this.getvt() == VariantDecimal){
+ return (BigDecimal)(getVariantDec());
+ } else {
+ throw new IllegalStateException(
+ "getDecimal() only legal on Variants of type VariantDecimal, not "+this.getvt());
+ }
+ }
+
+ /**
+ * return the BigDecimal value held in this variant (fails on other types)
+ * @return BigDecimal
+ * @throws IllegalStateException if variant is not of the requested type
+ */
+ public BigDecimal getDecimalRef(){
+ if ((this.getvt() & VariantDecimal) == VariantDecimal &&
+ (this.getvt() & VariantByref) == VariantByref) {
+ return (BigDecimal)(getVariantDecRef());
+ } else {
+ throw new IllegalStateException(
+ "getDecimalRef() only legal on byRef Variants of type VariantDecimal, not "+this.getvt());
+ }
+ }
+
+ /**
+ * private JNI method called by putDecimal
+ * @param signum sign
+ * @param scale BigDecimal's scale
+ * @param lo low 32 bits
+ * @param mid middle 32 bits
+ * @param hi high 32 bits
+ */
+ private native void putVariantDec(int signum, int scale, int lo, int mid, int hi);
+
+ /**
+ * Set the value of this variant and set the type.
+ * This may throw exceptions more often than the caller expects because
+ * most callers don't manage the scale of their BigDecimal objects.
+ * @param in the big decimal that will convert to the VT_DECIMAL type
+ * @throws IllegalArgumentException if the scale is > 28, the maximum for VT_DECIMAL
+ */
+ public void putDecimal(BigDecimal in){
+ // verify we aren't released yet
+ getvt();
+ int scale = in.scale();
+ if (scale > 28) {
+ // should this really cast to a string and call putStringRef()?
+ throw new IllegalArgumentException("VT_DECIMAL only supports a scale of 28 and the passed"+
+ " in value has a scale of "+scale);
+ }
+ else {
+ BigInteger unscaled = in.unscaledValue();
+ BigInteger shifted = unscaled.shiftRight(32);
+ putVariantDec(in.signum(), in.scale(), unscaled.intValue(), shifted.intValue(), shifted.shiftRight(32).intValue());
+ }
+ }
+
/**
* set the value of this variant
* @param in
@@ -1514,6 +1628,11 @@ public class Variant extends JacobObject {
putFloatRef(((Float) pValueObject).floatValue());
else
putFloat(((Float) pValueObject).floatValue());
+ } else if (pValueObject instanceof BigDecimal) {
+ if (fByRef)
+ putDecimalRef(((BigDecimal) pValueObject));
+ else
+ putDecimal(((BigDecimal) pValueObject));
} else if (pValueObject instanceof Byte){
if (fByRef){
putByteRef(((Byte)pValueObject).byteValue());
@@ -1805,6 +1924,12 @@ public class Variant extends JacobObject {
case Variant.VariantObject : //13
result = new NotImplementedException("toJavaObject() Not implemented for VariantObject");
break;
+ case Variant.VariantDecimal : //14
+ result = getDecimal();
+ break;
+ case Variant.VariantDecimal | Variant.VariantByref: //14
+ result = getDecimalRef();
+ break;
case Variant.VariantByte : //17
result = new Byte(this.getByte());
break;
diff --git a/unittest/com/jacob/com/VariantTest.java b/unittest/com/jacob/com/VariantTest.java
index 6af029e..dd2b3df 100644
--- a/unittest/com/jacob/com/VariantTest.java
+++ b/unittest/com/jacob/com/VariantTest.java
@@ -1,5 +1,6 @@
package com.jacob.com;
+import java.math.BigDecimal;
import java.util.Date;
import com.jacob.test.BaseTestCase;
@@ -72,6 +73,17 @@ public class VariantTest extends BaseTestCase {
+" java objects come out the same");
}
+ // Ugh, you have to pick a magic number whose scale is less than 28
+ // 53.53 had a scale of 64 and 53.52 had a scale of 47
+ BigDecimal testDecimal = new BigDecimal(53.50);
+ v = new Variant(testDecimal,false);
+ vByRef = new Variant(testDecimal,true);
+ if (!v.toJavaObject().equals(vByRef.toJavaObject())){
+ fail(v.toString() + " could not make type "
+ + v.getvt() +" and "+ vByRef.getvt()
+ +" java objects come out the same");
+ }
+
Date now = new Date();
v = new Variant(now,false);
vByRef = new Variant(now,true);
@@ -307,42 +319,42 @@ public class VariantTest extends BaseTestCase {
*/
public void testPutsAndGets(){
Variant v = new Variant();
- v.putInt(10);
- if (v.getInt() != 10){
- fail("int test failed");
- }
- v.putShort((short)10);
- if (v.getShort() != 10){
- fail("short test failed");
- }
- v.putByte((byte)10);
- if (v.getByte() != 10){
- fail("int test failed");
- }
- v.putFloat(10);
- if (v.getFloat() != 10.0){
+
+ v.putInt((int)10);
+ assertEquals("int test failed", (int)10, v.getInt());
+
+ v.putShort((short)20);
+ assertEquals("short test failed", (short)20, v.getShort());
+
+ v.putByte((byte)30);
+ assertEquals("byte test failed", (byte)30, v.getByte());
+
+ v.putFloat(40);
+ if (v.getFloat() != 40.0){
fail("float test failed");
}
- v.putDouble(10);
- if (v.getDouble() != 10.0){
+
+ v.putDouble(50);
+ if (v.getDouble() != 50.0){
fail("double test failed");
}
+
v.putString("1234.567");
- if (!"1234.567".equals(v.getString())){
- fail("string test failed");
- }
+ assertEquals("string test failed","1234.567", v.getString());
+
v.putBoolean(true);
- if (v.getBoolean() != true){
- fail("failed boolean test(true)");
- }
+ assertEquals("failed boolean test(true)",true, v.getBoolean());
+
v.putBoolean(false);
- if (v.getBoolean() != false){
- fail("failed boolean test(false)");
- }
+ assertEquals("failed boolean test(false)",false,v.getBoolean());
+
v.putCurrency(123456789123456789L);
- if (v.getCurrency()!=123456789123456789L){
- fail("failed currency test");
- }
+ assertEquals("failed currency test",123456789123456789L, v.getCurrency());
+
+ BigDecimal testDecimal = new BigDecimal("22.222");
+ v.putDecimal(testDecimal);
+ assertEquals("failed BigDecimal test", testDecimal,v.getDecimal());
+
Date ourDate = new Date();
v.putDate(ourDate);