All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jinterop.dcom.core.VariantBody Maven / Gradle / Ivy

/**
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 *
 * Though a sincere effort has been made to deliver a professional,
 * quality product,the library itself is distributed WITHOUT ANY WARRANTY;
 * See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
 */
package org.jinterop.dcom.core;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import ndr.NetworkDataRepresentation;
import org.jinterop.dcom.common.JIErrorCodes;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.common.JIRuntimeException;
import org.jinterop.dcom.impls.automation.IJIDispatch;

class VariantBody implements Serializable {

    private static final Logger LOGGER = Logger.getLogger("org.jinterop");
    private static final long serialVersionUID = -8484108480626831102L;
    public static final short VT_PTR = 0x1A;
    public static final short VT_SAFEARRAY = 0x1B;
    public static final short VT_CARRAY = 0x1C;
    public static final short VT_USERDEFINED = 0x1D;

    static final class EMPTY {
    }

    static final class NULL {
    }

    static final class SCODE {

        private int errorCode;

        private SCODE() {
        }

        private SCODE(int errorCode) {
            this.errorCode = errorCode;
        }
    }

    /**
     * EMPTY VARIANT
     */
    public static final EMPTY EMPTY = new EMPTY();

    /**
     * NULL VARIANT
     */
    public static final NULL NULL = new NULL();

    /**
     * SCODE VARIANT
     */
    public static final SCODE SCODE = new SCODE();

    private boolean is2Dimensional = false;
    private Object obj = null;
    private int type = -1;
    private JIStruct safeArrayStruct = null;
    private boolean isArray = false;
    private boolean isScode = false;
    private boolean isNull = false;
    private Class nestedArraysRealClass = null;
    private static final List> type3 = new ArrayList<>();
    private boolean isByRef = false;

    int FLAG = JIFlags.FLAG_NULL;
//  int variantType = 0x1d; //base jump

    static {
        type3.add(Integer.class);
        type3.add(Short.class);
        type3.add(Float.class);
        type3.add(Boolean.class);
        type3.add(Character.class);
        type3.add(Byte.class);
        type3.add(EMPTY.class);
        type3.add(NULL.class);
        type3.add(SCODE.class);
        type3.add(JIUnsignedByte.class);
        type3.add(JIUnsignedShort.class);
        type3.add(JIUnsignedInteger.class);
    }

    boolean isByRef() {
        return isByRef;
    }

    boolean isNull() {
        return isNull;
    }

    int getType() {
        return isArray ? JIVariant.VT_ARRAY | type : type;
    }
    //The class of the object determines its type.

    /**
     * Setting up a VARIANT with an object. Used via serializing
     * the VARIANT.
     *
     * @param referent
     */
    VariantBody(Object referent, boolean isByRef) {
        this(referent, isByRef, -1);
    }

    private VariantBody(Object referent, boolean isByRef, int dataType) {
        this.obj = referent == null ? VariantBody.EMPTY : referent;

        if (obj instanceof JIString && ((JIString) obj).getType() != JIFlags.FLAG_REPRESENTATION_STRING_BSTR) {
            throw new JIRuntimeException(JIErrorCodes.JI_VARIANT_BSTR_ONLY);
        }

        if (obj instanceof Boolean) {
            FLAG = JIFlags.FLAG_REPRESENTATION_VARIANT_BOOL;
        }

        this.isByRef = isByRef;
        //for an unsupported type this could be null
        //but then this is my bug, any thread entering this ctor , will support a type.
        Integer types = JIVariant.getSupportedType(obj, dataType);
        if (types != null) {
            type = types | (isByRef ? JIVariant.VT_BYREF : 0);
        } else {
            throw new JIRuntimeException(JIErrorCodes.JI_VARIANT_UNSUPPORTED_TYPE);
        }
        if (dataType == JIVariant.VT_NULL) {
            isNull = true;
            obj = 0;
        }
    }

    /**
     * Setting up a VARIANT with a NULL value. Used via serializing
     * the VARIANT.
     *
     * @param value
     */
    VariantBody(NULL value) {
        this(new Integer(0), false);
        isNull = true;
        type = JIVariant.VT_NULL;
    }

    /**
     * Setting up a VARIANT with a SCODE value and it's errorCode.
     * Used via serializing the VARIANT.
     *
     * @param value
     * @param errorCode
     */
    VariantBody(SCODE value, int errorCode, boolean isByRef) {
        this(new Integer(errorCode), isByRef);
        isScode = true;
        type = JIVariant.VT_ERROR;
    }

