FileDocCategorySizeDatePackage
BeanDeserializer.javaAPI DocApache Axis 1.424111Sat Apr 22 18:57:28 BST 2006org.apache.axis.encoding.ser

BeanDeserializer

public class BeanDeserializer extends org.apache.axis.encoding.DeserializerImpl implements Serializable
General purpose deserializer for an arbitrary java bean.
author
Sam Ruby
author
Rich Scheuerle
author
Tom Jordahl

Fields Summary
protected static Log
log
private final CharArrayWriter
val
QName
xmlType
Class
javaType
protected Map
propertyMap
protected QName
prevQName
protected Constructor
constructorToUse
Constructor if no default constructor
protected org.apache.axis.encoding.Target
constructorTarget
Constructor Target object to use (if constructorToUse != null)
protected org.apache.axis.description.TypeDesc
typeDesc
Type metadata about this class for XML deserialization
protected int
collectionIndex
protected SimpleDeserializer
cacheStringDSer
protected QName
cacheXMLType
Constructors Summary
public BeanDeserializer(Class javaType, QName xmlType)


    // Construct BeanSerializer for the indicated class/qname
         
        this(javaType, xmlType, TypeDesc.getTypeDescForClass(javaType));
    
public BeanDeserializer(Class javaType, QName xmlType, org.apache.axis.description.TypeDesc typeDesc)

        this(javaType, xmlType, typeDesc,
             BeanDeserializerFactory.getProperties(javaType, typeDesc));
    
public BeanDeserializer(Class javaType, QName xmlType, org.apache.axis.description.TypeDesc typeDesc, Map propertyMap)

        this.xmlType = xmlType;
        this.javaType = javaType;
        this.typeDesc = typeDesc;
        this.propertyMap = propertyMap;

        // create a value
        try {
            value=javaType.newInstance();
        } catch (Exception e) {
            // Don't process the exception at this point.
            // This is defered until the call to startElement
            // which will throw the exception.
        }
    
Methods Summary
public voidcharacters(char[] chars, int start, int end)

        val.write(chars, start, end);
    
public org.apache.axis.utils.BeanPropertyDescriptorgetAnyPropertyDesc()
Get a BeanPropertyDescriptor which indicates where we should put extensibility elements (i.e. XML which falls under the auspices of an <xsd:any> declaration in the schema)

return
an appropriate BeanPropertyDescriptor, or null

        if (typeDesc == null)
            return null;
        
       return typeDesc.getAnyDesc();
    
protected org.apache.axis.encoding.DeserializergetDeserializer(javax.xml.namespace.QName xmlType, java.lang.Class javaType, java.lang.String href, org.apache.axis.encoding.DeserializationContext context)
Get the Deserializer for the attribute or child element.

param
xmlType QName of the attribute/child element or null if not known.
param
javaType Class of the corresponding property
param
href String is the value of the href attribute, which is used to determine whether the child element is complete or an href to another element.
param
context DeserializationContext
return
Deserializer or null if not found.

        if (javaType.isArray()) {
            context.setDestinationClass(javaType);
        } 
        // See if we have a cached deserializer
        if (cacheStringDSer != null) {
            if (String.class.equals(javaType) &&
                href == null &&
                (cacheXMLType == null && xmlType == null ||
                 cacheXMLType != null && cacheXMLType.equals(xmlType))) {
                cacheStringDSer.reset();
                return cacheStringDSer;
            }
        }
        
        Deserializer dSer = null;

        if (xmlType != null && href == null) {
            // Use the xmlType to get the deserializer.
            dSer = context.getDeserializerForType(xmlType);
        } else {
            // If the xmlType is not set, get a default xmlType
            TypeMapping tm = context.getTypeMapping();
            QName defaultXMLType = tm.getTypeQName(javaType);
            // If there is not href, then get the deserializer
            // using the javaType and default XMLType,
            // If there is an href, the create the generic
            // DeserializerImpl and set its default type (the
            // default type is used if the href'd element does 
            // not have an xsi:type.
            if (href == null) {
                dSer = context.getDeserializer(javaType, defaultXMLType);
            } else {
                dSer = new DeserializerImpl();
                context.setDestinationClass(javaType);
                dSer.setDefaultType(defaultXMLType);
            }
        }
        if (javaType.equals(String.class) &&
            dSer instanceof SimpleDeserializer) {
            cacheStringDSer = (SimpleDeserializer) dSer;
            cacheXMLType = xmlType;
        }
        return dSer;
    
