+1
Good stuff Rich! -- Tom Jordahl Macromedia -----Original Message----- From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED]] Sent: Friday, April 19, 2002 8:22 PM To: [EMAIL PROTECTED] Subject: cvs commit: xml-axis/java/test/encoding AttributeBean.java TestAttributes.java scheu 02/04/19 17:22:25 Modified: java/src/org/apache/axis/description TypeDesc.java java/src/org/apache/axis/encoding/ser BeanDeserializer.java BeanPropertyTarget.java BeanSerializer.java SimpleDeserializer.java SimpleSerializer.java java/src/org/apache/axis/utils BeanPropertyDescriptor.java BeanUtils.java java/test/encoding AttributeBean.java TestAttributes.java Log: There has been some discussion and bugs related to the serialization and deserialization of beans that do not have getter/setters but instead have public fields. This change fixes the following bug: http://nagoya.apache.org/bugzilla/show_bug.cgi?id=7869 Changes to utils.BeanPropertyDescriptor: * Add constructor to set BPD with a java.lang.reflect.Field. So now a BPD can be constructed with a Field or it can be constructed with a read/write Method pair. * Get rid of the public getReadMethod/getWriteMethod methods. * Instead have set/get methods which will use the methods if available or use the field. * Have isReadable, isWriteable, isIndexed methods to query the BPD. Changes to utils.BeanUtils * Add the BPD's for public fields that do not have getters/setters. Changes to encoding/ser/BeanPropertyTarget * Change the getWriteMethod calls to use the new BPD.set calls ...simplifies this code. Changes to encoding/ser/BeanSerializer & BeanDeserializer etc. * Similar changes to remove getWriteMethod and getReadMethod calls and use the set/get and other query methods. This also simplifies the code. Also changed the test/encoding/AttributeBean to have an element which does not have getters/setters. This tests serialization and deserialization. Revision Changes Path 1.11 +4 -2 xml-axis/java/src/org/apache/axis/description/TypeDesc.java Index: TypeDesc.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/description/TypeDesc.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- TypeDesc.java 16 Apr 2002 20:26:32 -0000 1.10 +++ TypeDesc.java 20 Apr 2002 00:22:24 -0000 1.11 @@ -69,7 +69,9 @@ * @author Glen Daniels ([EMAIL PROTECTED]) */ public class TypeDesc { - public static Class [] noClasses = new Class [] {}; + public static final Class [] noClasses = new Class [] {}; + public static final Object[] noObjects = new Object[] {}; + public TypeDesc(Class javaClass) { this.javaClass = javaClass; @@ -105,7 +107,7 @@ } if (getTypeDesc != null) { return (TypeDesc)getTypeDesc.invoke(null, - BeanSerializer.noArgs); + noObjects); } } catch (Exception e) { } 1.20 +23 -21 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.19 retrieving revision 1.20 diff -u -r1.19 -r1.20 --- BeanDeserializer.java 16 Apr 2002 18:56:16 -0000 1.19 +++ BeanDeserializer.java 20 Apr 2002 00:22:24 -0000 1.20 @@ -229,16 +229,20 @@ dSer.setDefaultType(tm.getTypeQName(type)); } - if (propDesc.getWriteMethod().getParameterTypes().length == 1) { - // Success! Register the target and deserializer. - collectionIndex = -1; - dSer.registerValueTarget(new BeanPropertyTarget(value, propDesc)); - } else { - // Success! This is a collection of properties so use the index - collectionIndex++; - dSer.registerValueTarget(new BeanPropertyTarget(value, - propDesc, - collectionIndex)); + if (propDesc.isWriteable()) { + if (!propDesc.isIndexed()) { + // Success! Register the target and deserializer. + collectionIndex = -1; + dSer.registerValueTarget( + new BeanPropertyTarget(value, propDesc)); + } else { + // Success! This is a collection of properties so use the index + collectionIndex++; + dSer.registerValueTarget( + new BeanPropertyTarget(value, + propDesc, + collectionIndex)); + } } return (SOAPHandler)dSer; } @@ -277,7 +281,7 @@ BeanPropertyDescriptor bpd = (BeanPropertyDescriptor) propertyMap.get(fieldName); if (bpd != null) { - if (bpd.getWriteMethod() == null ) continue ; + if (!bpd.isWriteable() || bpd.isIndexed() ) continue ; // determine the QName for this child element TypeMapping tm = context.getTypeMapping(); @@ -298,16 +302,14 @@ 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); - } + // Success! Create an object from the string and set + // it in the bean + try { + Object val = ((SimpleDeserializer)dSer). + makeValue(attributes.getValue(i)); + bpd.set(value, val); + } catch (Exception e) { + throw new SAXException(e); } } // if 1.6 +5 -5 xml-axis/java/src/org/apache/axis/encoding/ser/BeanPropertyTarget.java Index: BeanPropertyTarget.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/encoding/ser/BeanPropertyTarget.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- BeanPropertyTarget.java 16 Apr 2002 18:56:16 -0000 1.5 +++ BeanPropertyTarget.java 20 Apr 2002 00:22:24 -0000 1.6 @@ -99,17 +99,17 @@ public void set(Object value) throws SAXException { try { if (index < 0) - pd.getWriteMethod().invoke(object, new Object[] {value}); + pd.set(object, value); else - pd.getWriteMethod().invoke(object, new Object[] {new Integer(index), value}); + pd.set(object, index, value); } catch (Exception e) { - Class type = pd.getReadMethod().getReturnType(); + Class type = pd.getType(); value = JavaUtils.convert(value, type); try { if (index < 0) - pd.getWriteMethod().invoke(object, new Object[] {value}); + pd.set(object, value); else - pd.getWriteMethod().invoke(object, new Object[] {new Integer(index), value}); + pd.set(object, index, value); } catch (Exception ex) { String field= pd.getName(); int i = 0; 1.28 +12 -15 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.27 retrieving revision 1.28 diff -u -r1.27 -r1.28 --- BeanSerializer.java 16 Apr 2002 18:56:16 -0000 1.27 +++ BeanSerializer.java 20 Apr 2002 00:22:24 -0000 1.28 @@ -97,8 +97,6 @@ protected static Log log = LogFactory.getLog(BeanSerializer.class.getName()); - public static final Object[] noArgs = new Object[] {}; // For convenience - QName xmlType; Class javaType; @@ -169,15 +167,16 @@ qname = new QName("", propName); } - Method readMethod = propertyDescriptor[i].getReadMethod(); - // if there is a read method for a property - if(readMethod != null) { - Class baseJavaType = readMethod.getReturnType(); + // Read the value from the property + if(propertyDescriptor[i].isReadable()) { + Class baseJavaType = propertyDescriptor[i].getType(); Class javaType; - if (readMethod.getParameterTypes().length == 0) { + if (!propertyDescriptor[i].isIndexed()) { // Normal case: serialize the value - Object propValue = readMethod.invoke(value,noArgs); - javaType = (propValue == null || baseJavaType.isPrimitive()) + Object propValue = + propertyDescriptor[i].get(value); + javaType = (propValue == null || + baseJavaType.isPrimitive()) ? baseJavaType : propValue.getClass(); context.serialize(qname, null, @@ -189,8 +188,7 @@ Object propValue = null; try { propValue = - readMethod.invoke(value, - new Object[] { new Integer(j) }); + propertyDescriptor[i].get(value, j); j++; } catch (Exception e) { j = -1; @@ -408,11 +406,10 @@ qname = new QName("", propName); } - Method readMethod = propertyDescriptor[i].getReadMethod(); - if (readMethod != null && - readMethod.getParameterTypes().length == 0) { + if (propertyDescriptor[i].isReadable() && + !propertyDescriptor[i].isIndexed()) { // add to our attributes - Object propValue = readMethod.invoke(value,noArgs); + Object propValue = propertyDescriptor[i].get(value); // If the property value does not exist, don't serialize // the attribute. In the future, the decision to serializer // the attribute may be more sophisticated. For example, don't 1.12 +14 -19 xml-axis/java/src/org/apache/axis/encoding/ser/SimpleDeserializer.java Index: SimpleDeserializer.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/encoding/ser/SimpleDeserializer.java,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- SimpleDeserializer.java 16 Apr 2002 18:56:16 -0000 1.11 +++ SimpleDeserializer.java 20 Apr 2002 00:22:24 -0000 1.12 @@ -280,7 +280,7 @@ BeanPropertyDescriptor bpd = (BeanPropertyDescriptor) propertyMap.get(fieldName); if (bpd != null) { - if (bpd.getWriteMethod() == null ) continue ; + if (!bpd.isWriteable() || bpd.isIndexed() ) continue ; // determine the QName for this child element TypeMapping tm = context.getTypeMapping(); @@ -301,21 +301,18 @@ bpd.getName(), type.toString())); - if (bpd.getWriteMethod().getParameterTypes().length == 1) { - // Success! Create an object from the string and save - // it in our attribute map for later. - if (attributeMap == null) { - attributeMap = new HashMap(); - } - try { - Object val = ((SimpleDeserializer)dSer). - makeValue(attributes.getValue(i)); - attributeMap.put(fieldName, val); - } catch (Exception e) { - throw new SAXException(e); - } + // Success! Create an object from the string and save + // it in our attribute map for later. + if (attributeMap == null) { + attributeMap = new HashMap(); + } + try { + Object val = ((SimpleDeserializer)dSer). + makeValue(attributes.getValue(i)); + attributeMap.put(fieldName, val); + } catch (Exception e) { + throw new SAXException(e); } - } // if } // attribute loop } // onStartElement @@ -338,11 +335,9 @@ BeanPropertyDescriptor bpd = (BeanPropertyDescriptor) propertyMap.get(name); - if (bpd.getWriteMethod() == null) continue; + if (!bpd.isWriteable() || bpd.isIndexed()) continue; try { - if (bpd.getWriteMethod().getParameterTypes().length == 1) { - bpd.getWriteMethod().invoke(value, new Object[] {val} ); - } + bpd.set(value, val ); } catch (Exception e) { throw new SAXException(e); } 1.12 +3 -5 xml-axis/java/src/org/apache/axis/encoding/ser/SimpleSerializer.java Index: SimpleSerializer.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/encoding/ser/SimpleSerializer.java,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- SimpleSerializer.java 18 Apr 2002 19:02:31 -0000 1.11 +++ SimpleSerializer.java 20 Apr 2002 00:22:24 -0000 1.12 @@ -193,12 +193,10 @@ qname = new QName("", propName); } - Method readMethod = propertyDescriptor[i].getReadMethod(); - if (readMethod != null && - readMethod.getParameterTypes().length == 0) { + if (propertyDescriptor[i].isReadable() && + !propertyDescriptor[i].isIndexed()) { // add to our attributes - Object propValue = readMethod. - invoke(value,BeanSerializer.noArgs); + Object propValue = propertyDescriptor[i].get(value); // If the property value does not exist, don't serialize // the attribute. In the future, the decision to serializer // the attribute may be more sophisticated. For example, don't 1.2 +150 -9 xml-axis/java/src/org/apache/axis/utils/BeanPropertyDescriptor.java Index: BeanPropertyDescriptor.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/utils/BeanPropertyDescriptor.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- BeanPropertyDescriptor.java 16 Apr 2002 18:56:16 -0000 1.1 +++ BeanPropertyDescriptor.java 20 Apr 2002 00:22:25 -0000 1.2 @@ -64,10 +64,14 @@ import org.apache.commons.logging.LogFactory; import java.beans.Introspector; import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; /** * This class is essentially a copy of the PropertyDescriptor information, except * that the values in it can be modified. + * Updated this to include fields that don't have getter/setters. * @author Rich Scheuerle <[EMAIL PROTECTED]> **/ public class BeanPropertyDescriptor @@ -75,19 +79,156 @@ protected static Log log = LogFactory.getLog(BeanPropertyDescriptor.class.getName()); - private String name; - private Method getter; - private Method setter; - - public BeanPropertyDescriptor(String _name, Method _getter, Method _setter) { + private String name = null; + private Method getter = null; + private Method setter = null; + private Field field = null; + private static final Object[] noArgs = new Object[] {}; + + /** + * Construct a BPD with getter/setter methods + * Both must be set + */ + public BeanPropertyDescriptor(String _name, + Method _getter, + Method _setter) { name = _name; getter = _getter; setter = _setter; + if (_getter == null || _setter == null || _name == null) { + throw new IllegalArgumentException(); + } + } + + /** + * Construct a BPD with a field + * Both must be set + */ + public BeanPropertyDescriptor(String _name, + Field _field) { + name = _name; + field = _field; + if (_field == null || _name == null) { + throw new IllegalArgumentException(); + } + } + + /** + * Query if property is readable + */ + public boolean isReadable() { + return (getter != null || + field != null); + } + /** + * Query if property is writeable + */ + public boolean isWriteable() { + return (setter != null || + field != null); + } + /** + * Query if property is indexed. + * Indexed properties require valid setters/getters + */ + public boolean isIndexed() { + return (getter != null && + getter.getParameterTypes().length == 1 && + setter != null && + setter.getParameterTypes().length == 2); } - public Method getReadMethod() { return getter; } - public Method getWriteMethod() { return setter; } + + /** + * Get the property value + */ + public Object get(Object obj) + throws InvocationTargetException, IllegalAccessException { + if (isIndexed()) { + throw new IllegalArgumentException(); + } + if (getter != null) { + return getter.invoke(obj, noArgs); + } else if (field != null) { + return field.get(obj); + } + return null; + } + /** + * Set the property value + */ + public void set(Object obj, Object newValue) + throws InvocationTargetException, IllegalAccessException { + if (isIndexed()) { + throw new IllegalArgumentException(); + } + if (setter != null) { + setter.invoke(obj, new Object[] {newValue}); + } else if (field != null) { + field.set(obj, newValue); + } + } + /** + * Get an indexed property + */ + public Object get(Object obj, int i) + throws InvocationTargetException, IllegalAccessException { + if (!isIndexed()) { + throw new IllegalArgumentException(); + } + if (getter != null) { + return getter.invoke(obj, new Object[] { new Integer(i)}); + } else if (field != null) { + return Array.get(field.get(obj), i); + } + return null; + } + /** + * Get an indexed property value + */ + public void set(Object obj, int i, Object newValue) + throws InvocationTargetException, IllegalAccessException { + if (!isIndexed()) { + throw new IllegalArgumentException(); + } + if (setter != null) { + setter.invoke(obj, new Object[] {new Integer(i), newValue}); + } else if (field != null) { + Array.set(field.get(obj),i, newValue); + } + } + + /** + * Get the name of a property + */ public String getName() {return name;} - public Class getType() {return getter.getReturnType(); } -} + /** + * Get the type of a property + */ + public Class getType() { + if (getter != null) { + return getter.getReturnType(); + } else { + return field.getType(); + } + } + /** + * Get the read Method. + * (This is package visibility so that Bean Utils + * can access this information. The other methods + * should be used during serialization/deserialization.) + */ + Method getReadMethod() { + return getter; + } + /** + * Get the write Method. + * (This is package visibility so that Bean Utils + * can access this information. The other methods + * should be used during serialization/deserialization.) + */ + Method getWriteMethod() { + return setter; + } +} 1.2 +49 -6 xml-axis/java/src/org/apache/axis/utils/BeanUtils.java Index: BeanUtils.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/utils/BeanUtils.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- BeanUtils.java 16 Apr 2002 18:56:16 -0000 1.1 +++ BeanUtils.java 20 Apr 2002 00:22:25 -0000 1.2 @@ -62,6 +62,8 @@ import java.beans.Introspector; import java.util.Vector; import java.lang.reflect.Method; +import java.lang.reflect.Field; +import java.util.ArrayList; public class BeanUtils { @@ -140,10 +142,14 @@ PropertyDescriptor[] rawPd, Class cls) { BeanPropertyDescriptor[] myPd = new BeanPropertyDescriptor[rawPd.length]; + int index1 = 0; for (int i=0; i < rawPd.length; i++) { - myPd[i] = new BeanPropertyDescriptor(rawPd[i].getName(), - rawPd[i].getReadMethod(), - rawPd[i].getWriteMethod()); + if (rawPd[i].getReadMethod() != null && + rawPd[i].getWriteMethod() != null) { + myPd[index1++] = new BeanPropertyDescriptor(rawPd[i].getName(), + rawPd[i].getReadMethod(), + rawPd[i].getWriteMethod()); + } } try { @@ -151,7 +157,7 @@ int index = 0; // Build a new pd array - // defined by the order of the get methods. + // defined by the order of the set methods. BeanPropertyDescriptor[] newPd = new BeanPropertyDescriptor[rawPd.length]; Method[] methods = cls.getMethods(); for (int i=0; i < methods.length; i++) { @@ -159,7 +165,8 @@ if (method.getName().startsWith("set")) { boolean found = false; for (int j=0; j < myPd.length && !found; j++) { - if (myPd[j].getWriteMethod() != null && + if (myPd[j] != null && + myPd[j].getWriteMethod() != null && myPd[j].getWriteMethod().equals(method)) { found = true; newPd[index] = myPd[j]; @@ -199,7 +206,8 @@ methods[j].getParameterTypes()[0] == int.class && methods[i].getParameterTypes()[0] == int.class) { for (int k=0; k < myPd.length; k++) { - if (myPd[k].getReadMethod() != null && + if (myPd[k] != null && + myPd[k].getReadMethod() != null && myPd[k].getWriteMethod() != null && myPd[k].getReadMethod().getName().equals(methods[j].getName()) && myPd[k].getWriteMethod().getName().equals(methods[i].getName())) { @@ -212,6 +220,41 @@ } } } + ArrayList pd = new ArrayList(); + for (int i=0; i < myPd.length; i++) { + if (myPd[i] != null) { + pd.add(myPd[i]); + } + } + // Now look for public fields + Field fields[] = cls.getFields(); + if (fields != null && fields.length > 0) { + // See if the field is in the list of properties + // add it if not. + for (int i=0; i < fields.length; i++) { + Field f = fields[i]; + String fName = f.getName(); + boolean found = false; + for (int j=0; j<pd.size() && !found; j++) { + String pName = + ((BeanPropertyDescriptor)pd.get(i)).getName(); + if (pName.length() == fName.length() && + pName.substring(0,1).equalsIgnoreCase( + fName.substring(0,1))) { + found = pName.length() == 1 || + pName.substring(1).equals(fName.substring(1)); + } + } + if (!found) { + pd.add(new BeanPropertyDescriptor(f.getName(), f)); + } + } + } + myPd = new BeanPropertyDescriptor[pd.size()]; + for (int i=0; i <pd.size(); i++) { + myPd[i] = (BeanPropertyDescriptor) pd.get(i); + } + } catch (Exception e) { // Don't process Property Descriptors if problems occur return myPd; 1.5 +11 -4 xml-axis/java/test/encoding/AttributeBean.java Index: AttributeBean.java =================================================================== RCS file: /home/cvs/xml-axis/java/test/encoding/AttributeBean.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- AttributeBean.java 3 Apr 2002 06:09:55 -0000 1.4 +++ AttributeBean.java 20 Apr 2002 00:22:25 -0000 1.5 @@ -68,17 +68,18 @@ public class AttributeBean extends ParentBean { private int age; private float iD; + public String company; // element without getter/setter private java.lang.String name; // attribute private boolean male; // attribute - public AttributeBean() { - } + public AttributeBean() {} - public AttributeBean(int age, float iD, java.lang.String name, boolean male) { + public AttributeBean(int age, float iD, String name, String company, boolean male) { this.age = age; this.iD = iD; this.name = name; this.male = male; + this.company = company; } public int getAge() { @@ -128,8 +129,14 @@ if (other.getName() != null) { return false; } + }else if (!name.equals(other.getName())) { + return false; } - if (!name.equals(other.getName())) { + if (company == null) { + if (company != null) { + return false; + } + } else if (!company.equals(other.company)) { return false; } if (getParentFloat() != other.getParentFloat()) 1.5 +1 -1 xml-axis/java/test/encoding/TestAttributes.java Index: TestAttributes.java =================================================================== RCS file: /home/cvs/xml-axis/java/test/encoding/TestAttributes.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- TestAttributes.java 3 Apr 2002 06:09:55 -0000 1.4 +++ TestAttributes.java 20 Apr 2002 00:22:25 -0000 1.5 @@ -69,6 +69,7 @@ bean.setAge(35); bean.setID(1.15F); bean.setMale(true); + bean.company = "Majesty's Secret Service"; bean.setName("James Bond"); RPCParam arg = new RPCParam("", "struct", bean); @@ -115,7 +116,6 @@ Object obj = ((RPCParam)params.get(0)).getValue(); assertTrue("Deserialized param not an AttributeBean!", (obj instanceof AttributeBean)); - AttributeBean deserBean = (AttributeBean)obj; assertTrue("Deserialized bean not equal to expected values!", (bean.equals(deserBean)));