    VariantBody(JIStruct safeArray, Class nestedClass, boolean is2Dimensional, boolean isByRef, int FLAG) {
        this.FLAG = FLAG;
        //can't convert the array here , since this will have deffered pointers which may not be complete.
        safeArrayStruct = safeArray;
        isArray = true;
        if (safeArrayStruct == null) {
            isNull = true;
        }

        this.nestedArraysRealClass = nestedClass;
        this.is2Dimensional = is2Dimensional;
        //please remember JIVariant is a pointer and VariantBody is just the referent part of that.

        //for an unsupported type this could be null
        //but then this is my bug, any thread entering this ctor , will support a type.
        this.isByRef = isByRef;
        Integer types = JIVariant.getSupportedType(nestedClass, FLAG);
        if (types != null) {
            type = types | (isByRef ? JIVariant.VT_BYREF : 0);
        } else {
            throw new JIRuntimeException(JIErrorCodes.JI_VARIANT_UNSUPPORTED_TYPE);
        }
    }

    /**
     * Returns the contained object.
     *
     * @return
     */
    Object getObject() throws JIException {
        return obj == null ? getArray() : obj;
    }

    JIArray getArray() throws JIException {
        JIArray retVal = null;
        //TODO convert it to the right type based on the variantType before returning it.
        //everything is sent encapsulated in a variant(in safearray) , so an Integer[] will
        //go as a variant array for each integer, only the variantType = arry of ints. so convert the
        //array in the right format before returning it to the user. That is he must get Int[] within a JIArray
        //back.
        if (safeArrayStruct != null) {
            retVal = (JIArray) ((JIPointer) safeArrayStruct.getMember(7)).getReferent();

            if (is2Dimensional) {
                Object[] obj3 = (Object[]) retVal.getArrayInstance(); //these will all be variants
                //correct the array here , i.e reform the 2 dimensional array before returning back.
                JIArray safeArrayBound = (JIArray) safeArrayStruct.getMember(8);

                JIStruct[] safeArrayBound2 = (JIStruct[]) safeArrayBound.getArrayInstance();
                //should only be 2 since we support only 2 dim.

                int firstDim = ((Number) safeArrayBound2[0].getMember(0)).intValue();
                int secondDim = ((Number) safeArrayBound2[1].getMember(0)).intValue();

                Object obj = Array.newInstance(nestedArraysRealClass, new int[]{firstDim, secondDim});
                Object[][] obj2 = (Object[][]) obj;
                int k = 0;
                for (int i = 0; i < secondDim; i++) {
                    for (int j = 0; j < firstDim; j++) {
                        //if (nestedArraysRealClass == JIVariant.class)
                        //{
                        //  obj2[j][i] = ((JIVariant[])obj3)[k++];
                        //}
                        //else
                        //{
                        //  obj2[j][i] = ((JIVariant[])obj3)[k++].getObject();
                        //}
                        obj2[j][i] = obj3[k++];
                    }
                }

                retVal = new JIArray(obj2);

            } else {

                if (nestedArraysRealClass != null) {
                    Object[] obj = (Object[]) retVal.getArrayInstance(); //these will all be variants
                    Object obj2 = Array.newInstance(nestedArraysRealClass, obj.length);
                    System.arraycopy(obj, 0, (Object[]) obj2, 0, obj.length);//            if (nestedArraysRealClass == JIVariant.class)
                    // {
                    //   Array.set(obj2,i,((JIVariant[])obj)[i]);//should be the native type
                    // }
                    // else
                    // {
                    //   Array.set(obj2,i,((JIVariant[])obj)[i].getObject());//should be the native type
                    // }
                    //Array.set(obj2,i,obj[i]);
                    retVal = new JIArray(obj2);
                } else {
                    throw new JIException(JIErrorCodes.JI_VARIANT_UNSUPPORTED_TYPE);
                }
            }
        }
        return retVal;
    }

