/* * 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 */ package com.caucho.jaxb.mapping; import com.caucho.jaxb.BinderImpl; import com.caucho.jaxb.JAXBContextImpl; import com.caucho.jaxb.JAXBUtil; import com.caucho.jaxb.NodeIterator; import com.caucho.jaxb.accessor.Accessor; import com.caucho.jaxb.property.Property; import com.caucho.jaxb.skeleton.ClassSkeleton; import com.caucho.util.L10N; import com.caucho.xml.stream.StaxUtil; import org.w3c.dom.Node; import static javax.xml.XMLConstants.*; import javax.xml.bind.JAXBElement; 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.XmlAnyAttribute; import javax.xml.bind.annotation.XmlAnyElement; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementRef; import javax.xml.bind.annotation.XmlElements; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlID; import javax.xml.bind.annotation.XmlIDREF; import javax.xml.bind.annotation.XmlList; import javax.xml.bind.annotation.XmlMimeType; import javax.xml.bind.annotation.XmlNsForm; import javax.xml.bind.annotation.XmlSchema; import javax.xml.bind.annotation.XmlSchemaType; import javax.xml.bind.annotation.XmlSchemaTypes; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlValue; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; import java.io.IOException; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Comparator; import java.util.Map; public abstract class XmlMapping implements Namer { private static final L10N L = new L10N(XmlMapping.class); public static final String XML_SCHEMA_PREFIX = "xsd"; public static final String XML_INSTANCE_PREFIX = "xsi"; public static final String XML_MIME_NS = "http://www.w3.org/2005/05/xmlmime"; protected static boolean _generateRICompatibleSchema = true; protected Accessor _accessor; protected JAXBContextImpl _context; protected Property _property; protected boolean _nillable; protected XmlMapping() { } protected XmlMapping(JAXBContextImpl context, Accessor accessor) { _context = context; _accessor = accessor; } public static final Comparator<XmlMapping> nameComparator = new Comparator<XmlMapping>() { public int compare(XmlMapping a1, XmlMapping a2) { return a1._accessor.getName().compareTo(a2._accessor.getName()); } public boolean equals(Object o) { return this == o; } }; public static void setGenerateRICompatibleSchema(boolean compatible) { _generateRICompatibleSchema = compatible; } public static XmlMapping newInstance(JAXBContextImpl context, Accessor accessor) throws JAXBException { XmlAnyAttribute xmlAnyAttribute = accessor.getAnnotation(XmlAnyAttribute.class); XmlAnyElement xmlAnyElement = accessor.getAnnotation(XmlAnyElement.class); XmlAttribute xmlAttribute = accessor.getAnnotation(XmlAttribute.class); XmlElement xmlElement = accessor.getAnnotation(XmlElement.class); XmlElementRef xmlElementRef = accessor.getAnnotation(XmlElementRef.class); XmlElements xmlElements = accessor.getAnnotation(XmlElements.class); XmlID xmlID = accessor.getAnnotation(XmlID.class); XmlIDREF xmlIDREF = accessor.getAnnotation(XmlIDREF.class); XmlValue xmlValue = accessor.getAnnotation(XmlValue.class); // jaxb/02d1 if (xmlID != null && ! String.class.equals(accessor.getType())) throw new JAXBException(L.l("Fields or properties annotated with XmlID must have type String: {0}", accessor)); // check for conflicting annotations Annotation[] annotations = new Annotation[] { xmlAnyAttribute, xmlAnyElement, xmlAttribute, xmlElement, xmlElementRef, xmlElements, xmlIDREF, xmlValue }; int count = 0; StringBuilder sb = new StringBuilder(); for (int i = 0; i < annotations.length; i++) { if (annotations[i] != null) { count++; sb.append("@" + annotations[i].getClass().getName()); sb.append(", "); } } if (count > 1) { throw new JAXBException(L.l("Annotations {0} cannot be used together on a single field or property", sb.toString())); } if (xmlValue != null) return new XmlValueMapping(context, accessor); else if (xmlAnyAttribute != null) return new AnyAttributeMapping(context, accessor); else if (xmlAttribute != null) return new AttributeMapping(context, accessor); else if (xmlElementRef != null) return new ElementRefMapping(context, accessor); else if (xmlElements != null) return new ElementsMapping(context, accessor); else if (xmlAnyElement != null) return new AnyElementMapping(context, accessor); else return new ElementMapping(context, accessor); } // accessor methods public Accessor getAccessor() { return _accessor; } public QName getSchemaType() { return _property.getSchemaType(); } // utility methods public void putQNames(Map<QName,XmlMapping> map) throws JAXBException { } protected QName qnameFromXmlElement(XmlElement element) { String name = _accessor.getName(); String namespace = null; XmlSchema xmlSchema = _accessor.getPackageAnnotation(XmlSchema.class); if (xmlSchema != null && xmlSchema.elementFormDefault() == XmlNsForm.QUALIFIED) namespace = xmlSchema.namespace(); if (! element.name().equals("##default")) name = element.name(); if (! element.namespace().equals("##default")) namespace = element.namespace(); if (namespace == null) return new QName(name); else return new QName(namespace, name); } // output methods public void write(Marshaller m, XMLStreamWriter out, Object obj) throws IOException, JAXBException, XMLStreamException { Object value = _accessor.get(obj); ArrayList<XmlMapping> attributes = null; if (value == null && _nillable) { attributes = new ArrayList<XmlMapping>(); attributes.add(NilWrapper.INSTANCE); } // XXX: interface/enum if (_property != null) _property.write(m, out, value, this, obj, attributes); } public void write(Marshaller m, XMLStreamWriter out, Object obj, ArrayList<XmlMapping> attributes) throws IOException, JAXBException, XMLStreamException { Object value = _accessor.get(obj); if (value == null && _nillable) { if (attributes == null) attributes = new ArrayList<XmlMapping>(); attributes.add(NilWrapper.INSTANCE); } _property.write(m, out, value, this, obj, attributes); } public Node bindTo(BinderImpl binder, Node node, Object obj) throws IOException, JAXBException { return _property.bindTo(binder, node, _accessor.get(obj), this); } // input methods public void readAttribute(XMLStreamReader in, int i, Object parent) throws IOException, XMLStreamException, JAXBException { throw new UnsupportedOperationException(L.l("Internal JAXB error: this field or property does not map to an attribute {0}", _accessor)); } public void read(Unmarshaller u, XMLStreamReader in, Object parent, ClassSkeleton attributed) throws IOException, XMLStreamException, JAXBException { Object value = _property.read(u, in, _accessor.get(parent), attributed, parent); _accessor.set(parent, value); } public void read(Unmarshaller u, XMLStreamReader in, Object parent) throws IOException, XMLStreamException, JAXBException { Object value = _property.read(u, in, _accessor.get(parent)); _accessor.set(parent, value); } public void bindFrom(BinderImpl binder, NodeIterator node, Object obj) throws IOException, JAXBException { Object value = _property.bindFrom(binder, node, _accessor.get(obj)); _accessor.set(obj, value); } // abstract methods public abstract QName getQName(Object obj) throws JAXBException; public abstract void generateSchema(XMLStreamWriter out) throws JAXBException, XMLStreamException; }