Index: src/main/org/codehaus/xfire/aegis/type/basic/BeanType.java =================================================================== --- src/main/org/codehaus/xfire/aegis/type/basic/BeanType.java (revision 1455) +++ src/main/org/codehaus/xfire/aegis/type/basic/BeanType.java (working copy) @@ -1,10 +1,7 @@ package org.codehaus.xfire.aegis.type.basic; import java.beans.PropertyDescriptor; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; +import java.lang.reflect.*; import java.util.HashSet; import java.util.Iterator; import java.util.Set; @@ -25,7 +22,7 @@ /** * Serializes JavaBeans. - * + * * @author Dan Diephouse * @author Jack Hong */ @@ -35,17 +32,17 @@ private BeanTypeInfo _info; private boolean isInterface = false; private boolean isException = false; - + public BeanType() { } - + public BeanType(BeanTypeInfo info) { - this._info = info; + this._info = info; this.setTypeClass(info.getTypeClass()); } - + /* (non-Javadoc) * @see org.codehaus.xfire.aegis.type.Type#readObject(org.codehaus.xfire.aegis.MessageReader, org.codehaus.xfire.MessageContext) */ @@ -53,14 +50,14 @@ throws XFireFault { BeanTypeInfo info = getTypeInfo(); - + try { Class clazz = getTypeClass(); Object object = null; InterfaceInvocationHandler delegate = null; boolean isProxy = false; - + if (isInterface) { String impl = null; @@ -68,7 +65,7 @@ { impl = (String) context.getService().getProperty(clazz.getName() + ".implementation"); } - + if (impl == null) { delegate = new InterfaceInvocationHandler(); @@ -86,7 +83,7 @@ } catch (ClassNotFoundException e) { - throw new XFireRuntimeException("Could not find implementation class " + + throw new XFireRuntimeException("Could not find implementation class " + impl + " for class " + clazz.getName()); } } @@ -105,18 +102,18 @@ { MessageReader childReader = reader.getNextAttributeReader(); QName name = childReader.getName(); - + Type type = info.getType(name); if (type != null) { Object writeObj = type.readObject(childReader, context); - if (isProxy) + if (isProxy) { delegate.writeProperty(name.getLocalPart(), writeObj); } - else + else { writeProperty(name, object, writeObj, clazz); } @@ -137,11 +134,11 @@ { Object writeObj = type.readObject(childReader, context); - if (isProxy) + if (isProxy) { delegate.writeProperty(name.getLocalPart(), writeObj); } - else + else { writeProperty(name, object, writeObj, clazz); } @@ -162,7 +159,7 @@ childReader.readToEnd(); } } - + return object; } catch (IllegalAccessException e) @@ -191,7 +188,7 @@ * If the class is an exception, this will try and instantiate it with information * from the XFireFault (if it exists). */ - protected Object createFromFault(MessageContext context) + protected Object createFromFault(MessageContext context) throws SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Class clazz = getTypeClass(); @@ -199,9 +196,9 @@ Object o; Object body = context.getExchange().getFaultMessage().getBody(); - if (!(body instanceof XFireFault)) + if (!(body instanceof XFireFault)) return clazz.newInstance(); - + XFireFault fault = (XFireFault) body; try @@ -242,14 +239,14 @@ try { PropertyDescriptor desc = getTypeInfo().getPropertyDescriptorFromMappedName(name); - + Method m = desc.getWriteMethod(); - - if (m == null) + + if (m == null) { if (getTypeClass().isInterface()) m = getWriteMethodFromImplClass(impl, desc); - + if (m == null) throw new XFireFault("No write method for property " + name + " in " + object.getClass(), XFireFault.SENDER); } @@ -263,7 +260,7 @@ catch (Exception e) { if (e instanceof XFireFault) throw (XFireFault) e; - + throw new XFireFault("Couldn't set property " + name + " on " + object + ". " + e.getMessage(), e, XFireFault.SENDER); } } @@ -277,21 +274,21 @@ { String name = pd.getName(); name = "set" + name.substring(0, 1).toUpperCase() + name.substring(1); - + return impl.getMethod(name, new Class[] { pd.getPropertyType() }); } /** - * @see org.codehaus.xfire.aegis.type.Type#writeObject(Object, org.codehaus.xfire.aegis.MessageWriter, org.codehaus.xfire.MessageContext) + * @see org.codehaus.xfire.aegis.type.Type#writeObject(Object, org.codehaus.xfire.aegis.MessageWriter, org.codehaus.xfire.MessageContext) */ public void writeObject(Object object, MessageWriter writer, MessageContext context) throws XFireFault { if (object == null) return; - + BeanTypeInfo info = getTypeInfo(); - + for (Iterator itr = info.getAttributes(); itr.hasNext(); ) { QName name = (QName) itr.next(); @@ -300,24 +297,24 @@ if (value != null) { Type type = getType( info, name ); - + if ( type == null ) throw new XFireRuntimeException( "Couldn't find type for " + value.getClass() + " for property " + name ); - + MessageWriter cwriter = writer.getAttributeWriter(name); type.writeObject(value, cwriter, context); - + cwriter.close(); } } - + for (Iterator itr = info.getElements(); itr.hasNext(); ) { QName name = (QName) itr.next(); Object value = readProperty(object, name); - + Type type = getType( info, name ); MessageWriter cwriter; @@ -325,27 +322,27 @@ if ( value != null) { cwriter = getWriter(writer, name, type); - + if ( type == null ) throw new XFireRuntimeException( "Couldn't find type for " + value.getClass() + " for property " + name ); type.writeObject(value, cwriter, context); - + cwriter.close(); } else if (info.isNillable(name)) { cwriter = getWriter(writer, name, type); - + // Write the xsi:nil if it is null. cwriter.writeXsiNil(); - + cwriter.close(); } } } - private MessageWriter getWriter(MessageWriter writer, QName name, Type type) + private MessageWriter getWriter(MessageWriter writer, QName name, Type type) { MessageWriter cwriter; if (type.isAbstract()) @@ -366,7 +363,7 @@ PropertyDescriptor desc = getTypeInfo().getPropertyDescriptorFromMappedName(name); Method m = desc.getReadMethod(); - + if (m == null) throw new XFireFault("No read method for property " + name + " in class " + object.getClass().getName(), XFireFault.SENDER); return m.invoke(object, new Object[0]); @@ -378,47 +375,82 @@ } /** - * @see org.codehaus.xfire.aegis.type.Type#writeSchema(org.jdom.Element) + * @see org.codehaus.xfire.aegis.type.Type#writeSchema(org.jdom.Element) */ public void writeSchema(Element root) { BeanTypeInfo info = getTypeInfo(); - + Element complex = new Element("complexType", SoapConstants.XSD_PREFIX, SoapConstants.XSD); complex.setAttribute(new Attribute("name", getSchemaType().getLocalPart())); + + // Check for abstract classes + if(Modifier.isAbstract(info.getTypeClass().getModifiers())) + { + complex.setAttribute(new Attribute("abstract", "true")); + } + root.addContent(complex); + Type baseType = info.getBaseType(); + if(baseType != null) + { + Element content = new Element("complexContent", SoapConstants.XSD_PREFIX, SoapConstants.XSD); + complex.addContent(content); + Element extension = new Element("extension", SoapConstants.XSD_PREFIX, SoapConstants.XSD); + + QName baseSchemaType = baseType.getSchemaType(); + String baseName = baseSchemaType.getLocalPart(); + if(!baseSchemaType.getNamespaceURI().equals(getSchemaType().getNamespaceURI())) + { + String prefix = NamespaceHelper.getUniquePrefix((Element)root.getParent(), baseSchemaType.getNamespaceURI()); + baseName = prefix + ":" + baseName; + } + extension.setAttribute(new Attribute("base", baseName)); + content.addContent(extension); + + // Shift complex + complex = extension; + } + Element seq = null; - + // Write out schema for elements for (Iterator itr = info.getElements(); itr.hasNext();) { + QName name = (QName)itr.next(); + + if(_info.isInherited(name)) + { + continue; + } + if (seq == null) { seq = new Element("sequence", SoapConstants.XSD_PREFIX, SoapConstants.XSD); complex.addContent(seq); } - - QName name = (QName) itr.next(); - + + + Element element = new Element("element", SoapConstants.XSD_PREFIX, SoapConstants.XSD); seq.addContent(element); - + Type type = getType(info, name); - - String nameNS = name.getNamespaceURI(); + + String nameNS = name.getNamespaceURI(); String nameWithPrefix = getNameWithPrefix(root, nameNS, name.getLocalPart()); - - String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), + + String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), type.getSchemaType().getNamespaceURI() ); - + writeTypeReference(name, nameWithPrefix, element, type, prefix); } - + /** * if future proof then add element */ @@ -429,30 +461,33 @@ seq = new Element("sequence", SoapConstants.XSD_PREFIX, SoapConstants.XSD); complex.addContent(seq); } - seq.addContent( createAnyElement () ); + seq.addContent( createAnyElement () ); } - + // Write out schema for attributes for (Iterator itr = info.getAttributes(); itr.hasNext();) { QName name = (QName) itr.next(); - - Element element = new Element("attribute", - SoapConstants.XSD_PREFIX, - SoapConstants.XSD); + + if(_info.isInherited(name)) + { + continue; + } + + Element element = new Element("attribute", SoapConstants.XSD_PREFIX, SoapConstants.XSD); complex.addContent(element); - + Type type = getType(info, name); - String nameNS = name.getNamespaceURI(); + String nameNS = name.getNamespaceURI(); String nameWithPrefix = getNameWithPrefix(root, nameNS, name.getLocalPart()); - String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), + String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), type.getSchemaType().getNamespaceURI() ); element.setAttribute(new Attribute("name", nameWithPrefix)); element.setAttribute(new Attribute("type", prefix + ':' + type.getSchemaType().getLocalPart())); } - + /** * If extensible attributes then add */ @@ -467,7 +502,7 @@ if (!nameNS.equals(getSchemaType().getNamespaceURI())) { String prefix = NamespaceHelper.getUniquePrefix((Element) root.getParent(), nameNS); - if (prefix != null && prefix.length() > 0) + if (prefix != null && prefix.length() > 0) return prefix + ":" + localName; } return localName; @@ -476,13 +511,13 @@ private Type getType(BeanTypeInfo info, QName name) { Type type = info.getType(name); - + if (type == null) { - throw new NullPointerException("Couldn't find type for" + name + " in class " + + throw new NullPointerException("Couldn't find type for" + name + " in class " + getTypeClass().getName()); } - + return type; } @@ -492,13 +527,13 @@ { element.setAttribute(new Attribute("name", nameWithPrefix)); element.setAttribute(new Attribute("type", prefix + ':' + type.getSchemaType().getLocalPart())); - + int minOccurs = getTypeInfo().getMinOccurs(name); if ( minOccurs != 1 ) { element.setAttribute(new Attribute("minOccurs", new Integer(minOccurs).toString())); } - + if (getTypeInfo().isNillable(name)) { element.setAttribute(new Attribute("nillable", "true")); @@ -509,18 +544,18 @@ element.setAttribute(new Attribute("ref", prefix + ':' + type.getSchemaType().getLocalPart())); } } - + public void setTypeClass(Class typeClass) { super.setTypeClass(typeClass); - + isInterface = typeClass.isInterface(); isException = Exception.class.isAssignableFrom(typeClass); } /** * We need to write a complex type schema for Beans, so return true. - * + * * @see org.codehaus.xfire.aegis.type.Type#isComplex() */ public boolean isComplex() @@ -533,7 +568,13 @@ Set deps = new HashSet(); BeanTypeInfo info = getTypeInfo(); - + + Type baseType = info.getBaseType(); + if(baseType != null) + { + deps.add(baseType); + } + for (Iterator itr = info.getAttributes(); itr.hasNext(); ) { QName name = (QName) itr.next(); @@ -547,7 +588,7 @@ deps.add(info.getType(name)); } - + return deps; } @@ -557,26 +598,26 @@ { _info = createTypeInfo(); } - + // Delay initialization so things work in recursive scenarios (XFIRE-117) if (!_info.isInitialized()) { _info.initialize(); } - + return _info; } public BeanTypeInfo createTypeInfo() { - BeanTypeInfo info = new BeanTypeInfo(getTypeClass(), + BeanTypeInfo info = new BeanTypeInfo(getTypeClass(), getSchemaType().getNamespaceURI()); info.setTypeMapping(getTypeMapping()); return info; } - + /** * Create an element to represent any future elements * that might get added to the schema @@ -589,10 +630,10 @@ SoapConstants.XSD_PREFIX, SoapConstants.XSD); result.setAttribute(new Attribute("minOccurs", "0")); - result.setAttribute(new Attribute("maxOccurs", "unbounded")); + result.setAttribute(new Attribute("maxOccurs", "unbounded")); return result; } - + /** * Create an element to represent any future attributes * that might get added to the schema @@ -603,8 +644,8 @@ { Element result = new Element("anyAttribute", SoapConstants.XSD_PREFIX, - SoapConstants.XSD); + SoapConstants.XSD); return result; } - + } Index: src/main/org/codehaus/xfire/aegis/type/basic/BeanTypeInfo.java =================================================================== --- src/main/org/codehaus/xfire/aegis/type/basic/BeanTypeInfo.java (revision 1455) +++ src/main/org/codehaus/xfire/aegis/type/basic/BeanTypeInfo.java (working copy) @@ -4,11 +4,7 @@ import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import javax.xml.namespace.QName; @@ -26,10 +22,18 @@ private Class beanClass; + private Class baseClass; + + private BeanInfo baseBeanInfo; + + private Type baseType; + private List attributes = new ArrayList(); private List elements = new ArrayList(); + private Set inheritedProperties = new HashSet(); + private PropertyDescriptor[] descriptors; private TypeMapping typeMapping; @@ -39,9 +43,9 @@ private String defaultNamespace; private int minOccurs = 0; - + private boolean nillable = true; - + /** * extensibleElements means adding xs:any to WSDL Complex Type Definition */ @@ -63,7 +67,7 @@ /** * Create a BeanTypeInfo class. - * + * * @param typeClass * @param defaultNamespace * @param initiallize @@ -108,10 +112,7 @@ public boolean isMapped(PropertyDescriptor pd) { - if (pd.getReadMethod() == null) - return false; - - return true; + return pd.getReadMethod() != null; } public boolean isInitialized() @@ -224,6 +225,20 @@ return true; } + public Type getBaseType() + { + if(baseType == null && baseClass != null) + { + TypeMapping tm = getTypeMapping(); + baseType = tm.getType(baseClass); + if(baseType == null) + { + baseType = tm.getTypeCreator().createType(baseClass); + } + } + return baseType; + } + public void mapType(QName name, Type type) { mappedName2type.put(name, type); @@ -247,7 +262,7 @@ /** * Specifies the name of the property as it shows up in the xml schema. This * method just returns propertyDescriptor.getName(); - * + * * @param desc * @return */ @@ -260,17 +275,51 @@ { mappedName2pdName.put(mappedName, property); attributes.add(mappedName); + mapInheritance(property, mappedName); } public void mapElement(String property, QName mappedName) { mappedName2pdName.put(mappedName, property); elements.add(mappedName); + mapInheritance(property, mappedName); } + protected void mapInheritance(String property, QName mappedName) + { + if(isInherited(property)) + { + inheritedProperties.add(mappedName); + } + } + + public boolean isInherited(QName mappedName) + { + return inheritedProperties.contains(mappedName); + } + + protected boolean isInherited(String property) + { + if(baseClass != null) + { + PropertyDescriptor[] pds = baseBeanInfo.getPropertyDescriptors(); + if(pds != null) + { + for(int i = 0; i < pds.length; i++) + { + if(pds[i].getName().equals(property)) + { + return true; + } + } + } + } + return false; + } + /** * Specifies the SchemaType for a particular class. - * + * * @param mappedName * @param type */ @@ -309,6 +358,12 @@ else { beanInfo = Introspector.getBeanInfo(beanClass, Object.class); + Class superclass = beanClass.getSuperclass(); + if(superclass != Object.class) + { + baseClass = superclass; + baseBeanInfo = Introspector.getBeanInfo(baseClass, Object.class); + } } } catch (IntrospectionException e) @@ -405,7 +460,7 @@ { this.minOccurs = minOccurs; } - + public void setDefaultNillable (boolean nillable) { this.nillable = nillable;