    /**
     * Retrieves the contained object as int.
     *
     * @return
     */
    int getObjectAsInt() {
        try {
            return ((Number) obj).intValue();
        } catch (ClassCastException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }

    long getObjectAsLong() {
        try {
            return ((Number) obj).longValue();
        } catch (ClassCastException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }

    IJIUnsigned getObjectAsUnsigned() {
        try {
            return ((IJIUnsigned) obj);
        } catch (ClassCastException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }

    int getObjectAsSCODE() {
        try {
            return ((SCODE) obj).errorCode;
        } catch (ClassCastException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }

    /**
     * Retrieves the contained object as float.
     *
     * @return
     */
    float getObjectAsFloat() {
        try {
            return ((Number) obj).floatValue();
        } catch (ClassCastException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }

    /**
     * Retrieves the contained object as double.
     *
     * @return
     */
    double getObjectAsDouble() {
        try {
            return ((Number) obj).doubleValue();
        } catch (ClassCastException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }

    /**
     * Retrieves the contained object as short.
     *
     * @return
     */
    short getObjectAsShort() {
        try {
            return ((Number) obj).shortValue();
        } catch (ClassCastException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }

    /**
     * Retrieves the contained object as boolean.
     *
     * @return
     */
    boolean getObjectAsBoolean() {
        try {
            return ((Boolean) obj);
        } catch (ClassCastException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }

    /**
     * Retrieves the contained object as JIString.
     *
     * @return
     */
    JIString getObjectAsString() {
        try {
            return ((JIString) obj);
        } catch (ClassCastException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }

    /**
     * Retrieves the contained object as Date.
     *
     * @return
     */
    Date getObjectAsDate() {
        try {
            return ((Date) obj);
        } catch (ClassCastException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }

    /**
     * Retrieves the contained object as char.
     *
     * @return
     */
    char getObjectAsChar() {
        try {
            return ((Character) obj);
        } catch (ClassCastException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }

    /**
     * Retrieves the contained object as Variant.
     *
     * @return
     */
    JIVariant getObjectAsVariant() {
        try {
            return ((JIVariant) obj);
        } catch (ClassCastException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }

    IJIComObject getObjectAsComObject() {
        try {
            return ((IJIComObject) obj);
        } catch (ClassCastException e) {
            throw new IllegalStateException(e.getMessage());
        }
    }

    void encode(NetworkDataRepresentation ndr, List defferedPointers, int FLAG) {

//    try
        {
            FLAG |= this.FLAG;
            //align with 8 boundary
            double index = new Integer(ndr.getBuffer().getIndex()).doubleValue();
            if (index % 8.0 != 0) {
                long i = (i = Math.round(index % 8.0)) == 0 ? 0 : 8 - i;
                ndr.writeOctetArray(new byte[(int) i], 0, (int) i);
            }

            int start = ndr.getBuffer().getIndex();

            // if (safeArrayStruct != null)
            // {
            //   //length for the array
            //   length = fillArrayType(ndr);
            // }
            // else
            // {
            //   ndr.writeUnsignedLong(variantType);
            // }
            //just a place holder for length
            ndr.writeUnsignedLong(0xFFFFFFFF);

            ndr.writeUnsignedLong(0);

            //Type
            int varType = getVarType(obj != null ? obj.getClass() : nestedArraysRealClass, obj);

            //For IUnknown , since the inner object is a JIComObjectImpl it will be fine.
            if ((FLAG & JIFlags.FLAG_REPRESENTATION_IDISPATCH_NULL_FOR_OUT) == JIFlags.FLAG_REPRESENTATION_IDISPATCH_NULL_FOR_OUT) {
                varType = isByRef ? 0x4000 | JIVariant.VT_DISPATCH : JIVariant.VT_DISPATCH;
            }
            ndr.writeUnsignedShort(varType);

            //reserved bytes
            ndr.writeUnsignedSmall(0xCC);
            ndr.writeUnsignedSmall(0xCC);
            ndr.writeUnsignedSmall(0xCC);
            ndr.writeUnsignedSmall(0xCC);
            ndr.writeUnsignedSmall(0xCC);
            ndr.writeUnsignedSmall(0xCC);

            if (obj != null) {
                ndr.writeUnsignedLong(varType);
            } else {
                if (!isByRef) {
                    ndr.writeUnsignedLong(JIVariant.VT_ARRAY);
                } else {
                    ndr.writeUnsignedLong(JIVariant.VT_BYREF_VT_ARRAY);
                }
            }

            if (isByRef) {
                int flag = -1;
                if (isArray) //object arrays will come here....
                {
                    flag = 4;
                } else {
                    //no idea what these flags are but 0x10 is for variant, 0x8 for date, and 0x4 is for others
                    switch (type) {
                        case JIVariant.VT_BYREF_VT_VARIANT:
                            flag = 0x10;
                            break;
                        case JIVariant.VT_BYREF_VT_DATE:
                        case JIVariant.VT_BYREF_VT_CY:
                            flag = 8;
                            break;
                        default:
                            flag = 4;
                    }
                }
                ndr.writeUnsignedLong(flag);

            }

            //we should not use the deffered pointers here, but pass our own one, so that only they are written...
            List varDefferedPointers = new ArrayList<>();
            //we should use FLAG here, since the decision should be based on this only.
            setValue(ndr, obj, varDefferedPointers, FLAG);
            //making changes to write the deffered pointers here itself , since we need to put the entire Variant completed to the length
            //as in varType.
            int x = 0;
            while (x < varDefferedPointers.size()) {
                List newList = new ArrayList<>();
                JIMarshalUnMarshalHelper.serialize(ndr, JIPointer.class, varDefferedPointers.get(x), newList, FLAG);
                x++; //incrementing index
                varDefferedPointers.addAll(x, newList);
            }

            int currentIndex = ndr.getBuffer().getIndex();
            int length = currentIndex - start;
            int value = length / 8;
            if (length % 8.0 != 0) { //entire variant is aligned by 8 bytes.
                value++;
            }
            ndr.getBuffer().setIndex(start);
            ndr.writeUnsignedLong(value);
            ndr.getBuffer().setIndex(currentIndex);

            if (LOGGER.isLoggable(Level.FINEST)) {
                LOGGER.log(Level.FINEST, "Variant length is {0} , value {1} , variant type{2}", new Object[]{length, value, type});
            }
            // if (safeArrayStruct != null && isArray)
            // {
            //   //SafeArray have the alignment rule , that all Size <=4 are aligned by 4 and size 8 is aligned by 8.
            //   //Variant is aligned by 4, Interface pointers are aligned by 4 as well.
            //   //but this should not exceed the length
            //   index = new Integer(ndr.getBuffer().getIndex()).doubleValue();
            //   length = length * 8 + start;
            //   if (index < length)
            //   {
            //     Integer size = (Integer)safeArrayStruct.getMember(2);
            //     long i = 0;
            //     if (size.intValue() == 8)
            //     {
            //       if (index%8.0 != 0)
            //       {
            //         i = (i=Math.round(index%8.0)) == 0 ? 0 : 8 - i ;
            //         if (index + i <= length)
            //         {
            //           ndr.writeOctetArray(new byte[(int)i],0,(int)i);
            //         }
            //         else
            //         {
            //           ndr.writeOctetArray(new byte[(length - (int)index)],0,(int)(length - (int)index));
            //         }
            //       }
            //     }
            //     else
            //     {
            //       //align by 4...
            //       //TODO this needs to be tested for Structs and Unions.
            //       if (index%4.0 != 0)
            //       {
            //         i = (i=Math.round(index%4.0)) == 0 ? 0 : 4 - i ;
            //         if (index + i <= length)
            //         {
            //           ndr.writeOctetArray(new byte[(int)i],0,(int)i);
            //         }
            //         else
            //         {
            //           ndr.writeOctetArray(new byte[(length - (int)index)],0,(int)(length - (int)index));
            //         }
            //       }
            //     }
            //
            //
            //   }
            // }
        }
        //catch (JIException e)
        //{
        //  throw new JIRuntimeException(e.getErrorCode());
        //}
    }

    //multiple of 8.
    //  private int getMaxLength(Class c, boolean isByRef, Object obj)
    //  {
    //    int length = 3; //Empty
    //    if (type3.contains(c))
    //    {
    //      length = 3;
    //      if (isByRef)
    //      {
    //        length = length + 1; //for the pointer
    //      }
    //    }
    //    else
    //    if(c.equals(Long.class) || c.equals(Double.class) || c.equals(Date.class) || c.equals(JICurrency.class))
    //    {
    //      length = 4;
    //      //here the byref can be left out since it will cover 24 bytes properly
    //    }
    //    else
    //    if(c.equals(JIString.class))
    //    {
    //
    //      int strlen = 0;
    //      if (obj != null && ((JIString)obj).getString() != null)
    //      {
    //        strlen = ((JIString)obj).getString().length();
    //      }
    //
    //      //20 is of variant, 4+4+4+4 of bstr(user,maxlen,actlen,offset) , (strlen*2) of the actual array
    //      double value = 20 + 16 + strlen*2;
    //      if (isByRef)
    //      {
    //        value = value + 4;
    //      }
    //      double d = value%8.0;
    //      length = (int)value/8;
    //      if (d != 0.0)
    //      {
    //        length++;
    //      }
    //
    //
    //    }else // for Interface pointers without
    //    if((obj instanceof IJIComObject))
    //    {
    //      double value = ((IJIComObject)obj).internal_getInterfacePointer().getLength();
    //      if (isByRef)
    //      {
    //        value = value + 4;
    //      }
    //
    //      value = value + 20 + 4 + 4 + 4; //20 of variant , 4 of the ptr, 4 of max count, 4 of actual count
    //
    //      double d = value%8.0;
    //      length = (int)value/8;
    //      if (d != 0.0)
    //      {
    //        length++;
    //      }
    //      //length += 4;
    //      //double a = ((IJIComObject)obj).getInterfacePointer().getLength()/8.0;
    //      //length = 4 + (int)Math.ceil(a);
    //    }
    //
    //
    //    return length;
    //
    //  }
    //returns the length in bytes
    private int getMaxLength2(Class c, Object obj) {
        int length = 0;

        //since this is getMaxLength2 and hence will either contain
        //proper type 3 elements and not EMPTY,NULL,SCODE since these are parts of Variant.
        //and not simple types like Integer, JIUnsignedXXX or Float etc.
        if (type3.contains(c)) {
            length = JIMarshalUnMarshalHelper.getLengthInBytes(c, obj, FLAG);
        } else if (c.equals(Long.class) || c.equals(Double.class) || c.equals(Date.class) || c.equals(JICurrency.class)) {
            length = 8;
        } else if (c.equals(JIString.class)) {
            length = JIMarshalUnMarshalHelper.getLengthInBytes(c, obj, FLAG);
        } else // for Interface pointers without
        if (obj instanceof IJIComObject) {
            double value = ((IJIComObject) obj).internal_getInterfacePointer().getLength();
            value = value + 4 + 4 + 4; //20 of variant , 4 of the ptr, 4 of max count, 4 of actual count
        }

        return length;

    }

    //  int getVariantType() throws JIException
    //  {
    //    return safeArrayStruct == null ? variantType : getArrayLengthForVarType();
    //  }
    //  private int fillArrayType(NetworkDataRepresentation ndr) throws JIException
    //  {
    //    int length = getArrayLengthForVarType();
    //    ndr.writeUnsignedLong(length);
    //    return length;
    //  }
    private int getArrayLengthForVarType() throws JIException {
        //now the array will be of variants, nestedArraysRealClass identifies the class itself
        //for iteration we need the variants and then there members.

        JIArray objArray = (JIArray) ((JIPointer) safeArrayStruct.getMember(7)).getReferent();
        Object[] array = (Object[]) objArray.getArrayInstance();

        double length = 20;//variant
        if (isByRef) {
            length += 4;//byref
        }

        //SafeArray is 44
        length += 44;

        boolean isVariantArray = (((Short) safeArrayStruct.getMember(1)) & JIVariant.FADF_VARIANT) == JIVariant.FADF_VARIANT;

        if (array != null) {
            length += 4; //for max count of the array.
            if (isVariantArray) {
                //each variant is 3 (size 20 = 20/8 = 3)
                for (Object array1 : array) {
                    JIVariant variant = (JIVariant) array1;
                    length += variant.getLengthInBytes(FLAG);//* 8;//((VariantBody)(variant.member.getReferent())).variantType * 8;
                }
                //now for the "user" pointer part
                //length = length + array.length * 4;
            } else {
                //normal non variant array has been sent...
                for (Object array1 : array) {
                    length += getMaxLength2(array1.getClass(), array1);
                }
            }
        } else {
            length += 4; //for the null 0000.
        }

        int value = (int) length / 8;
        if (length % 8.0 != 0) {
            value++;
        }

        return value;
    }

    static VariantBody decode(NetworkDataRepresentation ndr, List defferedPointers, int FLAG, Map additionalData) {
        //boolean readLong = false;
        double index = new Integer(ndr.getBuffer().getIndex()).doubleValue();
        if (index % 8.0 != 0) {
            long i = (i = Math.round(index % 8.0)) == 0 ? 0 : 8 - i;
            ndr.readOctetArray(new byte[(int) i], 0, (int) i);
        }

        int start = ndr.getBuffer().getIndex();
        int length = ndr.readUnsignedLong(); //read the potential length
        ndr.readUnsignedLong(); //read the reserved byte

        int variantType = ndr.readUnsignedShort(); //varType

        //read reserved bytes
        ndr.readUnsignedShort();
        ndr.readUnsignedShort();
        ndr.readUnsignedShort();

        ndr.readUnsignedLong(); //32 bit varType

        VariantBody variant;
        List varDefferedPointers = new ArrayList<>();
        if ((variantType & JIVariant.VT_ARRAY) == 0x2000) {
            boolean isByRef = (variantType & JIVariant.VT_BYREF) != 0;
            //the struct may be null if the array has nothing
            JIStruct safeArray = getDecodedValueAsArray(ndr, varDefferedPointers, variantType & ~JIVariant.VT_ARRAY, isByRef, additionalData, FLAG);
            int type2 = variantType;
            if (isByRef) {
                type2 &= ~JIVariant.VT_BYREF; //so that actual type can be determined
            }

            type2 &= 0x0FFF;
            int flagofFlags = FLAG;
            if (type2 == JIVariant.VT_INT) {
                flagofFlags |= JIFlags.FLAG_REPRESENTATION_VT_INT;
            } else if (type2 == JIVariant.VT_UINT) {
                flagofFlags |= JIFlags.FLAG_REPRESENTATION_VT_UINT;
            } else if (type2 == JIVariant.VT_BOOL) {
                FLAG = flagofFlags |= JIFlags.FLAG_REPRESENTATION_VARIANT_BOOL;
            }

            if (safeArray != null) {
                variant = new VariantBody(safeArray, JIVariant.getSupportedClass(type2 & ~JIVariant.VT_ARRAY), (((Object[]) ((JIArray) safeArray.getMember(8)).getArrayInstance()).length > 1), isByRef, flagofFlags);
            } else {
                variant = new VariantBody(null, JIVariant.getSupportedClass(type2 & ~JIVariant.VT_ARRAY), false, isByRef, flagofFlags);
            }

            variant.FLAG = flagofFlags;

        } else {
            boolean isByRef = (variantType & JIVariant.VT_BYREF) != 0;
            variant = new VariantBody(getDecodedValue(ndr, varDefferedPointers, variantType, isByRef, additionalData, FLAG), isByRef, variantType);
            int type2 = variantType & 0x0FFF;
            if (type2 == JIVariant.VT_INT) {
                variant.FLAG = JIFlags.FLAG_REPRESENTATION_VT_INT;
            } else if (type2 == JIVariant.VT_UINT) {
                variant.FLAG = JIFlags.FLAG_REPRESENTATION_VT_UINT;
            }
        }

        int x = 0;
        while (x < varDefferedPointers.size()) {

            List newList = new ArrayList<>();
            JIPointer replacement = (JIPointer) JIMarshalUnMarshalHelper.deSerialize(ndr, varDefferedPointers.get(x), newList, FLAG, additionalData);
            varDefferedPointers.get(x).replaceSelfWithNewPointer(replacement); //this should replace the value in the original place.
            x++;
            varDefferedPointers.addAll(x, newList);
        }

        if (variant.isArray && variant.safeArrayStruct != null) {
            //SafeArray have the alignment rule , that all Size <=4 are aligned by 4 and size 8 is aligned by 8.
            //Variant is aligned by 4, Interface pointers are aligned by 4 as well.
            //but this should not exceed the length
            index = new Integer(ndr.getBuffer().getIndex()).doubleValue();
            length = length * 8 + start;
            if (index < length) {
                JIStruct safeArrayStruct = variant.safeArrayStruct;
                Integer size = (Integer) safeArrayStruct.getMember(2);
                long i = 0;
                if (size == 8) {
                    if (index % 8.0 != 0) {
                        i = (i = Math.round(index % 8.0)) == 0 ? 0 : 8 - i;
                        if (index + i <= length) {
                            ndr.readOctetArray(new byte[(int) i], 0, (int) i);
                        } else {
                            ndr.readOctetArray(new byte[(length - (int) index)], 0, (length - (int) index));
                        }
                    }
                } else {
                    //align by 4...
                    //TODO this needs to be tested for Structs and Unions.
                    if (index % 4.0 != 0) {
                        i = (i = Math.round(index % 4.0)) == 0 ? 0 : 4 - i;
                        if (index + i <= length) {
                            ndr.readOctetArray(new byte[(int) i], 0, (int) i);
                        } else {
                            ndr.readOctetArray(new byte[(length - (int) index)], 0, (length - (int) index));
                        }
                    }
                }

            }

            //SafeArray is complete
            JIArray array = null;
            try {
                array = variant.getArray();
            } catch (JIException e) {
                throw new JIRuntimeException(e.getErrorCode());
            }
            JIVariant variantMain = new JIVariant(array, variant.isByRef, variant.FLAG);
            variant = (VariantBody) variantMain.member.getReferent();
        }

        return variant;
    }

    //Variants need specialised handling and the standard serializers may or maynot be used.
    private static Class getVarClass(int type) {
        Class c = null;
        //now first to check if this is a pointer or not.
        type &= 0x0FFF; //0x4XXX & 0x0FFF = real type
        switch (type) {
            case 0:  //VT_EMPTY , Not specified.
                c = VariantBody.EMPTY.class;
                break;
            case 1:  // VT_NULL , Null.
                c = VariantBody.NULL.class;
                break;
            case 10:
                c = VariantBody.SCODE.class; //VT_ERROR,Scodes.
                break;
            default:
                c = JIVariant.getSupportedClass(type);
                if (c == null) {
                    //TODO log this , what has come that i don't support.
                }
                break;
        }

        return c;
    }

    private int getVarType(Class c, Object obj) {
        int type = 0; //EMPTY

        if (obj instanceof IJIDispatch) {
            return isByRef ? 0x4000 | JIVariant.VT_DISPATCH : JIVariant.VT_DISPATCH;
        }

        if (obj instanceof IJIComObject) {
            return isByRef ? 0x4000 | JIVariant.VT_UNKNOWN : JIVariant.VT_UNKNOWN;
        }

        if (c != null) {
            Integer type2 = JIVariant.getSupportedType(c, FLAG);

            if (type2 != null) {
                type = type2;
            } else {
                LOGGER.log(Level.WARNING, "In getVarType: Unsupported Type found ! {0} , please add this to the supportedType map ! ", c);
                //make that an array of variants
                type2 = JIVariant.getSupportedType(JIVariant.class, FLAG);
            }

            if (isNull) {
                type = 1;
            } else if (isScode) {
                type = 10; //scode
            } else if (isArray) {
                type = (int) 0x2000 | type; //0xC; should not assume an array of variants anymore
            }
        }

        if (isByRef && type != 0 && !c.equals(JIArray.class)) {
            //then it is a pointer. have to set it correctly
            type |= 0x4000;
        }
        return type;
    }

    private static Object getDecodedValue(NetworkDataRepresentation ndr, List defferedPointers, int type, boolean isByRef, Map additionalData, int FLAG) {

        Object obj = null;
        Class c = getVarClass(type);
        if (c != null) {
            if (isByRef) {
                ndr.readUnsignedLong(); //Read the Pointer
            }

            if (c.equals(VariantBody.SCODE.class)) {
                obj = JIMarshalUnMarshalHelper.deSerialize(ndr, Integer.class, null, FLAG, additionalData);
                obj = new SCODE(((Number) obj).intValue());
                type = JIVariant.VT_ERROR;
            } else if (c.equals(VariantBody.NULL.class)) {
                //have read 20 bytes
                //JIMarshalUnMarshalHelper.deSerialize(ndr,Integer.class,null,JIFlags.FLAG_NULL);//read the last 4 bytes, since there could be parameters before this.
                obj = NULL;
                type = JIVariant.VT_NULL;
            } else if (c.equals(VariantBody.EMPTY.class)) //empty is 20 bytes
            {
                obj = VariantBody.EMPTY;
                type = JIVariant.VT_EMPTY;
            } else if (c.equals(JIString.class)) {
                obj = new JIString(JIFlags.FLAG_REPRESENTATION_STRING_BSTR);
                obj = ((JIString) obj).decode(ndr, null, FLAG, additionalData);
            } else if (c.equals(Boolean.class)) {
                obj = JIMarshalUnMarshalHelper.deSerialize(ndr, c, defferedPointers, FLAG | JIFlags.FLAG_REPRESENTATION_VARIANT_BOOL, additionalData);
            } else {
                obj = JIMarshalUnMarshalHelper.deSerialize(ndr, c, defferedPointers, FLAG, additionalData);
            }
        }

        return obj;
    }

    private static JIStruct getDecodedValueAsArray(NetworkDataRepresentation ndr, List defferedPointers, int type, boolean isByRef, Map additionalData, int FLAG) {
        //int newFLAG = FLAG;
        if (isByRef) {
            ndr.readUnsignedLong();//read the pointer
            type &= ~JIVariant.VT_BYREF; //so that actual type can be determined
        }

        if (ndr.readUnsignedLong() == 0)//read pointer referent id
        {
            return null;
        }

        ndr.readUnsignedLong();//1

        JIStruct safeArray = new JIStruct();
        try {
            safeArray.addMember(Short.class);//dim

            JIStruct safeArrayBound = new JIStruct();
            safeArrayBound.addMember(Integer.class);
            safeArrayBound.addMember(Integer.class); //starts at 0

            safeArray.addMember(Short.class);//flags
            safeArray.addMember(Integer.class);//size
            safeArray.addMember(Short.class);//locks
            safeArray.addMember(Short.class);//locks
            safeArray.addMember(Integer.class);//safearrayunion
            safeArray.addMember(Integer.class);//size in safearrayunion

            Class c = JIVariant.getSupportedClass(type);
            if (c == null) {
                if (LOGGER.isLoggable(Level.WARNING)) {
                    LOGGER.log(Level.WARNING, "From JIVariant: while decoding an Array, type {0} , was not found in supportedTypes_classes map , hence using JIVariant instead...", type);
                }
                //not available , lets try with JIVariant.
                //This is a bug, I should have the type.
                c = JIVariant.class;
            }

            if (c == Boolean.class) {
                FLAG |= JIFlags.FLAG_REPRESENTATION_VARIANT_BOOL;
            }
            //HARDCODING to JIVariant...kindof forgotten why I even wrote the code below.
            //since all of the examples I have come across always return a Variant array.
            //then why did I typify this thing to it's class (like JIString), it produces an
            //exception when the result is returned back is not an array of strings...
            //c = JIVariant.class;
            JIArray values = null;
            if (c == JIString.class) {
                values = new JIArray(new JIString(JIFlags.FLAG_REPRESENTATION_STRING_BSTR), null, 1, true);
                safeArray.addMember(new JIPointer(values));//single dimension array, will convert it into the
                //[] or [][] after inspecting dimension read.
            } else {
                values = new JIArray(c, null, 1, true);
                safeArray.addMember(new JIPointer(values));//single dimension array, will convert it into the
                //[] or [][] after inspecting dimension read.
            }

            safeArray.addMember(new JIArray(safeArrayBound, null, 1, true));

            safeArray = (JIStruct) JIMarshalUnMarshalHelper.deSerialize(ndr, safeArray, defferedPointers, FLAG, additionalData);

            //now set the right class after examining the flags , only set for JIVariant.class now., the BSTR would already be set previously.
            Short features = (Short) safeArray.getMember(1);
            //this condition is being kept in the front since the feature flags can be a combination of FADF_VARIANT and the
            //other flags , in which case the Variant takes priority (since they will all be wrapped as variants).
            if ((features & JIVariant.FADF_VARIANT) == JIVariant.FADF_VARIANT) {
                values.updateClazz(JIVariant.class);
            } else if (((features & JIVariant.FADF_DISPATCH) == JIVariant.FADF_DISPATCH)
                    || ((features & JIVariant.FADF_UNKNOWN) == JIVariant.FADF_UNKNOWN)) {
                values.updateClazz(IJIComObject.class);
            }
            //For JIStrings , it will be done before these above conditions are examined.

        } catch (JIException e) {
            throw new JIRuntimeException(e.getErrorCode());
        }

        return safeArray;
    }

    private void setValue(NetworkDataRepresentation ndr, Object obj, List defferedPointers, int FLAG) {
        if (isNull) {
            return; //null , is only 20 bytes
        }
        if (obj != null) {
            Class c = obj.getClass();

            if (c.equals(EMPTY.class)) //20 bytes
            {
            } // else
            // if (c.equals(Boolean.class))
            // {
            //   ndr.writeUnsignedShort(((Boolean)obj).booleanValue() == true ? 0xFFFF: 0x0000);
            //   ndr.writeUnsignedShort(0);
            // }
            else {
                if (obj instanceof IJIComObject) {
                    c = IJIComObject.class;
                }
                JIMarshalUnMarshalHelper.serialize(ndr, c, obj, defferedPointers, FLAG);
            }
        } else {

            ndr.writeUnsignedLong(new Object().hashCode());//pointer referentId
            ndr.writeUnsignedLong(1);

            JIMarshalUnMarshalHelper.serialize(ndr, JIStruct.class, safeArrayStruct, defferedPointers, FLAG);

        }
    }

    boolean isArray() {
        return isArray;
    }

    int getLengthInBytes() {
        if (safeArrayStruct == null && obj.getClass().equals(VariantBody.EMPTY.class)) {
            return 28;
        }

        if (isArray) {
            int length = 0;
            try {
                length = getArrayLengthForVarType() * 8;
            } catch (JIException e) {
                throw new RuntimeException(e);
            }
            return length;
        } else {
            Class c = obj.getClass();

            if (obj instanceof IJIComObject) {
                c = IJIComObject.class;
            } else if (c.equals(VariantBody.SCODE.class)) {
                return 24 + 4; //4 for integer scode.
            } else if (c.equals(VariantBody.NULL.class) || c.equals(VariantBody.EMPTY.class)) {
                return 24;
            }

            return 24 + JIMarshalUnMarshalHelper.getLengthInBytes(c, obj, FLAG);
        }
    }

    @Override
    public String toString() {
        String retVal = "";
        if (obj == null) {
            retVal += "obj is null , ";
        } else {
            retVal += obj.toString();
        }
        if (isArray) {
            if (is2Dimensional) {
                retVal += "2 dimensional array , ";
            } else {
                retVal = "1 dimensional array , ";
            }

            if (safeArrayStruct != null) {
                retVal += safeArrayStruct.toString();
            }
        }
        return retVal;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy