Package com.jacob.com

Source Code of com.jacob.com.VariantUtilities

/**
*
*/
package com.jacob.com;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.util.Date;

/**
* A utility class used to convert between Java objects and Variants
*/
public final class VariantUtilities {
  private VariantUtilities() {
    // utility class with only static methods don't need constructors
  }

  /**
   * Populates a variant object from a java object. This method attempts to
   * figure out the appropriate Variant type
   *
   * @param targetVariant
   * @param pValueObject
   * @param fByRef
   */
  protected static void populateVariant(Variant targetVariant,
      Object pValueObject, boolean fByRef) {
    if (pValueObject == null) {
      targetVariant.putEmpty();
    } else if (pValueObject instanceof Integer) {
      if (fByRef) {
        targetVariant.putIntRef(((Integer) pValueObject).intValue());
      } else {
        targetVariant.putInt(((Integer) pValueObject).intValue());
      }
    } else if (pValueObject instanceof Short) {
      if (fByRef) {
        targetVariant.putShortRef(((Short) pValueObject).shortValue());
      } else {
        targetVariant.putShort(((Short) pValueObject).shortValue());
      }
    } else if (pValueObject instanceof String) {
      if (fByRef) {
        targetVariant.putStringRef((String) pValueObject);
      } else {
        targetVariant.putString((String) pValueObject);
      }
    } else if (pValueObject instanceof Boolean) {
      if (fByRef) {
        targetVariant.putBooleanRef(((Boolean) pValueObject)
            .booleanValue());
      } else {
        targetVariant.putBoolean(((Boolean) pValueObject)
            .booleanValue());
      }
    } else if (pValueObject instanceof Double) {
      if (fByRef) {
        targetVariant.putDoubleRef(((Double) pValueObject)
            .doubleValue());
      } else {
        targetVariant.putDouble(((Double) pValueObject).doubleValue());
      }
    } else if (pValueObject instanceof Float) {
      if (fByRef) {
        targetVariant.putFloatRef(((Float) pValueObject).floatValue());
      } else {
        targetVariant.putFloat(((Float) pValueObject).floatValue());
      }
    } else if (pValueObject instanceof BigDecimal) {
      if (fByRef) {
        targetVariant.putDecimalRef(((BigDecimal) pValueObject));
      } else {
        targetVariant.putDecimal(((BigDecimal) pValueObject));
      }
    } else if (pValueObject instanceof Byte) {
      if (fByRef) {
        targetVariant.putByteRef(((Byte) pValueObject).byteValue());
      } else {
        targetVariant.putByte(((Byte) pValueObject).byteValue());
      }
    } else if (pValueObject instanceof Date) {
      if (fByRef) {
        targetVariant.putDateRef((Date) pValueObject);
      } else {
        targetVariant.putDate((Date) pValueObject);
      }
    } else if (pValueObject instanceof Long) {
      if (fByRef) {
        targetVariant.putLongRef(((Long) pValueObject).longValue());
      } else {
        targetVariant.putLong(((Long) pValueObject).longValue());
      }
    } else if (pValueObject instanceof Currency) {
      if (fByRef) {
        targetVariant.putCurrencyRef(((Currency) pValueObject));
      } else {
        targetVariant.putCurrency(((Currency) pValueObject));
      }
    } else if (pValueObject instanceof SafeArray) {
      if (fByRef) {
        targetVariant.putSafeArrayRef((SafeArray) pValueObject);
      } else {
        targetVariant.putSafeArray((SafeArray) pValueObject);
      }
    } else if (pValueObject instanceof Dispatch) {
      if (fByRef) {
        targetVariant.putDispatchRef((Dispatch) pValueObject);
      } else {
        targetVariant.putDispatch((Dispatch) pValueObject);
      }
    } else if (pValueObject instanceof Variant) {
      // newly added 1.12-pre6 to support VT_VARIANT
      targetVariant.putVariant(pValueObject);
    } else {
      // should really throw an illegal argument exception if its an
      // invalid type
      if (fByRef) {
        targetVariant.putObjectRef(pValueObject);
      } else {
        targetVariant.putObject(pValueObject);
      }
    }
  }

