tomj 02/02/19 12:50:46 Modified: java/src/org/apache/axis/encoding/ser BeanSerializer.java BeanDeserializer.java Log: First step to supporting <attribute> in schema types: Add support for bean properties which are attributes instead of element. If the bean has a static getAttributeElements() function, which returns a Vector of property names, we use this to serialize/deserialize those properties as attributes on the element. Revision Changes Path 1.5 +107 -28 xml-axis/java/src/org/apache/axis/encoding/ser/BeanSerializer.java Index: BeanSerializer.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/encoding/ser/BeanSerializer.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- BeanSerializer.java 19 Feb 2002 17:38:20 -0000 1.4 +++ BeanSerializer.java 19 Feb 2002 20:50:46 -0000 1.5 @@ -57,6 +57,7 @@ import org.xml.sax.Attributes; import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; import javax.xml.rpc.namespace.QName; import java.io.IOException; @@ -89,12 +90,14 @@ import java.util.HashMap; import java.util.Vector; +import java.util.Iterator; /** * General purpose serializer/deserializerFactory for an arbitrary java bean. * * @author Sam Ruby <[EMAIL PROTECTED]> * @author Rich Scheuerle <[EMAIL PROTECTED]> + * @author Tom Jordahl <[EMAIL PROTECTED]> */ public class BeanSerializer implements Serializer, Serializable { @@ -117,23 +120,25 @@ QName xmlType; Class javaType; - // Static Table to store pd[] keyed by class - private static HashMap pdMap = new HashMap(); + private BeanPropertyDescriptor[] propertyDescriptor = null; + private Vector beanAttributeNames = null; + // Construct BeanSerializer for the indicated class/qname public BeanSerializer(Class javaType, QName xmlType) { this.xmlType = xmlType; this.javaType = javaType; + propertyDescriptor = getPd(javaType); + beanAttributeNames = getBeanAttributes(javaType); } // Construct BeanSerializer for the indicated class/qname and format public BeanSerializer(Class javaType, QName xmlType, short format) { this.xmlType = xmlType; this.javaType = javaType; - if (format > FORCE_LOWER || - format < PROPERTY_NAME) - format = PROPERTY_NAME; - this.elementPropertyFormat = format; + setElementPropertyFormat(format); + propertyDescriptor = getPd(javaType); + beanAttributeNames = getBeanAttributes(javaType); } /** @@ -147,32 +152,43 @@ Object value, SerializationContext context) throws IOException { - // Get the property descriptors describing the bean properties - BeanPropertyDescriptor[] pd = getPd(javaType); - - context.startElement(name, attributes); + boolean isSOAP_ENC = Constants. + isSOAP_ENC(context.getMessageContext().getEncodingStyle()); + if (isSOAP_ENC) { + // SOAP encoding doesn't allow attributes + context.startElement(name, attributes); + } else { + // Check for meta-data in the bean that will tell us if any of the + // properties are actually attributes, add those to the element + // attribute list + Attributes beanAttrs = getObjectAttributes(value, attributes); + context.startElement(name, beanAttrs); + } try { // Serialize each property - for (int i=0; i<pd.length; i++) { - String propName = pd[i].getName(); - if (propName.equals("class")) continue; + for (int i=0; i<propertyDescriptor.length; i++) { + String propName = propertyDescriptor[i].getName(); + if (propName.equals("class")) + continue; + if (!isSOAP_ENC && beanAttributeNames.contains(propName)) + continue; propName = format(propName, elementPropertyFormat); - Method readMethod = pd[i].getReadMethod(); + Method readMethod = propertyDescriptor[i].getReadMethod(); if (readMethod != null && readMethod.getParameterTypes().length == 0) { // Normal case: serialize the value - Object propValue = pd[i].getReadMethod().invoke(value,noArgs); + Object propValue = propertyDescriptor[i].getReadMethod().invoke(value,noArgs); context.serialize(new QName("", propName), null, propValue, - pd[i].getReadMethod().getReturnType()); + propertyDescriptor[i].getReadMethod().getReturnType()); } else { // Collection of properties: serialize each one int j=0; while(j >= 0) { Object propValue = null; try { - propValue = pd[i].getReadMethod().invoke(value, + propValue = propertyDescriptor[i].getReadMethod().invoke(value, new Object[] { new Integer(j) }); j++; } catch (Exception e) { @@ -181,7 +197,7 @@ if (j >= 0) { context.serialize(new QName("", propName), null, propValue, - pd[i].getReadMethod().getReturnType()); + propertyDescriptor[i].getReadMethod().getReturnType()); } } } @@ -195,23 +211,40 @@ } /** - * Get/Create a BeanPropertyDescriptor array for the indicated class. + * Create a BeanPropertyDescriptor array for the indicated class. */ static BeanPropertyDescriptor[] getPd(Class javaType) { - BeanPropertyDescriptor[] pd = (BeanPropertyDescriptor[]) pdMap.get(javaType); - if (pd == null) { - try { - PropertyDescriptor[] rawPd = Introspector.getBeanInfo(javaType).getPropertyDescriptors(); - pd = BeanPropertyDescriptor.processPropertyDescriptors(rawPd,javaType); - } catch (Exception e) { - // this should never happen - throw new InternalException(e); - } + BeanPropertyDescriptor[] pd; + try { + PropertyDescriptor[] rawPd = Introspector.getBeanInfo(javaType).getPropertyDescriptors(); + pd = BeanPropertyDescriptor.processPropertyDescriptors(rawPd,javaType); + } catch (Exception e) { + // this should never happen + throw new InternalException(e); } return pd; } /** + * Return a list of properties in the bean which should be attributes + */ + static Vector getBeanAttributes(Class javaType) { + // See if this object defined the 'getAttributeElements' function + // which returns a Vector of property names that are attributes + try { + Method getAttributeElements = + javaType.getMethod("getAttributeElements", + new Class [] {}); + + return (Vector) getAttributeElements.invoke(null, noArgs); + + } catch (Exception e) { + return new Vector(); // empty vector + } + } + + + /** * Get the format of the elements for the properties */ public short getElementPropertyFormat() { @@ -260,4 +293,50 @@ return true; } + /** + * Check for meta-data in the bean that will tell us if any of the + * properties are actually attributes, add those to the element + * attribute list + * + * @param value the object we are serializing + * @param pd the properties of this class + * @return attributes for this element, null if none + */ + private Attributes getObjectAttributes(Object value, + Attributes attributes) { + + if (beanAttributeNames.isEmpty()) + return attributes; + + AttributesImpl attrs = new AttributesImpl(attributes); + + try { + // Find each property that is an attribute + // and add it to our attribute list + for (int i=0; i<propertyDescriptor.length; i++) { + String propName = propertyDescriptor[i].getName(); + // skip it if its not in the list + if (!beanAttributeNames.contains(propName)) continue; + if (propName.equals("class")) continue; + propName = format(propName, elementPropertyFormat); + + Method readMethod = propertyDescriptor[i].getReadMethod(); + if (readMethod != null && + readMethod.getParameterTypes().length == 0) { + // add to our attributes + Object propValue = propertyDescriptor[i]. + getReadMethod().invoke(value,noArgs); + // NOTE: we will always set the attribute here to something, + // which we may not want (i.e. if null, omit it) + String propString = propValue != null ? propValue.toString() : ""; + attrs.addAttribute("", propName, propName, "CDATA", propString); + } + } + } catch (Exception e) { + // no attributes + return attrs; + } + + return attrs; + } } 1.4 +96 -12 xml-axis/java/src/org/apache/axis/encoding/ser/BeanDeserializer.java Index: BeanDeserializer.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/encoding/ser/BeanDeserializer.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- BeanDeserializer.java 19 Feb 2002 17:38:20 -0000 1.3 +++ BeanDeserializer.java 19 Feb 2002 20:50:46 -0000 1.4 @@ -71,6 +71,8 @@ import org.apache.axis.encoding.DeserializerImpl; import org.apache.axis.encoding.TypeMapping; import org.apache.axis.utils.JavaUtils; +import org.apache.axis.Constants; + import java.beans.IntrospectionException; import java.lang.reflect.Method; @@ -81,12 +83,15 @@ import java.beans.PropertyDescriptor; import java.io.ObjectStreamField; import java.io.Serializable; +import java.util.Vector; +import java.util.HashMap; /** * General purpose deserializer for an arbitrary java bean. * * @author Sam Ruby <[EMAIL PROTECTED]> * @author Rich Scheuerle <[EMAIL PROTECTED]> + * @author Tom Jordahl <[EMAIL PROTECTED]> */ public class BeanDeserializer extends DeserializerImpl implements Deserializer, Serializable { static Log log = @@ -95,6 +100,7 @@ QName xmlType; Class javaType; private BeanPropertyDescriptor[] pd = null; + private HashMap propertyMap = new HashMap(); // This counter is updated to deal with deserialize collection properties protected int collectionIndex = -1; @@ -103,9 +109,20 @@ public BeanDeserializer(Class javaType, QName xmlType) { this.xmlType = xmlType; this.javaType = javaType; + // Get a list of the bean properties + this.pd = BeanSerializer.getPd(javaType); + // loop through properties and grab the names for later + for (int i = 0; i < pd.length; i++) { + BeanPropertyDescriptor descriptor = pd[i]; + propertyMap.put(descriptor.getName(), descriptor); + } + // create a value try { value=javaType.newInstance(); - } catch (Exception e) {} + } catch (Exception e) { + //throw new SAXException(e.toString()); + } + } /** @@ -127,17 +144,6 @@ DeserializationContext context) throws SAXException { - // Get a list of the bean properties - BeanPropertyDescriptor[] pd = BeanSerializer.getPd(javaType); - - // create a value if there isn't one already... - if (value==null) { - try { - value=javaType.newInstance(); - } catch (Exception e) { - throw new SAXException(e.toString()); - } - } // look for a field by this name. Assumes the the number of // properties in a bean is (relatively) small, so uses a linear @@ -185,4 +191,82 @@ throw new SAXException( JavaUtils.getMessage("badElem00", javaType.getName(), localName)); } + + /** + * 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 qName is the prefixed qName of the element + * @param attributes are the attributes on the element...used to get the type + * @param context is the DeserializationContext + */ + public void onStartElement(String namespace, String localName, + String qName, Attributes attributes, + DeserializationContext context) + throws SAXException { + + // No attributes are allowed for SOAP encoding + if (Constants.isSOAP_ENC(context.getMessageContext().getEncodingStyle())) { + return; + } + // get list of properties that are really attributes + Vector beanAttributeNames = BeanSerializer.getBeanAttributes(javaType); + + // loop through the attributes and set bean properties that + // correspond to attributes + for (int i=0; i < attributes.getLength(); i++) { + String attrName = attributes.getLocalName(i); + String attrNameUp = BeanSerializer.format(attrName, BeanSerializer.FORCE_UPPER); + String attrNameLo = BeanSerializer.format(attrName, BeanSerializer.FORCE_LOWER); + String mangledName = JavaUtils.xmlNameToJava(attrName); + + // look for the attribute property + BeanPropertyDescriptor bpd = + (BeanPropertyDescriptor) propertyMap.get(attrNameUp); + if (bpd == null) + bpd = (BeanPropertyDescriptor) propertyMap.get(attrNameLo); + if (bpd == null) + bpd = (BeanPropertyDescriptor) propertyMap.get(mangledName); + if (bpd != null) { + if (bpd.getWriteMethod() == null ) continue ; + + // determine the QName for this child element + TypeMapping tm = context.getTypeMapping(); + Class type = bpd.getType(); + QName qn = tm.getTypeQName(type); + if (qn == null) + throw new SAXException( + JavaUtils.getMessage("unregistered00", type.toString())); + + // get the deserializer + Deserializer dSer = context.getDeserializerForType(qn); + if (dSer == null) + throw new SAXException( + JavaUtils.getMessage("noDeser00", type.toString())); + if (! (dSer instanceof SimpleDeserializer)) + throw new SAXException( + JavaUtils.getMessage("AttrNotSimpleType00", + bpd.getName(), + type.toString())); + + if (bpd.getWriteMethod().getParameterTypes().length == 1) { + // Success! Create an object from the string and set + // it in the bean + try { + Object val = ((SimpleDeserializer)dSer). + makeValue(attributes.getValue(i)); + bpd.getWriteMethod().invoke(value, new Object[] {val} ); + } catch (Exception e) { + throw new SAXException(e); + } + } + + } // if + } // attribute loop + + } // onStartElement + }