ArrayDeserializerpublic class ArrayDeserializer extends org.apache.axis.encoding.DeserializerImpl An ArrayDeserializer handles deserializing SOAP
arrays.
Some code borrowed from ApacheSOAP - thanks to Matt Duftler! |
Fields Summary |
---|
protected static Log | log | public QName | arrayType | public int | curIndex | QName | defaultItemType | int | length | Class | arrayClass | ArrayList | mDimLength | ArrayList | mDimFactor | org.apache.axis.soap.SOAPConstants | soapConstants |
Methods Summary |
---|
public void | characters(char[] chars, int i, int i1)
for (int idx = i; i < i1; i++) {
if (!Character.isWhitespace(chars[idx]))
throw new SAXException(Messages.getMessage("charsInArray"));
}
| private int | convertToIndex(java.lang.String text, java.lang.String exceptKey)Converts the given string to an index.
Assumes the string consists of a brackets surrounding comma
separated digits. For example "[2]" or [2,3]".
The routine returns a single index.
For example "[2]" returns 2.
For example "[2,3]" depends on the size of the multiple dimensions.
if the dimensions are "[3,5]" then 13 is returned (2*5) + 3.
StringTokenizer tokenizer = new StringTokenizer(text, "[],");
int index = 0;
try {
if (mDimLength == null) {
// Normal Case: Single dimension
index = Integer.parseInt(tokenizer.nextToken());
if (tokenizer.hasMoreTokens()) {
throw new SAXException(
Messages.getMessage(exceptKey, text));
}
}
else {
// Multiple Dimensions:
int dim = -1;
ArrayList work = new ArrayList();
while(tokenizer.hasMoreTokens()) {
// Problem if the number of dimensions specified exceeds
// the number of dimensions of arrayType
dim++;
if (dim >= mDimLength.size()) {
throw new SAXException(
Messages.getMessage(exceptKey, text));
}
// Get the next token and convert to integer
int workIndex = Integer.parseInt(tokenizer.nextToken());
// Problem if the index is out of range.
if (workIndex < 0 ||
workIndex >=
((Integer)mDimLength.get(dim)).intValue()) {
throw new SAXException(
Messages.getMessage(exceptKey, text));
}
work.add(new Integer(workIndex));
}
index = toSingleIndex(work); // Convert to single index
}
} catch (SAXException e) {
throw e;
} catch (Exception e) {
throw new SAXException(Messages.getMessage(exceptKey, text));
}
return index;
| public void | onEndChild(java.lang.String namespace, java.lang.String localName, org.apache.axis.encoding.DeserializationContext context)
// reverse onStartChild operation.
context.setDestinationClass(arrayClass);
| public org.apache.axis.message.SOAPHandler | onStartChild(java.lang.String namespace, java.lang.String localName, java.lang.String prefix, org.xml.sax.Attributes attributes, org.apache.axis.encoding.DeserializationContext context)onStartChild is called on each child element.
if (log.isDebugEnabled()) {
log.debug("Enter: ArrayDeserializer.onStartChild()");
}
// If the position attribute is set,
// use it to update the current index
if (attributes != null) {
String pos =
Constants.getValue(attributes,
Constants.URIS_SOAP_ENC,
Constants.ATTR_POSITION);
if (pos != null) {
if (soapConstants == SOAPConstants.SOAP12_CONSTANTS) {
throw new SAXException(Messages.getMessage("noSparseArray"));
}
int leftBracketIndex = pos.lastIndexOf('[");
int rightBracketIndex = pos.lastIndexOf(']");
if (leftBracketIndex == -1
|| rightBracketIndex == -1
|| rightBracketIndex < leftBracketIndex)
{
throw new SAXException(
Messages.getMessage("badPosition00", pos));
}
curIndex =
convertToIndex(pos.substring(leftBracketIndex + 1,
rightBracketIndex),
"badPosition00");
}
// If the xsi:nil attribute, set the value to null
// and return since there is nothing to deserialize.
if (context.isNil(attributes)) {
setChildValue(null, new Integer(curIndex++));
return null;
}
}
// Use the xsi:type setting on the attribute if it exists.
QName itemType = context.getTypeFromAttributes(namespace,
localName,
attributes);
// Get the deserializer for the type.
Deserializer dSer = null;
if (itemType != null && (context.getCurElement().getHref() == null)) {
dSer = context.getDeserializerForType(itemType);
}
if (dSer == null) {
// No deserializer can be found directly. Need to look harder
QName defaultType = defaultItemType;
Class javaType = null;
if (arrayClass != null &&
arrayClass.isArray() &&
defaultType == null) {
javaType = arrayClass.getComponentType();
defaultType = context.getTypeMapping().getTypeQName(javaType);
}
// We don't have a deserializer, the safest thing to do
// is to set up using the DeserializerImpl below.
// The DeserializerImpl will take care of href/id and
// install the appropriate serializer, etc. The problem
// is that takes a lot of time and will occur
// all the time if no xsi:types are sent. Most of the
// time an item is a simple schema type (i.e. String)
// so the following shortcut is used to get a Deserializer
// for these cases.
if (itemType == null && dSer == null) {
if (defaultType != null && SchemaUtils.isSimpleSchemaType(defaultType)) {
dSer = context.getDeserializer(javaType, defaultType);
}
}
// If no deserializer is
// found, the deserializer is set to DeserializerImpl().
// It is possible that the element has an href, thus we
// won't know the type until the definitition is encountered.
if (dSer == null) {
dSer = new DeserializerImpl();
// Determine a default type for the deserializer
if (itemType == null) {
dSer.setDefaultType(defaultType);
}
}
}
// Register the callback value target, and
// keep track of this index so we know when it has been set.
dSer.registerValueTarget(
new DeserializerTarget(this, new Integer(curIndex)));
// The framework handles knowing when the value is complete, as
// long as we tell it about each child we're waiting on...
addChildDeserializer(dSer);
curIndex++;
// In case of multi-array, we need to specify the destination class
// of the children elements of this element array deserializer.
context.setDestinationClass(arrayClass.getComponentType());
if (log.isDebugEnabled()) {
log.debug("Exit: ArrayDeserializer.onStartChild()");
}
return (SOAPHandler)dSer;
| public void | onStartElement(java.lang.String namespace, java.lang.String localName, java.lang.String prefix, org.xml.sax.Attributes attributes, org.apache.axis.encoding.DeserializationContext context)This method is invoked after startElement when the element requires
deserialization (i.e. the element is not an href & the value is not nil)
DeserializerImpl provides default behavior, which simply
involves obtaining a correct Deserializer and plugging its handler.
// Deserializing the xml array requires processing the
// xsi:type= attribute, the soapenc:arrayType attribute,
// and the xsi:type attributes of the individual elements.
//
// The xsi:type=<qName> attribute is used to determine the java
// type of the array to instantiate. Axis expects it
// to be set to the generic "soapenc:Array" or to
// a specific qName. If the generic "soapenc:Array"
// specification is used, Axis determines the array
// type by examining the soapenc:arrayType attribute.
//
// The soapenc:arrayType=<qname><dims> is used to determine
// i) the number of dimensions,
// ii) the length of each dimension,
// iii) the default xsi:type of each of the elements.
//
// If the arrayType attribute is missing, Axis assumes
// a single dimension array with length equal to the number
// of nested elements. In such cases, the default xsi:type of
// the elements is determined using the array xsi:type.
//
// The xsi:type attributes of the individual elements of the
// array are used to determine the java type of the element.
// If the xsi:type attribute is missing for an element, the
// default xsi:type value is used.
if (log.isDebugEnabled()) {
log.debug("Enter: ArrayDeserializer::startElement()");
}
soapConstants = context.getSOAPConstants();
// Get the qname for the array type=, set it to null if
// the generic type is used.
QName typeQName = context.getTypeFromAttributes(namespace,
localName,
attributes);
if (typeQName == null) {
typeQName = getDefaultType();
}
if (typeQName != null &&
Constants.equals(Constants.SOAP_ARRAY, typeQName)) {
typeQName = null;
}
// Now get the arrayType value
QName arrayTypeValue = context.getQNameFromString(
Constants.getValue(attributes,
Constants.URIS_SOAP_ENC,
soapConstants.getAttrItemType()));
// The first part of the arrayType expression is
// the default item type qname.
// The second part is the dimension information
String dimString = null;
QName innerQName = null;
String innerDimString = "";
if (arrayTypeValue != null) {
if (soapConstants != SOAPConstants.SOAP12_CONSTANTS) {
// Doing SOAP 1.1
// Array dimension noted like this : [][x]
String arrayTypeValueNamespaceURI =
arrayTypeValue.getNamespaceURI();
String arrayTypeValueLocalPart =
arrayTypeValue.getLocalPart();
int leftBracketIndex =
arrayTypeValueLocalPart.lastIndexOf('[");
int rightBracketIndex =
arrayTypeValueLocalPart.lastIndexOf(']");
if (leftBracketIndex == -1
|| rightBracketIndex == -1
|| rightBracketIndex < leftBracketIndex) {
throw new IllegalArgumentException(
Messages.getMessage("badArrayType00",
"" + arrayTypeValue));
}
dimString =
arrayTypeValueLocalPart.substring(leftBracketIndex + 1,
rightBracketIndex);
arrayTypeValueLocalPart =
arrayTypeValueLocalPart.substring(0, leftBracketIndex);
// If multi-dim array set to soapenc:Array
if (arrayTypeValueLocalPart.endsWith("]")) {
defaultItemType = Constants.SOAP_ARRAY;
int bracket = arrayTypeValueLocalPart.indexOf("[");
innerQName = new QName(arrayTypeValueNamespaceURI,
arrayTypeValueLocalPart.substring(0, bracket));
innerDimString = arrayTypeValueLocalPart.substring(bracket);
} else {
defaultItemType = new QName(arrayTypeValueNamespaceURI,
arrayTypeValueLocalPart);
}
} else {
String arraySizeValue = attributes.getValue(soapConstants.getEncodingURI(), Constants.ATTR_ARRAY_SIZE);
int leftStarIndex = arraySizeValue.lastIndexOf('*");
// Skip to num if any
if (leftStarIndex != -1) {
// "*" => ""
if (leftStarIndex == 0 && arraySizeValue.length() == 1) {
// "* *" => ""
} else if (leftStarIndex == (arraySizeValue.length() - 1)) {
throw new IllegalArgumentException(
Messages.getMessage("badArraySize00",
"" + arraySizeValue));
// "* N" => "N"
} else {
dimString = arraySizeValue.substring(leftStarIndex + 2);
innerQName = arrayTypeValue;
innerDimString = arraySizeValue.substring(0, leftStarIndex + 1);
}
} else {
dimString = arraySizeValue;
}
if (innerDimString == null || innerDimString.length() == 0) {
defaultItemType = arrayTypeValue;
} else {
defaultItemType = Constants.SOAP_ARRAY12;
}
}
}
// If no type QName and no defaultItemType qname, use xsd:anyType
if (defaultItemType == null && typeQName == null) {
Class destClass = context.getDestinationClass();
if (destClass != null && destClass.isArray()) {
// This will get set OK down below...
} else {
defaultItemType = Constants.XSD_ANYTYPE;
}
}
// Determine the class type for the array.
arrayClass = null;
if (typeQName != null) {
arrayClass = context.getTypeMapping().
getClassForQName(typeQName);
}
if (typeQName == null || arrayClass == null) {
// type= information is not sufficient.
// Get an array of the default item type.
Class arrayItemClass = null;
QName compQName = defaultItemType;
// Nested array, use the innermost qname
String dims = "[]";
if (innerQName != null) {
compQName = innerQName;
if (soapConstants == SOAPConstants.SOAP12_CONSTANTS) {
// With SOAP 1.2 Array, we append [] for each * found
int offset = 0;
while ((offset = innerDimString.indexOf('*", offset)) != -1) {
dims += "[]";
offset ++;
}
} else {
// With SOAP 1.1 Array, we can append directly the complete innerDimString
dims += innerDimString;
}
}
// item Class
arrayItemClass = context.getTypeMapping().getClassForQName(compQName);
if (arrayItemClass != null) {
try {
// Append the dimension found to the classname computed from the itemClass
// to form the array classname
//
String loadableArrayClassName = JavaUtils.getLoadableClassName(
JavaUtils.getTextClassName(arrayItemClass.getName()) + dims);
arrayClass = ClassUtils.forName(loadableArrayClassName,
true,
arrayItemClass.getClassLoader());
} catch (Exception e) {
throw new SAXException(
Messages.getMessage("noComponent00",
"" + defaultItemType));
}
}
}
if (arrayClass == null) {
arrayClass = context.getDestinationClass();
}
if (arrayClass == null) {
throw new SAXException(
Messages.getMessage("noComponent00", "" + defaultItemType));
}
if (dimString == null || dimString.length() == 0) {
// Size determined using length of the members
value = new ArrayListExtension(arrayClass);
} else {
try
{
StringTokenizer tokenizer;
if (soapConstants == SOAPConstants.SOAP12_CONSTANTS) {
tokenizer = new StringTokenizer(dimString);
} else {
tokenizer = new StringTokenizer(dimString, "[],");
}
length = Integer.parseInt(tokenizer.nextToken());
if (tokenizer.hasMoreTokens()) {
// If the array is passed as a multi-dimensional array
// (i.e. int[2][3]) then store all of the
// mult-dim lengths.
// The valueReady method uses this array to set the
// proper mult-dim element.
mDimLength = new ArrayList();
mDimLength.add(new Integer(length));
while(tokenizer.hasMoreTokens()) {
mDimLength.add(
new Integer(
Integer.parseInt(tokenizer.nextToken())));
}
}
// Create an ArrayListExtension class to store the ArrayList
// plus converted objects.
ArrayList list = new ArrayListExtension(arrayClass, length);
// This is expensive as our array may not grown this big.
// Prevents problems when XML claims a huge size
// that it doesn't actually fill.
//for (int i = 0; i < length; i++) {
// list.add(null);
//}
value = list;
}
catch (NumberFormatException e)
{
throw new IllegalArgumentException(
Messages.getMessage("badInteger00", dimString));
}
}
// If soapenc:offset specified, set the current index accordingly
String offset = Constants.getValue(attributes,
Constants.URIS_SOAP_ENC,
Constants.ATTR_OFFSET);
if (offset != null) {
if (soapConstants == SOAPConstants.SOAP12_CONSTANTS) {
throw new SAXException(Messages.getMessage("noSparseArray"));
}
int leftBracketIndex = offset.lastIndexOf('[");
int rightBracketIndex = offset.lastIndexOf(']");
if (leftBracketIndex == -1
|| rightBracketIndex == -1
|| rightBracketIndex < leftBracketIndex)
{
throw new SAXException(
Messages.getMessage("badOffset00", offset));
}
curIndex =
convertToIndex(offset.substring(leftBracketIndex + 1,
rightBracketIndex),
"badOffset00");
}
if (log.isDebugEnabled()) {
log.debug("Exit: ArrayDeserializer::startElement()");
}
| public void | setChildValue(java.lang.Object value, java.lang.Object hint)set is called during deserialization to assign
the Object value to the array position indicated by hint.
The hint is always a single Integer. If the array being
deserialized is a multi-dimensional array, the hint is
converted into a series of indices to set the correct
nested position.
The array deserializer always deserializes into
an ArrayList, which is converted and copied into the
actual array after completion (by valueComplete).
It is important to wait until all indices have been
processed before invoking valueComplete.
if (log.isDebugEnabled()) {
log.debug("Enter: ArrayDeserializer::setValue(" + value + ", " + hint + ")");
}
ArrayList list = (ArrayList)this.value;
int offset = ((Integer)hint).intValue();
if (this.mDimLength == null) {
// Normal Case: Set the element in the list
// grow the list if necessary to accomodate the new member
while (list.size() <= offset) {
list.add(null);
}
list.set(offset, value);
} else {
// Multi-Dim Array case: Need to find the nested ArrayList
// and set the proper element.
// Convert the offset into a series of indices
ArrayList mDimIndex = toMultiIndex(offset);
// Get/Create the nested ArrayList
for(int i=0; i < mDimLength.size(); i++) {
int length = ((Integer)mDimLength.get(i)).intValue();
int index = ((Integer)mDimIndex.get(i)).intValue();
while (list.size() < length) {
list.add(null);
}
// If not the last dimension, get the nested ArrayList
// Else set the value
if (i < mDimLength.size()-1) {
if (list.get(index) == null) {
list.set(index, new ArrayList());
}
list = (ArrayList) list.get(index);
} else {
list.set(index, value);
}
}
}
| private java.util.ArrayList | toMultiIndex(int single)Converts single index to list of multiple indices.
if (mDimLength == null)
return null;
// Calculate the index factors if not already known
if (mDimFactor == null) {
mDimFactor = new ArrayList();
for (int i=0; i < mDimLength.size(); i++) {
int factor = 1;
for (int j=i+1; j<mDimLength.size(); j++) {
factor *= ((Integer)mDimLength.get(j)).intValue();
}
mDimFactor.add(new Integer(factor));
}
}
ArrayList rc = new ArrayList();
for (int i=0; i < mDimLength.size(); i++) {
int factor = ((Integer)mDimFactor.get(i)).intValue();
rc.add(new Integer(single / factor));
single = single % factor;
}
return rc;
| private int | toSingleIndex(java.util.ArrayList indexArray)Converts multiple index to single index.
if (mDimLength == null || indexArray == null)
return -1;
// Calculate the index factors if not already known
if (mDimFactor == null) {
mDimFactor = new ArrayList();
for (int i=0; i < mDimLength.size(); i++) {
int factor = 1;
for (int j=i+1; j<mDimLength.size(); j++) {
factor *= ((Integer)mDimLength.get(j)).intValue();
}
mDimFactor.add(new Integer(factor));
}
}
int single = 0;
for (int i=0; i < indexArray.size(); i++) {
single += ((Integer)mDimFactor.get(i)).intValue()*
((Integer)indexArray.get(i)).intValue();
}
return single;
| public void | valueComplete()When valueComplete() is invoked on the array,
first convert the array value into the expected array.
Then call super.valueComplete() to inform referents
that the array value is ready.
if (componentsReady()) {
try {
if (arrayClass != null) {
value = JavaUtils.convert(value, arrayClass);
}
} catch (RuntimeException e) {
// We must ignore exceptions from convert for Arrays with null - why?
}
}
super.valueComplete();
|
|