protected voidhandleMixedContent()

        BeanPropertyDescriptor propDesc = getAnyPropertyDesc();
        if (propDesc == null || val.size() == 0) {
            return;
        }
        String textValue = val.toString().trim();
        val.reset();
        if (textValue.length() == 0) {
            return;
        }
        try {
            MessageElement[] curElements = (MessageElement[]) propDesc.get(value);
            int length = 0;
            if (curElements != null) {
                length = curElements.length;
            }
            MessageElement[] newElements = new MessageElement[length + 1];
            if (curElements != null) {
                System.arraycopy(curElements, 0,
                        newElements, 0, length);
            }
            MessageElement thisEl = new MessageElement(new org.apache.axis.message.Text(textValue));
            newElements[length] = thisEl;
            propDesc.set(value, newElements);
        } catch (Exception e) {
            throw new SAXException(e);
        }
    
public voidonEndElement(java.lang.String namespace, java.lang.String localName, org.apache.axis.encoding.DeserializationContext context)

        handleMixedContent();
    
public org.apache.axis.message.SOAPHandleronStartChild(java.lang.String namespace, java.lang.String localName, java.lang.String prefix, org.xml.sax.Attributes attributes, org.apache.axis.encoding.DeserializationContext context)
Deserializer interface called on each child element encountered in the XML stream.

param
namespace is the namespace of the child element
param
localName is the local name of the child element
param
prefix is the prefix used on the name of the child element
param
attributes are the attributes of the child element
param
context is the deserialization context.
return
is a Deserializer to use to deserialize a child (must be a derived class of SOAPHandler) or null if no deserialization should be performed.

        handleMixedContent();

        BeanPropertyDescriptor propDesc = null;
        FieldDesc fieldDesc = null;

        SOAPConstants soapConstants = context.getSOAPConstants();
        String encodingStyle = context.getEncodingStyle();
        boolean isEncoded = Constants.isSOAP_ENC(encodingStyle);

        QName elemQName = new QName(namespace, localName);
        // The collectionIndex needs to be reset for Beans with multiple arrays
        if ((prevQName == null) || (!prevQName.equals(elemQName))) {
            collectionIndex = -1;
        }  

        boolean isArray = false;
        QName itemQName = null;
        if (typeDesc != null) {
            // Lookup the name appropriately (assuming an unqualified
            // name for SOAP encoding, using the namespace otherwise)
            String fieldName = typeDesc.getFieldNameForElement(elemQName, 
                                                               isEncoded);
            propDesc = (BeanPropertyDescriptor)propertyMap.get(fieldName);
            fieldDesc = typeDesc.getFieldByName(fieldName);

            if (fieldDesc != null) {
               ElementDesc element = (ElementDesc)fieldDesc;
               isArray = element.isMaxOccursUnbounded();
               itemQName = element.getItemQName();
           }
        }

        if (propDesc == null) {
            // look for a field by this name.
            propDesc = (BeanPropertyDescriptor) propertyMap.get(localName);
        }

        // try and see if this is an xsd:any namespace="##any" element before
        // reporting a problem
        if (propDesc == null || 
        		(((prevQName != null) && prevQName.equals(elemQName) &&
        				!(propDesc.isIndexed()||isArray)
						&& getAnyPropertyDesc() != null ))) {
            // try to put unknown elements into a SOAPElement property, if
            // appropriate
        	prevQName = elemQName;
            propDesc = getAnyPropertyDesc();
            if (propDesc != null) {
                try {
                    MessageElement [] curElements = (MessageElement[])propDesc.get(value);
                    int length = 0;
                    if (curElements != null) {
                        length = curElements.length;
                    }
                    MessageElement [] newElements = new MessageElement[length + 1];
                    if (curElements != null) {
                        System.arraycopy(curElements, 0,
                                         newElements, 0, length);
                    }
                    MessageElement thisEl = context.getCurElement();

                    newElements[length] = thisEl;
                    propDesc.set(value, newElements);
                    // if this is the first pass through the MessageContexts
                    // make sure that the correct any element is set,
                    // that is the child of the current MessageElement, however
                    // on the first pass this child has not been set yet, so
                    // defer it to the child SOAPHandler
                    if (!localName.equals(thisEl.getName())) {
                        return new SOAPHandler(newElements, length);
                    }
                    return new SOAPHandler();
                } catch (Exception e) {
                    throw new SAXException(e);
                }
            }
        }


        if (propDesc == null) {
            // No such field
            throw new SAXException(
                    Messages.getMessage("badElem00", javaType.getName(), 
                                         localName));
        }

        prevQName = elemQName;
        // Get the child's xsi:type if available
        QName childXMLType = context.getTypeFromAttributes(namespace, 
                                                            localName,
                                                            attributes);

        String href = attributes.getValue(soapConstants.getAttrHref());
        Class fieldType = propDesc.getType();

        // If no xsi:type or href, check the meta-data for the field
        if (childXMLType == null && fieldDesc != null && href == null) {
            childXMLType = fieldDesc.getXmlType();
            if (itemQName != null) {
                // This is actually a wrapped literal array and should be
                // deserialized with the ArrayDeserializer
                childXMLType = Constants.SOAP_ARRAY;
                fieldType = propDesc.getActualType();
            } else {
                childXMLType = fieldDesc.getXmlType();
            }
        }
        
        // Get Deserializer for child, default to using DeserializerImpl
        Deserializer dSer = getDeserializer(childXMLType,
                                            fieldType,
                                            href,
                                            context);

        // It is an error if the dSer is not found - the only case where we
        // wouldn't have a deserializer at this point is when we're trying
        // to deserialize something we have no clue about (no good xsi:type,
        // no good metadata).
        if (dSer == null) {
            dSer = context.getDeserializerForClass(propDesc.getType());
        }

        // Fastpath nil checks...
        if (context.isNil(attributes)) {
            if (propDesc != null && (propDesc.isIndexed()||isArray)) {
                if (!((dSer != null) && (dSer instanceof ArrayDeserializer))) {
                    collectionIndex++;
                    dSer.registerValueTarget(new BeanPropertyTarget(value,
                            propDesc, collectionIndex));
                    addChildDeserializer(dSer);
                    return (SOAPHandler)dSer;
                }
            }
            return null;
        }            
          
        if (dSer == null) {
            throw new SAXException(Messages.getMessage("noDeser00",
                                                       childXMLType.toString()));
        }

        if (constructorToUse != null) {
            if (constructorTarget == null) {
                constructorTarget = new ConstructorTarget(constructorToUse, this);
            }
            dSer.registerValueTarget(constructorTarget);
        } else if (propDesc.isWriteable()) {
            // If this is an indexed property, and the deserializer we found
            // was NOT the ArrayDeserializer, this is a non-SOAP array:
            // <bean>
            //   <field>value1</field>
            //   <field>value2</field>
            // ...
            // In this case, we want to use the collectionIndex and make sure
            // the deserialized value for the child element goes into the
            // right place in the collection.

            // Register value target
            if ((itemQName != null || propDesc.isIndexed() || isArray) && !(dSer instanceof ArrayDeserializer)) {
                collectionIndex++;
                dSer.registerValueTarget(new BeanPropertyTarget(value,
                        propDesc, collectionIndex));
            } else {
                // If we're here, the element maps to a single field value,
                // whether that be a "basic" type or an array, so use the
                // normal (non-indexed) BeanPropertyTarget form.
                collectionIndex = -1;
                dSer.registerValueTarget(new BeanPropertyTarget(value,
                                                                propDesc));
            }
        }
        
        // Let the framework know that we need this deserializer to complete
        // for the bean to complete.
        addChildDeserializer(dSer);
        
        return (SOAPHandler)dSer;
    
public voidonStartElement(java.lang.String namespace, java.lang.String localName, java.lang.String prefix, org.xml.sax.Attributes attributes, org.apache.axis.encoding.DeserializationContext context)
Set the bean properties that correspond to element attributes. This method is invoked after startElement when the element requires deserialization (i.e. the element is not an href and the value is not nil.)

