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

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

There is a newer version: 3.5.1
Show newest version
/** j-Interop (Pure Java implementation of DCOM protocol)
 * Copyright (C) 2006  Vikram Roopchand
 *
 * 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.List;
import java.util.Map;
import java.util.Stack;
import ndr.NetworkDataRepresentation;
import org.jinterop.dcom.common.JIErrorCodes;
import org.jinterop.dcom.common.JISystem;

/**
 * < p>
 * Represents a C++ array which can display both conformant and standard
 * behaviors. Since this class forms a wrapper on the actual array, the
 * developer is expected to provide complete and final arrays (of Objects) to
 * this class. Modifying the wrapped array afterwards will have
 * unexpected results.
 * 

*

* Please refer to MSExcel examples for more details on how to use * this class. *

* Note: Wrapped Arrays can be at most two dimensional in nature. Above * that is not supported by the library. * * @since 1.0 */ public final class JIArray implements Serializable { private static final long serialVersionUID = -8267477025978489665L; private Object memberArray = null; private Class clazz = null; private int[] upperBounds = null; private int dimension = -1; private int numElementsInAllDimensions = 0; private boolean isConformant = false; private boolean isVarying = false; private boolean isConformantProxy = false; private boolean isVaryingProxy = false; private List conformantMaxCounts = new ArrayList(); //list of integers private Object template = null; private int sizeOfNestedArrayInBytes = 0; //used in both encoding and decoding. private JIArray() { } /** * < p> * Creates an array object of the type specified by clazz. This * is used to prepare a template for decoding an array of that type. Used * only for setting as an [out] parameter in a JICallBuilder. *

* For example:-
* This call creates a template for a single dimension Integer array of size * 10. * *
* JIArray array = new JIArray(Integer.class,new int[]{10},1,false); *
*
* *

* * @param clazz class whose instances will be members of the deserialized * array. * @param upperBounds highest index for each dimension. * @param dimension number of dimensions * @param isConformant declares whether the array is conformant or * not. * @throws IllegalArgumentException if upperBounds is supplied * and its length is not equal to the dimension parameter. */ public JIArray(Class clazz, int[] upperBounds, int dimension, boolean isConformant) { this.clazz = clazz; init2(upperBounds, dimension, isConformant, false); } /** * < P> * Refer to {@link #JIArray(Class, int[], int, boolean)} * * @param clazz class whose instances will be members of the deserialized * array. * @param upperBounds highest index for each dimension. * @param dimension number of dimensions * @param isConformant declares whether the array is conformant or * not. * @param isVarying declares whether the array is varying or not. * @throws IllegalArgumentException if upperBounds is supplied * and its length is not equal to the dimension parameter. * */ public JIArray(Class clazz, int[] upperBounds, int dimension, boolean isConformant, boolean isVarying) { this.clazz = clazz; init2(upperBounds, dimension, isConformant, isVarying); } /** * < p> * Creates an array object with members of the type template. * This constructor is used to prepare a template for decoding an array and * is exclusively for composites like JIStruct, * JIPointer, JIUnion, JIString where * more information on the structure of the composite is required before * trying to deserialize it. * *

* * Sample Usage:- *
* * JIStruct safeArrayBounds = new JIStruct();
* safeArrayBounds.addMember(Integer.class);
* safeArrayBounds.addMember(Integer.class);

* * //arraydesc
* JIStruct arrayDesc = new JIStruct();
* //typedesc
* JIStruct typeDesc = new JIStruct();

* * arrayDesc.addMember(typeDesc);
* arrayDesc.addMember(Short.class);
* arrayDesc.addMember(new JIArray(safeArrayBounds,new * int[]{1},1,true));
*
*

* * @param template can be only of the type JIStruct, * JIPointer, JIUnion, JIString * @param upperBounds highest index for each dimension. * @param dimension number of dimensions * @param isConformant declares whether the array is conformant or * not. * @throws IllegalArgumentException if upperBounds is supplied * and its length is not equal to the dimension parameter. * @throws IllegalArgumentException if template is null or is * not of the specified types. */ //for structs, pointers , unions. public JIArray(Object template, int[] upperBounds, int dimension, boolean isConformant) { if (template == null) { throw new IllegalArgumentException(JISystem.getLocalizedMessage(JIErrorCodes.JI_ARRAY_TEMPLATE_NULL)); } if (!template.getClass().equals(JIStruct.class) && !template.getClass().equals(JIUnion.class) && !template.getClass().equals(JIPointer.class) && !template.getClass().equals(JIString.class)) { throw new IllegalArgumentException(JISystem.getLocalizedMessage(JIErrorCodes.JI_ARRAY_INCORRECT_TEMPLATE_PARAM)); } this.template = template; this.clazz = template.getClass(); init2(upperBounds, dimension, isConformant, false); } /** * < p> * Refer to {@link #JIArray(Object, int[], int, boolean)} for details. * * * @param template can be only of the type JIStruct, * JIPointer, JIUnion, JIString * @param upperBounds highest index for each dimension. * @param dimension number of dimensions * @param isConformant declares whether the array is conformant or * not. * @param isVarying declares whether the array is varying or not. * @throws IllegalArgumentException if upperBounds is supplied * and its length is not equal to the dimension parameter. * @throws IllegalArgumentException if template is null or is * not of the specified types. */ //for structs, pointers , unions. public JIArray(Object template, int[] upperBounds, int dimension, boolean isConformant, boolean isVarying) { if (template == null) { throw new IllegalArgumentException(JISystem.getLocalizedMessage(JIErrorCodes.JI_ARRAY_TEMPLATE_NULL)); } if (!template.getClass().equals(JIStruct.class) && !template.getClass().equals(JIUnion.class) && !template.getClass().equals(JIPointer.class) && !template.getClass().equals(JIString.class)) { throw new IllegalArgumentException(JISystem.getLocalizedMessage(JIErrorCodes.JI_ARRAY_INCORRECT_TEMPLATE_PARAM)); } this.template = template; this.clazz = template.getClass(); init2(upperBounds, dimension, isConformant, isVarying); } private void init2(int[] upperBounds, int dimension, boolean isConformant, boolean isVarying) { this.upperBounds = upperBounds; this.dimension = dimension; this.isConformant = isConformant; this.isConformantProxy = isConformant; this.isVarying = isVarying; this.isVaryingProxy = isVarying; if (upperBounds != null) { //have to supply the upperbounds for each dimension , no gaps in between if (upperBounds.length != dimension) { throw new IllegalArgumentException(JISystem.getLocalizedMessage(JIErrorCodes.JI_ARRAY_UPPERBNDS_DIM_NOTMATCH)); } } for (int i = 0; upperBounds != null && i < upperBounds.length; i++) { numElementsInAllDimensions += upperBounds[i]; if (isConformant) { conformantMaxCounts.add(new Integer(upperBounds[i])); } } //numElementsInAllDimensions = numElementsInAllDimensions * dimension; } /** * < p> * Creates an object with array parameter as the nested Array. This * constructor is used when the developer wants to send an array to COM * server. *

* Sample Usage :- *
* * JIArray array = new JIArray(new JIString[]{new JIString(name)},true); *
*
* * @param array Array of any type. Primitive arrays are not allowed. * @param isConformant declares whether the array is conformant * or not. * @throws IllegalArgumentException if the array is not an * array or is of primitive type or is an array of * java.lang.Object. */ public JIArray(Object array, boolean isConformant) { this.isConformant = isConformant; this.isConformantProxy = isConformant; init(array); } /** * Refer {@link #JIArray(Object, boolean)} * * @param array Array of any type. Primitive arrays are not allowed. * @param isConformant declares whether the array is conformant * or not. * @param isVarying declares whether the array is varying or * not. * @throws IllegalArgumentException if the array is not an * array or is of primitive type or is an array of * java.lang.Object. */ public JIArray(Object array, boolean isConformant, boolean isVarying) { this.isConformant = isConformant; this.isConformantProxy = isConformant; this.isVarying = isVarying; this.isVaryingProxy = isVarying; init(array); } /** * < p> * Creates an object with array parameter as the nested Array. This * constructor forms a non-conformant array and is used when * the developer wants to send an array to COM server. *

* Sample Usage :- *
* * JIArray array = new JIArray(new JIString[]{new JIString(name)},true); *
*
* * @param array Array of any type. Primitive arrays are not allowed. * @throws IllegalArgumentException if the array is not an * array or is of primitive type or is an array of * java.lang.Object. */ public JIArray(Object array) { init(array); } private void init(Object array) { if (!array.getClass().isArray()) { throw new IllegalArgumentException(JISystem.getLocalizedMessage(JIErrorCodes.JI_ARRAY_PARAM_ONLY)); } if (array.getClass().isPrimitive()) { throw new IllegalArgumentException(JISystem.getLocalizedMessage(JIErrorCodes.JI_ARRAY_PRIMITIVE_NOTACCEPT)); } //bad way...but what the heck... if (array.getClass().toString().indexOf("java.lang.Object") != -1) { throw new IllegalArgumentException(JISystem.getLocalizedMessage(JIErrorCodes.JI_ARRAY_TYPE_INCORRECT)); } this.memberArray = array; ArrayList upperBounds2 = new ArrayList(); String name = array.getClass().getName(); Object subArray = array; numElementsInAllDimensions = 1; while (name.startsWith("[")) { name = name.substring(1); int x = ((Object[]) subArray).length; upperBounds2.add(new Integer(x)); numElementsInAllDimensions *= x; if (isConformant) { conformantMaxCounts.add(new Integer(x)); } clazz = subArray.getClass().getComponentType(); if (x == 0) //In which ever index the length is 0 , the array stops there, example Byte[0],Byte[0][10],Byte[10][0] { break; } subArray = Array.get(subArray, 0); dimension++; } if (dimension == -1) { numElementsInAllDimensions = 0; dimension++; } upperBounds = new int[upperBounds2.size()]; for (int i = 0; i < upperBounds2.size(); i++) { upperBounds[i] = ((Number) upperBounds2.get(i)).intValue(); } dimension++; //since it starts from -1. sizeOfNestedArrayInBytes = computeLengthArray(array); } private int computeLengthArray(Object array) { int length = 0; String name = array.getClass().getName(); Object o[] = (Object[]) array; for (int i = 0; i < o.length; i++) { if (name.charAt(1) != '[') { Object o1[] = (Object[]) array; for (int j = 0; j < o1.length; j++) { length += JIMarshalUnMarshalHelper.getLengthInBytes(o1.getClass().getComponentType(), o1[j], JIFlags.FLAG_NULL); } return length; } length += computeLengthArray(Array.get(array, i)); } return length; } /** * Returns the nested Array. * * @return array Object which can be type casted based on value returned by * {@link #getArrayClass()}. */ public Object getArrayInstance() { return memberArray; } /** * Class of the nested Array. * * @return class */ public Class getArrayClass() { return clazz; } /** * Array of integers depicting highest index for each dimension. * * @return int[] */ public int[] getUpperBounds() { return upperBounds; } /** * Returns the dimensions of the Array. * * @return int */ public int getDimensions() { return dimension; } int getSizeOfAllElementsInBytes() { // int length = numElementsInAllDimensions * JIMarshalUnMarshalHelper.getLengthInBytes(clazz,((Object[])memberArray)[0],JIFlags.FLAG_NULL); //this means that decode has created this array, and we need to compute the size to stay consistent. if (sizeOfNestedArrayInBytes == -1) { sizeOfNestedArrayInBytes = computeLengthArray(memberArray); } return sizeOfNestedArrayInBytes; } void encode(NetworkDataRepresentation ndr, Object array, List defferedPointers, int FLAG) { // ArrayList listofDefferedPointers = new ArrayList(); if (isConformantProxy) { //first write the max counts ...First to last dimension. int i = 0; while (i < conformantMaxCounts.size()) { JIMarshalUnMarshalHelper.serialize(ndr, Integer.class, conformantMaxCounts.get(i), defferedPointers, FLAG); i++; } isConformantProxy = false; //this is since encode is recursive. } if (isVaryingProxy) { //write the offset and the actual count int i = 0; while (i < conformantMaxCounts.size()) { JIMarshalUnMarshalHelper.serialize(ndr, Integer.class, new Integer(0), defferedPointers, FLAG);//offset JIMarshalUnMarshalHelper.serialize(ndr, Integer.class, conformantMaxCounts.get(i), defferedPointers, FLAG);//actual count i++; } isVaryingProxy = false; //this is since encode is recursive. } String name = array.getClass().getName(); Object o[] = (Object[]) array; for (int i = 0; i < o.length; i++) { if (name.charAt(1) != '[') { Object o1[] = (Object[]) array; for (int j = 0; j < o1.length; j++) { JIMarshalUnMarshalHelper.serialize(ndr, clazz, o1[j], defferedPointers, FLAG | JIFlags.FLAG_REPRESENTATION_ARRAY); } return; } encode(ndr, Array.get(array, i), defferedPointers, FLAG); } } /** * Status whether the array is conformant or not. * * @return true is array is conformant. */ public boolean isConformant() { return isConformant; } /** * Status whether the array is varying or not. * * @return true is array is varying. */ public boolean isVarying() { return isVarying; } Object decode(NetworkDataRepresentation ndr, Class arrayType, int dimension, List defferedPointers, int FLAG, Map additionalData) { JIArray retVal = new JIArray(); retVal.isConformantProxy = isConformantProxy; retVal.isVaryingProxy = isVaryingProxy; if (isConformantProxy) { //first read the max counts ...First to last dimension. int i = 0; while (i < dimension) { retVal.conformantMaxCounts.add(JIMarshalUnMarshalHelper.deSerialize(ndr, Integer.class, defferedPointers, FLAG, additionalData)); i++; } //isConformantProxy = false; //this is since decode is recursive. if (upperBounds == null) { //max elements will come now. retVal.numElementsInAllDimensions = 0; retVal.upperBounds = new int[retVal.conformantMaxCounts.size()]; i = 0; while (i < retVal.conformantMaxCounts.size()) { retVal.upperBounds[i] = ((Number) retVal.conformantMaxCounts.get(i)).intValue(); retVal.numElementsInAllDimensions *= retVal.upperBounds[i]; i++; } if (i == 0) { numElementsInAllDimensions = 0; } //retVal.numElementsInAllDimensions = retVal.numElementsInAllDimensions * dimension; } } else {//this is the case when it is non conformant or coming from struct. retVal.upperBounds = upperBounds; retVal.conformantMaxCounts = conformantMaxCounts; retVal.numElementsInAllDimensions = numElementsInAllDimensions; } if (isVaryingProxy) { //first read the max counts ...First to last dimension. int i = 0; retVal.conformantMaxCounts.clear();//can't take the max count size now retVal.upperBounds = null; retVal.numElementsInAllDimensions = 0; while (i < dimension) { JIMarshalUnMarshalHelper.deSerialize(ndr, Integer.class, defferedPointers, FLAG, null);///offset retVal.conformantMaxCounts.add(JIMarshalUnMarshalHelper.deSerialize(ndr, Integer.class, defferedPointers, FLAG, additionalData));//actual count i++; } //isConformantProxy = false; //this is since decode is recursive. if (upperBounds == null) { //max elements will come now. retVal.numElementsInAllDimensions = 1; retVal.upperBounds = new int[retVal.conformantMaxCounts.size()]; i = 0; while (i < retVal.conformantMaxCounts.size()) { retVal.upperBounds[i] = ((Number) retVal.conformantMaxCounts.get(i)).intValue(); retVal.numElementsInAllDimensions *= retVal.upperBounds[i]; i++; } if (i == 0) { numElementsInAllDimensions = 0; } //retVal.numElementsInAllDimensions = retVal.numElementsInAllDimensions * dimension; } } retVal.isConformant = isConformant; retVal.isVarying = isVarying; retVal.template = template; retVal.memberArray = recurseDecode(retVal, ndr, arrayType, dimension, defferedPointers, FLAG, additionalData); retVal.clazz = clazz; retVal.dimension = this.dimension; retVal.sizeOfNestedArrayInBytes = -1; // setting here so that when a call actually comes for it's lenght , the getLength will compute. This is required since while decoding many pointers are still not complete and their length cannot be decided. return retVal; } private Object recurseDecode(JIArray retVal, NetworkDataRepresentation ndr, Class arrayType, int dimension, List defferedPointers, int FLAG, Map additionalData) { Object array = null; Class c = arrayType; for (int j = 0; j < dimension; j++) { array = Array.newInstance(c, retVal.upperBounds[retVal.upperBounds.length - j - 1]); c = array.getClass(); } for (int i = 0; i < retVal.upperBounds[retVal.upperBounds.length - dimension]; i++) { if (dimension == 1) { //fill value here //Array.set(array,i,new Float(i)); if (template == null) { Array.set(array, i, JIMarshalUnMarshalHelper.deSerialize(ndr, c.getComponentType() == null ? c : c.getComponentType(), defferedPointers, FLAG | JIFlags.FLAG_REPRESENTATION_ARRAY, additionalData)); } else { Array.set(array, i, JIMarshalUnMarshalHelper.deSerialize(ndr, template, defferedPointers, FLAG | JIFlags.FLAG_REPRESENTATION_ARRAY, additionalData)); } } else { Array.set(array, i, recurseDecode(retVal, ndr, arrayType, dimension - 1, defferedPointers, FLAG, additionalData)); } } return array; } /** * Reverses Array elements for IJIDispatch. * * @return */ int reverseArrayForDispatch() { if (memberArray == null) { return 0; } int i = 0; Stack stack = new Stack(); for (i = 0; i < ((Object[]) memberArray).length; i++) { stack.push(((Object[]) memberArray)[i]); } i = 0; while (stack.size() > 0) { ((Object[]) memberArray)[i++] = stack.pop(); } return i; } List getConformantMaxCounts() { return conformantMaxCounts; } void setConformant(boolean isConformant) { isConformantProxy = isConformant; } void setVarying(boolean isVarying) { isVaryingProxy = isVarying; } void setMaxCountAndUpperBounds(List maxCount) { conformantMaxCounts = maxCount; // if (upperBounds == null) this will always be null since this api will get called from a decode and //in that the upperBounds is always null, since one does not know the dim expected. if (conformantMaxCounts.size() > 0) { //max elements will come now. numElementsInAllDimensions = 1; upperBounds = new int[conformantMaxCounts.size()]; int i = 0; while (i < conformantMaxCounts.size()) { upperBounds[i] = ((Number) conformantMaxCounts.get(i)).intValue(); numElementsInAllDimensions *= upperBounds[i]; i++; } if (i == 0) { numElementsInAllDimensions = 0; } } else { upperBounds = null; numElementsInAllDimensions = 0; } } int getNumElementsInAllDimensions() { return numElementsInAllDimensions; } /** * < p> * Used only from the JIVariant.getDecodedValueAsArray. It is required when * the real class of the array is determined after the SafeArray Struct has * been processed. SA in COM can contain these along with normal types as * well :- FADF_BSTR 0x0100 An array of BSTRs.
* FADF_UNKNOWN 0x0200 An array of IUnknown*.
* FADF_DISPATCH 0x0400 An array of IDispatch*.
* FADF_VARIANT 0x0800 An array of VARIANTs.
* * I have noticed that the "type" of the array doesn't always convey the * right thing, so this "feature" flag of the SA shas to be looked into. As * can be seen above except only BSTR require a template others do not. But * the logic for the JIString(BSTR) already works fine. So I will use this * flag only to set the JIVariant.class , whereever the "type" does not * specify it but the "feature" does. *

* * @exclude * @param c */ void updateClazz(Class c) { clazz = c; } @Override public String toString() { String retVal = "[Type: " + clazz + " , "; if (memberArray == null) { retVal += "memberArray is null , "; } else { retVal += memberArray + " , "; } if (isConformant) { retVal += " conformant , "; } if (isVarying) { retVal += " varying , "; } return retVal + "]"; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy