/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Emil Ong, Adam Megacz
*/
package com.caucho.jaxb.skeleton;
import org.w3c.dom.Node;
import static javax.xml.XMLConstants.*;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import java.io.IOException;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.caucho.jaxb.BinderImpl;
import com.caucho.jaxb.JAXBContextImpl;
import com.caucho.jaxb.JAXBUtil;
import com.caucho.jaxb.NodeIterator;
import com.caucho.jaxb.annotation.XmlLocation;
import com.caucho.jaxb.accessor.Accessor;
import com.caucho.jaxb.accessor.FieldAccessor;
import com.caucho.jaxb.accessor.GetterSetterAccessor;
import com.caucho.jaxb.mapping.AnyAttributeMapping;
import com.caucho.jaxb.mapping.AttributeMapping;
import com.caucho.jaxb.mapping.AnyElementMapping;
import com.caucho.jaxb.mapping.ElementMapping;
import com.caucho.jaxb.mapping.ElementRefMapping;
import com.caucho.jaxb.mapping.ElementsMapping;
import com.caucho.jaxb.mapping.Namer;
import com.caucho.jaxb.mapping.XmlMapping;
import com.caucho.jaxb.mapping.XmlValueMapping;
import com.caucho.util.L10N;
import com.caucho.xml.stream.StaxUtil;
public class ClassSkeleton<C> {
public static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema";
public static final String XML_SCHEMA_PREFIX = "xsd";
private static final L10N L = new L10N(ClassSkeleton.class);
private static final Logger log =
Logger.getLogger(ClassSkeleton.class.getName());
private static final Class[] NO_PARAMS = new Class[0];
private static final Object[] NO_ARGS = new Object[0];
protected JAXBContextImpl _context;
protected QName _elementName;
private Class<C> _class;
private ClassSkeleton _parent;
private Package _package;
private Method _createMethod;
private Object _factory;
private QName _typeName;
private HashMap<QName,XmlMapping> _attributeQNameToMappingMap
= new HashMap<QName,XmlMapping>();
private HashMap<QName,XmlMapping> _elementQNameToMappingMap
= new HashMap<QName,XmlMapping>();
private ArrayList<XmlMapping> _attributeMappings
= new ArrayList<XmlMapping>();
private ArrayList<XmlMapping> _elementMappings
= new ArrayList<XmlMapping>();
private AnyElementMapping _anyElementMapping;
private AnyAttributeMapping _anyAttributeMapping;
private Method _beforeUnmarshal;
private Method _afterUnmarshal;
private Method _beforeMarshal;
private Method _afterMarshal;
private Constructor _constructor;
/** Special hook to allow injecting the location where an instance was
unmarshalled from. */
private Accessor _locationAccessor;
/**
* The value @XmlValue.
*
**/
protected XmlValueMapping _value;
public Class<C> getType()
{
return _class;
}
public String toString()
{
return "ClassSkeleton[" + _class + "]";
}
protected ClassSkeleton(JAXBContextImpl context)
{
_context = context;
}
public ClassSkeleton(JAXBContextImpl context, Class<C> c)
{
this(context);
_class = c;
}
public void init()
throws JAXBException
{
try {
_package = _class.getPackage();
// check for special before and after methods
try {
_beforeUnmarshal =
_class.getMethod("beforeUnmarshal", Unmarshaller.class, Object.class);
} catch (NoSuchMethodException _) {
// deliberate
}
try {
_afterUnmarshal =
_class.getMethod("afterUnmarshal", Unmarshaller.class, Object.class);
} catch (NoSuchMethodException _) {
// deliberate
}
try {
_beforeMarshal = _class.getMethod("beforeMarshal", Marshaller.class);
} catch (NoSuchMethodException _) {
// deliberate
}
try {
_afterMarshal = _class.getMethod("afterMarshal", Marshaller.class);
} catch (NoSuchMethodException _) {
// deliberate
}
if (Set.class.isAssignableFrom(_class)) {
// XXX:
}
// XXX: @XmlJavaTypeAdapter
// Find the zero-parameter constructor
try {
_constructor = _class.getConstructor(NO_PARAMS);
_constructor.setAccessible(true);
}
catch (Exception e1) {
try {
_constructor = _class.getDeclaredConstructor(NO_PARAMS);
_constructor.setAccessible(true);
}
catch (Exception e2) {
throw new JAXBException(L.l("{0}: Zero-arg constructor not found",
_class.getName()), e2);
}
}
_typeName = JAXBUtil.getXmlSchemaDatatype(_class, _context);
// Special case: when name="", this is an "anonymous" type, bound
// exclusively to a particular element name
if (! "".equals(_typeName.getLocalPart()))
_context.addXmlType(_typeName, this);
// Check for the complete name of the element...
String namespace = _context.getTargetNamespace();
// look at package defaults first...
XmlSchema schema = (XmlSchema) _package.getAnnotation(XmlSchema.class);
if (schema != null && ! "".equals(schema.namespace()))
namespace = schema.namespace();
// then look at class specific overrides.
XmlRootElement xre = _class.getAnnotation(XmlRootElement.class);
if (xre != null) {
String localName = null;
if ("##default".equals(xre.name()))
localName = JAXBUtil.identifierToXmlName(_class);
else
localName = xre.name();
if (! "##default".equals(xre.namespace()))
namespace = xre.namespace();
if (namespace == null)
_elementName = new QName(localName);
else
_elementName = new QName(namespace, localName);
_context.addRootElement(this);
}
// order the elements, if specified
XmlAccessOrder accessOrder = XmlAccessOrder.UNDEFINED;
XmlAccessorOrder packageOrder =
_package.getAnnotation(XmlAccessorOrder.class);
XmlAccessorOrder classOrder =
_class.getAnnotation(XmlAccessorOrder.class);
if (packageOrder != null)
accessOrder = packageOrder.value();
if (classOrder != null)
accessOrder = classOrder.value();
// try property orders too
XmlType xmlType = (XmlType) _class.getAnnotation(XmlType.class);
HashMap<String,Integer> orderMap = null;
if (xmlType != null &&
(xmlType.propOrder().length != 1 ||
! "".equals(xmlType.propOrder()[0]))) {
// non-default propOrder
orderMap = new HashMap<String,Integer>();
for (int i = 0; i < xmlType.propOrder().length; i++)
orderMap.put(xmlType.propOrder()[i], i);
}
// Collect the fields/properties of the class
if (orderMap != null) {
for (int i = 0; i < orderMap.size(); i++)
_elementMappings.add(null);
}
XmlAccessorType accessorType =
_class.getAnnotation(XmlAccessorType.class);
XmlAccessType accessType = (accessorType == null ?
XmlAccessType.PUBLIC_MEMBER :
accessorType.value());
if (accessType != XmlAccessType.FIELD) {
// getter/setter
TreeSet<Method> methodSet = new TreeSet<Method>(methodComparator);
Method[] declared = _class.getDeclaredMethods();
for (Method m : declared)
methodSet.add(m);
Method[] methods = new Method[methodSet.size()];
methodSet.toArray(methods);
AccessibleObject.setAccessible(methods, true);
while (methodSet.size() > 0) {
Method m = methodSet.first();
methodSet.remove(m);
String name = null;
Method get = null;
Method set = null;
if (m.getName().startsWith("get")) {
get = m;
if (Void.TYPE.equals(get.getReturnType()))
continue;
name = get.getName().substring(3); // 3 == "get".length());
Class cl = get.getDeclaringClass();
try {
set = cl.getDeclaredMethod("set" + name, get.getReturnType());
}
catch (NoSuchMethodException e) {
continue;
}
if (! methodSet.remove(set))
continue;
}
else if (m.getName().startsWith("set")) {
set = m;
Class[] parameterTypes = set.getParameterTypes();
if (parameterTypes.length != 1)
continue;
name = set.getName().substring(3); // 3 == "set".length());
Class cl = set.getDeclaringClass();
try {
get = cl.getDeclaredMethod("get" + name);
}
catch (NoSuchMethodException e) {
continue;
}
if (! parameterTypes[0].equals(get.getReturnType()))
continue;
if (! methodSet.remove(get))
continue;
}
else
continue;
name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
// JAXB specifies that a "class" property must be specified as "clazz"
// because of Object.getClass()
if ("class".equals(name))
continue;
// XXX special cases for Throwable specified in JAX-WS
// Should it be in the general JAXB?
if (Throwable.class.isAssignableFrom(_class) &&
("stackTrace".equals(name) ||
"cause".equals(name) ||
"localizedMessage".equals(name)))
continue;
// XXX PUBLIC_MEMBER
if (accessType == XmlAccessType.NONE &&
! JAXBUtil.isJAXBAnnotated(get) &&
! JAXBUtil.isJAXBAnnotated(set))
continue;
// jaxb/0456
if (Modifier.isStatic(get.getModifiers()) &&
JAXBUtil.isJAXBAnnotated(get))
throw new JAXBException(L.l("JAXB annotations cannot be applied to static methods: {0}", get));
// jaxb/0457
if (Modifier.isStatic(set.getModifiers()) &&
JAXBUtil.isJAXBAnnotated(set))
throw new JAXBException(L.l("JAXB annotations cannot be applied to static methods: {0}", set));
// jaxb/0374
if (Modifier.isStatic(set.getModifiers()) ||
Modifier.isStatic(get.getModifiers()))
continue;
if (get != null && get.isAnnotationPresent(XmlTransient.class))
continue;
if (set != null && set.isAnnotationPresent(XmlTransient.class))
continue;
get.setAccessible(true);
set.setAccessible(true);
Accessor a = new GetterSetterAccessor(name, get, set);
if (orderMap != null) {
Integer i = orderMap.remove(name);
if (i != null)
a.setOrder(i.intValue());
// XXX else throw something?
}
processAccessor(a);
}
}
if (accessType != XmlAccessType.PROPERTY) {
// XXX Don't overwrite property accessors
HashSet<Field> fieldSet = new HashSet<Field>();
Field[] declared = _class.getDeclaredFields();
for (Field f : declared)
fieldSet.add(f);
Field[] fields = new Field[fieldSet.size()];
fieldSet.toArray(fields);
AccessibleObject.setAccessible(fields, true);
for (Field f : fields) {
if (f.isAnnotationPresent(XmlLocation.class))
{
if (! f.getType().equals(Location.class))
throw new JAXBException(L.l("Fields annotated by @Location must have type javax.xml.stream.Location"));
_locationAccessor = new FieldAccessor(f);
}
// special case: jaxb/0250
// fields which are static are skipped _unless_ they are also
// both final and attributes
if (Modifier.isStatic(f.getModifiers()) &&
! (Modifier.isFinal(f.getModifiers()) &&
f.isAnnotationPresent(XmlAttribute.class)))
continue;
if (f.isAnnotationPresent(XmlTransient.class))
continue;
// jaxb/0176: transient modifier ignored
if (accessType == XmlAccessType.PUBLIC_MEMBER &&
! Modifier.isPublic(f.getModifiers()) &&
! JAXBUtil.isJAXBAnnotated(f))
continue;
if (accessType == XmlAccessType.NONE && ! JAXBUtil.isJAXBAnnotated(f))
continue;
Accessor a = new FieldAccessor(f);
if (orderMap != null) {
Integer i = orderMap.remove(f.getName());
if (i != null)
a.setOrder(i.intValue());
// XXX else throw something?
}
processAccessor(a);
}
}
// do ordering if necessary
if (orderMap == null && accessOrder == XmlAccessOrder.ALPHABETICAL)
Collections.sort(_elementMappings, XmlMapping.nameComparator);
}
catch (JAXBException e) {
throw e;
}
catch (Exception e) {
throw new JAXBException(L.l("{0}: Initialization error",
_class.getName()), e);
}
if (! Object.class.equals(_class.getSuperclass()))
_parent = _context.getSkeleton(_class.getSuperclass());
}
/**
* Handles any processing that needs to happen after all ClassSkeletons
* have been created and all classes have been discovered.
**/
public void postProcess()
throws JAXBException
{
if (log.isLoggable(Level.FINEST))
log.finest("JAXB: " + _class.getName() + " has children: ");
for (int i = 0; i < _elementMappings.size(); i++)
_elementMappings.get(i).putQNames(_elementQNameToMappingMap);
}
/**
* Create an XmlMapping for this accessor and insert it in the correct
* mapping or field.
**/
private void processAccessor(Accessor accessor)
throws JAXBException
{
XmlMapping mapping = XmlMapping.newInstance(_context, accessor);
if (mapping instanceof XmlValueMapping) {
if (_value != null)
throw new JAXBException(L.l("Cannot have two @XmlValue annotated fields or properties"));
if (_elementMappings.size() > 0) {
// in case of propOrder & XmlValue
if (_elementMappings.size() != 1 || _elementMappings.get(0) != null)
throw new JAXBException(L.l("Cannot have both @XmlValue and elements in a JAXB element (e.g. {0})", _elementMappings.get(0)));
_elementMappings.clear();
}
_value = (XmlValueMapping) mapping;
}
else if (mapping instanceof AttributeMapping) {
mapping.putQNames(_attributeQNameToMappingMap);
_attributeMappings.add((AttributeMapping) mapping);
}
else if (mapping instanceof AnyAttributeMapping) {
if (_anyAttributeMapping != null)
throw new JAXBException(L.l("Cannot have two fields or properties with @XmlAnyAttribute annotation"));
_anyAttributeMapping = (AnyAttributeMapping) mapping;
_attributeMappings.add(mapping);
}
else if ((mapping instanceof ElementMapping) ||
(mapping instanceof ElementRefMapping) ||
(mapping instanceof ElementsMapping)) {
if (_value != null)
throw new JAXBException(L.l("{0}: Cannot have both @XmlValue and elements in a JAXB element", _class.getName()));
if (mapping.getAccessor().getOrder() >= 0)
_elementMappings.set(mapping.getAccessor().getOrder(), mapping);
else
_elementMappings.add(mapping);
}
else if (mapping instanceof AnyElementMapping) {
if (_anyElementMapping != null)
throw new JAXBException(L.l("{0}: Cannot have two @XmlAnyElement annotations in a single class", _class.getName()));
_anyElementMapping = (AnyElementMapping) mapping;
}
else {
throw new RuntimeException(L.l("Unknown mapping type {0}", mapping.getClass()));
}
}
private XmlMapping getElementMapping(QName q)
throws JAXBException
{
XmlMapping mapping = _elementQNameToMappingMap.get(q);
if (mapping != null)
return mapping;
if (_anyElementMapping != null)
return _anyElementMapping;
if (_parent != null)
return _parent.getElementMapping(q);
return null;
}
public XmlMapping getAttributeMapping(QName q)
throws JAXBException
{
XmlMapping mapping = _attributeQNameToMappingMap.get(q);
if (mapping != null)
return mapping;
if (_anyAttributeMapping != null)
return _anyAttributeMapping;
if (_parent != null)
return _parent.getAttributeMapping(q);
return null;
}
public QName getElementName()
{
if (_elementName != null)
return _elementName;
else
return _typeName;
}
public void setElementName(QName elementName)
{
_elementName = elementName;
}
public QName getTypeName()
{
return _typeName;
}
public void setCreateMethod(Method createMethod, Object factory)
{
_createMethod = createMethod;
_factory = factory;
}
public C newInstance()
throws JAXBException
{
try {
if (_createMethod != null && _factory != null) {
return (C) _createMethod.invoke(_factory);
}
else {
// XXX move into constructor
XmlType xmlType = getXmlType();
if (xmlType != null) {
Class factoryClass = xmlType.factoryClass();
if (xmlType.factoryClass() == XmlType.DEFAULT.class)
factoryClass = _class;
if (! "".equals(xmlType.factoryMethod())) {
Method m =
factoryClass.getMethod(xmlType.factoryMethod(), NO_PARAMS);
if (! Modifier.isStatic(m.getModifiers()))
throw new JAXBException(L.l("Factory method not static"));
return (C) m.invoke(null);
}
}
Constructor con = _class.getConstructor(NO_PARAMS);
return (C)con.newInstance(NO_ARGS);
}
}
catch (JAXBException e) {
throw e;
}
catch (Exception e) {
throw new JAXBException(e);
}
}
public XmlType getXmlType()
{
return (XmlType)_class.getAnnotation(XmlType.class);
}
public Object read(Unmarshaller u, XMLStreamReader in)
throws IOException, XMLStreamException, JAXBException
{
try {
C ret = null;
String nil = in.getAttributeValue(W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil");
if (! "true".equals(nil))
ret = newInstance();
if (_locationAccessor != null)
_locationAccessor.set(ret, in.getLocation());
if (_beforeUnmarshal != null)
_beforeUnmarshal.invoke(ret, u, null);
if (u.getListener() != null)
u.getListener().beforeUnmarshal(ret, null);
if (_value != null) {
_value.read(u, in, ret, this);
}
else {
// process the attributes
for (int i = 0; i < in.getAttributeCount(); i++) {
QName attributeName = in.getAttributeName(i);
XmlMapping mapping = getAttributeMapping(attributeName);
if (mapping == null)
throw new UnmarshalException(L.l("Attribute {0} not found in {1}",
attributeName, getType()));
mapping.readAttribute(in, i, ret);
}
int i = 0;
in.nextTag();
while (in.getEventType() == in.START_ELEMENT) {
XmlMapping mapping = getElementMapping(in.getName());
if (mapping == null) {
throw new UnmarshalException(L.l("Child <{0}> not found in {1}",
in.getName(), getType()));
}
if (! mapping.getAccessor().checkOrder(i++, u.getEventHandler())) {
throw new UnmarshalException(L.l("Child <{0}> misordered in {1}",
in.getName(), getType()));
}
mapping.read(u, in, ret);
}
// essentially a nextTag() that handles end of document gracefully
while (in.hasNext()) {
in.next();
if (in.getEventType() == in.START_ELEMENT ||
in.getEventType() == in.END_ELEMENT)
break;
}
}
if (_afterUnmarshal != null)
_afterUnmarshal.invoke(ret, u, null);
if (u.getListener() != null)
u.getListener().afterUnmarshal(ret, null);
return ret;
}
catch (InvocationTargetException e) {
if (e.getTargetException() != null)
throw new UnmarshalException(e.getTargetException());
throw new UnmarshalException(e);
}
catch (IllegalAccessException e) {
throw new UnmarshalException(e);
}
}
public Object bindFrom(BinderImpl binder, Object existing, NodeIterator node)
throws IOException, JAXBException
{
Node root = node.getNode();
C ret = (C) existing;
if (ret == null)
ret = newInstance();
if (_value != null) {
_value.bindFrom(binder, node, ret);
}
else {
int i = 0;
Node child = node.firstChild();
while (child != null) {
if (child.getNodeType() == Node.ELEMENT_NODE) {
QName name = JAXBUtil.qnameFromNode(child);
XmlMapping mapping = getElementMapping(name);
if (mapping == null)
throw new UnmarshalException(L.l("Child <{0}> not found", name));
if (! mapping.getAccessor().checkOrder(i++, binder.getEventHandler()))
throw new UnmarshalException(L.l("Child <{0}> misordered", name));
mapping.bindFrom(binder, node, ret);
}
child = node.nextSibling();
}
}
node.setNode(root);
binder.bind(ret, root);
return ret;
}
public void write(Marshaller m, XMLStreamWriter out,
Object obj, Namer namer,
ArrayList<XmlMapping> attributes)
throws IOException, XMLStreamException, JAXBException
{
if (obj == null)
return;
try {
if (_beforeMarshal != null)
_beforeMarshal.invoke(obj, m);
if (m.getListener() != null)
m.getListener().beforeMarshal(obj);
QName tagName = null;
if (namer != null)
tagName = namer.getQName(obj);
if (tagName == null)
tagName = _elementName;
if (_value != null) {
_value.setQName(tagName);
_value.write(m, out, obj, _attributeMappings);
}
else {
if (tagName.getNamespaceURI() == null ||
"".equals(tagName.getNamespaceURI()))
out.writeStartElement(tagName.getLocalPart());
else if (tagName.getPrefix() == null ||
"".equals(tagName.getPrefix()))
out.writeStartElement(tagName.getNamespaceURI(),
tagName.getLocalPart());
else
out.writeStartElement(tagName.getPrefix(),
tagName.getLocalPart(),
tagName.getNamespaceURI());
if (attributes != null) {
for (int i = 0; i < attributes.size(); i++)
attributes.get(i).write(m, out, obj);
}
for (XmlMapping mapping : _attributeMappings)
mapping.write(m, out, obj);
for (XmlMapping mapping : _elementMappings)
mapping.write(m, out, obj);
if (_anyElementMapping != null) // XXX ordering!
_anyElementMapping.write(m, out, obj);
out.writeEndElement();
}
if (_afterMarshal != null)
_afterMarshal.invoke(obj, m);
if (m.getListener() != null)
m.getListener().afterMarshal(obj);
}
catch (InvocationTargetException e) {
throw new JAXBException(e);
}
catch (IllegalAccessException e) {
throw new JAXBException(e);
}
}
public Node bindTo(BinderImpl binder, Node node,
Object obj, Namer namer,
ArrayList<XmlMapping> attributes)
throws IOException, JAXBException
{
if (obj == null)
return null;
QName tagName = null;
if (namer != null)
tagName = namer.getQName(obj);
if (tagName == null)
tagName = _elementName;
if (_value != null) {
Node newNode = _value.bindTo(binder, node, obj);
if (newNode != node) {
binder.invalidate(node);
node = newNode;
}
}
else {
QName nodeName = JAXBUtil.qnameFromNode(node);
if (tagName.equals(nodeName)) {
Node child = node.getFirstChild();
child = JAXBUtil.skipIgnorableNodes(child);
if (attributes != null) {
// XXX
}
for (XmlMapping mapping : _elementMappings) {
if (child != null) {
// try to reuse as many of the child nodes as possible
Node newNode = mapping.bindTo(binder, child, obj);
if (newNode != child) {
node.replaceChild(newNode, child);
binder.invalidate(child);
child = newNode;
}
child = child.getNextSibling();
child = JAXBUtil.skipIgnorableNodes(child);
}
else {
Node newNode =
JAXBUtil.elementFromQName(mapping.getQName(obj), node);
node.appendChild(mapping.bindTo(binder, newNode, obj));
}
}
}
else {
binder.invalidate(node);
node = JAXBUtil.elementFromQName(tagName, node);
for (XmlMapping mapping : _elementMappings) {
Node child = JAXBUtil.elementFromQName(mapping.getQName(obj), node);
node.appendChild(mapping.bindTo(binder, child, obj));
}
}
}
binder.bind(obj, node);
return node;
}
public boolean isRootElement()
{
return _elementName != null;
}
public void generateSchema(XMLStreamWriter out)
throws JAXBException, XMLStreamException
{
if (_elementName != null) {
if ("".equals(_typeName.getLocalPart()))
out.writeStartElement(XML_SCHEMA_PREFIX, "element", XML_SCHEMA_NS);
else {
out.writeEmptyElement(XML_SCHEMA_PREFIX, "element", XML_SCHEMA_NS);
out.writeAttribute("type", _typeName.getLocalPart());
}
out.writeAttribute("name", _elementName.getLocalPart());
}
generateSchemaType(out);
if (_elementName != null && "".equals(_typeName.getLocalPart()))
out.writeEndElement(); // element
}
public void generateSchemaType(XMLStreamWriter out)
throws JAXBException, XMLStreamException
{
if (_value != null) {
out.writeStartElement(XML_SCHEMA_PREFIX, "simpleType", XML_SCHEMA_NS);
if (! "".equals(_typeName.getLocalPart()))
out.writeAttribute("name", _typeName.getLocalPart());
if (Collection.class.isAssignableFrom(_value.getAccessor().getType())) {
out.writeEmptyElement(XML_SCHEMA_PREFIX, "list", XML_SCHEMA_NS);
String itemType = StaxUtil.qnameToString(out, _value.getSchemaType());
out.writeAttribute("itemType", itemType);
}
else {
out.writeEmptyElement(XML_SCHEMA_PREFIX, "restriction", XML_SCHEMA_NS);
String base = StaxUtil.qnameToString(out, _value.getSchemaType());
out.writeAttribute("base", base);
}
for (XmlMapping mapping : _attributeMappings)
mapping.generateSchema(out);
out.writeEndElement(); // simpleType
}
else {
out.writeStartElement(XML_SCHEMA_PREFIX, "complexType", XML_SCHEMA_NS);
if (Modifier.isAbstract(_class.getModifiers()))
out.writeAttribute("abstract", "true");
if (! "".equals(_typeName.getLocalPart()))
out.writeAttribute("name", _typeName.getLocalPart());
out.writeStartElement(XML_SCHEMA_PREFIX, "sequence", XML_SCHEMA_NS);
for (XmlMapping mapping : _elementMappings)
mapping.generateSchema(out);
if (_anyElementMapping != null)
_anyElementMapping.generateSchema(out);
out.writeEndElement(); // sequence
for (XmlMapping mapping : _attributeMappings)
mapping.generateSchema(out);
out.writeEndElement(); // complexType
}
}
//XXX The TreeSet needs this for some reason
private static final Comparator methodComparator
= new java.util.Comparator<Method>() {
public int compare(Method m1, Method m2)
{
return m1.toGenericString().compareTo(m2.toGenericString());
}
public boolean equals(Object obj)
{
return obj == this;
}
};
}