  /**
   * Map arguments based on msdn documentation. This method relies on the
   * variant constructor except for arrays.
   *
   * @param objectToBeMadeIntoVariant
   * @return Variant that represents the object
   */
  @SuppressWarnings("unchecked")
  protected static Variant objectToVariant(Object objectToBeMadeIntoVariant) {
    if (objectToBeMadeIntoVariant == null) {
      return new Variant();
    } else if (objectToBeMadeIntoVariant instanceof Variant) {
      // if a variant was passed in then be a slacker and just return it
      return (Variant) objectToBeMadeIntoVariant;
    } else if (objectToBeMadeIntoVariant.getClass().isArray()) {
      // automatically convert arrays using reflection
      SafeArray sa = null;
      int len1 = Array.getLength(objectToBeMadeIntoVariant);
      Object first = Array.get(objectToBeMadeIntoVariant, 0);
      if (first.getClass().isArray()) {
        int max = 0;
        for (int i = 0; i < len1; i++) {
          Object e1 = Array.get(objectToBeMadeIntoVariant, i);
          int len2 = Array.getLength(e1);
          if (max < len2) {
            max = len2;
          }
        }
        sa = new SafeArray(Variant.VariantVariant, len1, max);
        for (int i = 0; i < len1; i++) {
          Object e1 = Array.get(objectToBeMadeIntoVariant, i);
          for (int j = 0; j < Array.getLength(e1); j++) {
            sa.setVariant(i, j, objectToVariant(Array.get(e1, j)));
          }
        }
      } else {
        sa = new SafeArray(Variant.VariantVariant, len1);
        for (int i = 0; i < len1; i++) {
          sa.setVariant(i, objectToVariant(Array.get(
              objectToBeMadeIntoVariant, i)));
        }
      }
      Variant returnVariant = new Variant();
      populateVariant(returnVariant, sa, false);
      return returnVariant;
    } else {
      // rely on populateVariant to throw an exception if its an
      // invalid type
      Variant returnVariant = new Variant();
      populateVariant(returnVariant, objectToBeMadeIntoVariant, false);
      return returnVariant;
    }
  }

  /**
   * converts an array of objects into an array of Variants by repeatedly
   * calling obj2Variant(Object)
   *
   * @param arrayOfObjectsToBeConverted
   * @return Variant[]
   */
  protected static Variant[] objectsToVariants(
      Object[] arrayOfObjectsToBeConverted) {
    Variant vArg[] = new Variant[arrayOfObjectsToBeConverted.length];
    for (int i = 0; i < arrayOfObjectsToBeConverted.length; i++) {
      vArg[i] = objectToVariant(arrayOfObjectsToBeConverted[i]);
    }
    return vArg;
  }