param
namespace is the namespace of the element
param
localName is the name of the element
param
prefix is the prefix of the element
param
attributes are the attributes on the element...used to get the type
param
context is the DeserializationContext


        // The value should have been created or assigned already.
        // This code may no longer be needed.
        if (value == null && constructorToUse == null) {
            // create a value
            try {
                value=javaType.newInstance();
            } catch (Exception e) {
                throw new SAXException(Messages.getMessage("cantCreateBean00", 
                                                            javaType.getName(), 
                                                            e.toString()));
            }
        }

        // If no type description meta data, there are no attributes,
        // so we are done.
        if (typeDesc == null)
            return;

        // loop through the attributes and set bean properties that 
        // correspond to attributes
        for (int i=0; i < attributes.getLength(); i++) {
            QName attrQName = new QName(attributes.getURI(i),
                                        attributes.getLocalName(i));
            String fieldName = typeDesc.getFieldNameForAttribute(attrQName);
            if (fieldName == null)
                continue;

            FieldDesc fieldDesc = typeDesc.getFieldByName(fieldName);
            
            // look for the attribute property
            BeanPropertyDescriptor bpd =
                    (BeanPropertyDescriptor) propertyMap.get(fieldName);
            if (bpd != null) {
                if (constructorToUse == null) {
                    // check only if default constructor
                    if (!bpd.isWriteable() || bpd.isIndexed()) continue ;
                }
                
                // Get the Deserializer for the attribute
                Deserializer dSer = getDeserializer(fieldDesc.getXmlType(),
                                                    bpd.getType(), 
                                                    null,
                                                    context);
                if (dSer == null) {
                    dSer = context.getDeserializerForClass(bpd.getType());

                    // The java type is an array, but the context didn't
                    // know that we are an attribute.  Better stick with
                    // simple types..
                    if (dSer instanceof ArrayDeserializer)
                    {
                        SimpleListDeserializerFactory factory =
                            new SimpleListDeserializerFactory(bpd.getType(),
                                    fieldDesc.getXmlType());
                        dSer = (Deserializer)
                            factory.getDeserializerAs(dSer.getMechanismType());
                    }
                }

                if (dSer == null)
                    throw new SAXException(
                            Messages.getMessage("unregistered00",
                                                 bpd.getType().toString()));
                
                if (! (dSer instanceof SimpleDeserializer))
                    throw new SAXException(
                            Messages.getMessage("AttrNotSimpleType00", 
                                                 bpd.getName(), 
                                                 bpd.getType().toString()));
                
                // Success!  Create an object from the string and set
                // it in the bean
                try {
                    dSer.onStartElement(namespace, localName, prefix,
                                        attributes, context);
                    Object val = ((SimpleDeserializer)dSer).
                        makeValue(attributes.getValue(i));
                    if (constructorToUse == null) {
                        bpd.set(value, val);
                    } else {
                        // add value for our constructor
                        if (constructorTarget == null) {
                            constructorTarget = new ConstructorTarget(constructorToUse, this);
                        }
                        constructorTarget.set(val);
                    }
                } catch (Exception e) {
                    throw new SAXException(e);
                }
                
            } // if
        } // attribute loop
    
public voidstartElement(java.lang.String namespace, java.lang.String localName, java.lang.String prefix, org.xml.sax.Attributes attributes, org.apache.axis.encoding.DeserializationContext context)
startElement The ONLY reason that this method is overridden is so that the object value can be set or a reasonable exception is thrown indicating that the object cannot be created. This is done at this point so that it occurs BEFORE href/id processing.

param
namespace is the namespace of the element
param
localName is the name of the element
param
prefix is the prefix of the element
param
attributes are the attributes on the element...used to get the type
param
context is the DeserializationContext

        // Create the bean object if it was not already
        // created in the constructor.
        if (value == null) {
            try {
                value=javaType.newInstance();
            } catch (Exception e) {
                // Use first found constructor.
                // Note : the right way is to use XML mapping information
                // for example JSR 109's constructor-parameter-order
                Constructor[] constructors = javaType.getConstructors();
                if (constructors.length > 0) {
                    constructorToUse = constructors[0];
                }

                // Failed to create an object if no constructor
                if (constructorToUse == null) {
                    throw new SAXException(Messages.getMessage("cantCreateBean00", 
                                                            javaType.getName(), 
                                                            e.toString()));
                }
            }
        }
        // Invoke super.startElement to do the href/id processing.
        super.startElement(namespace, localName, 
                           prefix, attributes, context);