  /**
   * Convert a JACOB Variant value to a Java object (type conversions).
   * provided in Sourceforge feature request 959381. A fix was done to handle
   * byRef bug report 1607878.
   * <p>
   * Unlike other toXXX() methods, it does not do a type conversion except for
   * special data types (it shouldn't do any!)
   * <p>
   * Converts Variant.VariantArray types to SafeArrays
   *
   * @return Corresponding Java object of the type matching the Variant type.
   * @throws IllegalStateException
   *             if no underlying windows data structure
   * @throws NotImplementedException
   *             if unsupported conversion is requested
   * @throws JacobException
   *             if the calculated result was a JacobObject usually as a
   *             result of error
   */
  protected static Object variantToObject(Variant sourceData) {
    Object result = null;

    short type = sourceData.getvt(); // variant type

    if ((type & Variant.VariantArray) == Variant.VariantArray) { // array
      // returned?
      SafeArray array = null;
      type = (short) (type - Variant.VariantArray);
      // From SF Bug 1840487
      // This did call toSafeArray(false) but that meant
      // this was the only variantToObject() that didn't have its own
      // copy of the data so you would end up with weird run time
      // errors after some GC. So now we just get stupid about it and
      // always make a copy just like toSafeArray() does.
      array = sourceData.toSafeArray();
      result = array;
    } else { // non-array object returned
      switch (type) {
      case Variant.VariantEmpty: // 0
      case Variant.VariantNull: // 1
        break;
      case Variant.VariantShort: // 2
        result = new Short(sourceData.getShort());
        break;
      case Variant.VariantShort | Variant.VariantByref: // 2
        result = new Short(sourceData.getShortRef());
        break;
      case Variant.VariantInt: // 3
        result = new Integer(sourceData.getInt());
        break;
      case Variant.VariantInt | Variant.VariantByref: // 3
        result = new Integer(sourceData.getIntRef());
        break;
      case Variant.VariantFloat: // 4
        result = new Float(sourceData.getFloat());
        break;
      case Variant.VariantFloat | Variant.VariantByref: // 4
        result = new Float(sourceData.getFloatRef());
        break;
      case Variant.VariantDouble: // 5
        result = new Double(sourceData.getDouble());
        break;
      case Variant.VariantDouble | Variant.VariantByref: // 5
        result = new Double(sourceData.getDoubleRef());
        break;
      case Variant.VariantCurrency: // 6
        result = sourceData.getCurrency();
        break;
      case Variant.VariantCurrency | Variant.VariantByref: // 6
        result = sourceData.getCurrencyRef();
        break;
      case Variant.VariantDate: // 7
        result = sourceData.getJavaDate();
        break;
      case Variant.VariantDate | Variant.VariantByref: // 7
        result = sourceData.getJavaDateRef();
        break;
      case Variant.VariantString: // 8
        result = sourceData.getString();
        break;
      case Variant.VariantString | Variant.VariantByref: // 8
        result = sourceData.getStringRef();
        break;
      case Variant.VariantDispatch: // 9
        result = sourceData.getDispatch();
        break;
      case Variant.VariantDispatch | Variant.VariantByref: // 9
        result = sourceData.getDispatchRef(); // Can dispatches even
        // be byRef?
        break;
      case Variant.VariantError: // 10
        result = new NotImplementedException(
            "toJavaObject() Not implemented for VariantError");
        break;
      case Variant.VariantBoolean: // 11
        result = new Boolean(sourceData.getBoolean());
        break;
      case Variant.VariantBoolean | Variant.VariantByref: // 11
        result = new Boolean(sourceData.getBooleanRef());
        break;
      case Variant.VariantVariant: // 12 they are always by ref
        result = new NotImplementedException(
            "toJavaObject() Not implemented for VariantVariant without ByRef");
        break;
      case Variant.VariantVariant | Variant.VariantByref: // 12
        result = sourceData.getVariant();
        break;
      case Variant.VariantObject: // 13
        result = new NotImplementedException(
            "toJavaObject() Not implemented for VariantObject");
        break;
      case Variant.VariantDecimal: // 14
        result = sourceData.getDecimal();
        break;
      case Variant.VariantDecimal | Variant.VariantByref: // 14
        result = sourceData.getDecimalRef();
        break;
      case Variant.VariantByte: // 17
        result = new Byte(sourceData.getByte());
        break;
      case Variant.VariantByte | Variant.VariantByref: // 17
        result = new Byte(sourceData.getByteRef());
        break;
      case Variant.VariantLongInt: // 20
        result = new Long(sourceData.getLong());
        break;
      case Variant.VariantLongInt | Variant.VariantByref: // 20
        result = new Long(sourceData.getLongRef());
        break;
      case Variant.VariantTypeMask: // 4095
        result = new NotImplementedException(
            "toJavaObject() Not implemented for VariantTypeMask");
        break;
      case Variant.VariantArray: // 8192
        result = new NotImplementedException(
            "toJavaObject() Not implemented for VariantArray");
        break;
      case Variant.VariantByref: // 16384
        result = new NotImplementedException(
            "toJavaObject() Not implemented for VariantByref");
        break;
      default:
        result = new NotImplementedException("Unknown return type: "
            + type);
        // there was a "return result" here that caused defect 1602118
        // so it was removed
        break;
      }// switch (type)

      if (result instanceof JacobException) {
        throw (JacobException) result;
      }
    }

    return result;
  }// toJava()

  /**
   * Verifies that we have a scale 0 <= x <= 28 and now more than 96 bits of
   * data. The roundToMSDecimal method will attempt to adjust a BigDecimal to
   * pass this set of tests
   *
   * @param in
   * @throws IllegalArgumentException
   *             if out of bounds
   */
  protected static void validateDecimalScaleAndBits(BigDecimal in) {
    BigInteger allWordBigInt = in.unscaledValue();
    if (in.scale() > 28) {
      // should this cast to a string and call putStringRef()?
      throw new IllegalArgumentException(
          "VT_DECIMAL only supports a maximum scale of 28 and the passed"
              + " in value has a scale of " + in.scale());
    } else if (in.scale() < 0) {
      // should this cast to a string and call putStringRef()?
      throw new IllegalArgumentException(
          "VT_DECIMAL only supports a minimum scale of 0 and the passed"
              + " in value has a scale of " + in.scale());
    } else if (allWordBigInt.bitLength() > 12 * 8) {
      throw new IllegalArgumentException(
          "VT_DECIMAL supports a maximum of "
              + 12
              * 8
              + " bits not counting scale and the number passed in has "
              + allWordBigInt.bitLength());

    } else {
      // no bounds problem to be handled
    }

  }

  /**
   * Largest possible number with scale set to 0
   */
  private static final BigDecimal LARGEST_DECIMAL = new BigDecimal(
      new BigInteger("ffffffffffffffffffffffff", 16));
  /**
   * Smallest possible number with scale set to 0. MS doesn't support negative
   * scales like BigDecimal.
   */
  private static final BigDecimal SMALLEST_DECIMAL = new BigDecimal(
      new BigInteger("ffffffffffffffffffffffff", 16).negate());

  /**
   * Does any validation that couldn't have been fixed by rounding or scale
   * modification.
   *
   * @param in
   *            The BigDecimal to be validated
   * @throws IllegalArgumentException
   *             if the number is too large or too small or null
   */
  protected static void validateDecimalMinMax(BigDecimal in) {
    if (in == null) {
      throw new IllegalArgumentException(
          "null is not a supported Decimal value.");
    } else if (LARGEST_DECIMAL.compareTo(in) < 0) {
      throw new IllegalArgumentException(
          "Value too large for VT_DECIMAL data type:" + in.toString()
              + " integer: " + in.toBigInteger().toString(16)
              + " scale: " + in.scale());
    } else if (SMALLEST_DECIMAL.compareTo(in) > 0) {
      throw new IllegalArgumentException(
          "Value too small for VT_DECIMAL data type:" + in.toString()
              + " integer: " + in.toBigInteger().toString(16)
              + " scale: " + in.scale());
    }

  }

  /**
   * Rounds the scale and bit length so that it will pass
   * validateDecimalScaleBits(). Developers should call this method if they
   * really want MS Decimal and don't want to lose precision.
   * <p>
   * Changing the scale on a number that can fit in an MS Decimal can change
   * the number's representation enough that it will round to a number too
   * large to be represented by an MS VT_DECIMAL
   *
   * @param sourceDecimal
   * @return BigDecimal a new big decimal that was rounded to fit in an MS
   *         VT_DECIMAL
   */
  public static BigDecimal roundToMSDecimal(BigDecimal sourceDecimal) {
    BigInteger sourceDecimalIntComponent = sourceDecimal.unscaledValue();
    BigDecimal destinationDecimal = new BigDecimal(
        sourceDecimalIntComponent, sourceDecimal.scale());
    int roundingModel = BigDecimal.ROUND_HALF_UP;
    validateDecimalMinMax(destinationDecimal);
    // First limit the number of digits and then the precision.
    // Try and round to 29 digits because we can sometimes do that
    BigInteger allWordBigInt;
    allWordBigInt = destinationDecimal.unscaledValue();
    if (allWordBigInt.bitLength() > 96) {
      destinationDecimal = destinationDecimal.round(new MathContext(29));
      // see if 29 digits uses more than 96 bits
      if (allWordBigInt.bitLength() > 96) {
        // Dang. It was over 97 bits so shorten it one more digit to
        // stay <= 96 bits
        destinationDecimal = destinationDecimal.round(new MathContext(
            28));
      }
    }
    // the bit manipulations above may change the scale so do it afterwards
    // round the scale to the max MS can support
    if (destinationDecimal.scale() > 28) {
      destinationDecimal = destinationDecimal.setScale(28, roundingModel);
    }
    if (destinationDecimal.scale() < 0) {
      destinationDecimal = destinationDecimal.setScale(0, roundingModel);
    }
    return destinationDecimal;
  }
}
TOP

Related Classes of com.jacob.com.VariantUtilities

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.