001: /*
002: * Copyright (c) 1998-2007 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Emil Ong
028: */
029:
030: package com.caucho.jaxb.mapping;
031:
032: import com.caucho.jaxb.BinderImpl;
033: import com.caucho.jaxb.JAXBContextImpl;
034: import com.caucho.jaxb.JAXBUtil;
035: import com.caucho.jaxb.NodeIterator;
036: import com.caucho.jaxb.accessor.Accessor;
037: import com.caucho.jaxb.property.Property;
038: import com.caucho.jaxb.skeleton.ClassSkeleton;
039: import com.caucho.util.L10N;
040: import com.caucho.xml.stream.StaxUtil;
041:
042: import org.w3c.dom.Node;
043:
044: import static javax.xml.XMLConstants.*;
045:
046: import javax.xml.bind.JAXBElement;
047: import javax.xml.bind.JAXBException;
048: import javax.xml.bind.Marshaller;
049: import javax.xml.bind.Unmarshaller;
050: import javax.xml.bind.UnmarshalException;
051:
052: import javax.xml.bind.annotation.XmlAnyAttribute;
053: import javax.xml.bind.annotation.XmlAnyElement;
054: import javax.xml.bind.annotation.XmlAttribute;
055: import javax.xml.bind.annotation.XmlElement;
056: import javax.xml.bind.annotation.XmlElementRef;
057: import javax.xml.bind.annotation.XmlElements;
058: import javax.xml.bind.annotation.XmlElementWrapper;
059: import javax.xml.bind.annotation.XmlID;
060: import javax.xml.bind.annotation.XmlIDREF;
061: import javax.xml.bind.annotation.XmlList;
062: import javax.xml.bind.annotation.XmlMimeType;
063: import javax.xml.bind.annotation.XmlNsForm;
064: import javax.xml.bind.annotation.XmlSchema;
065: import javax.xml.bind.annotation.XmlSchemaType;
066: import javax.xml.bind.annotation.XmlSchemaTypes;
067: import javax.xml.bind.annotation.XmlType;
068: import javax.xml.bind.annotation.XmlValue;
069:
070: import javax.xml.namespace.QName;
071:
072: import javax.xml.stream.XMLStreamException;
073: import javax.xml.stream.XMLStreamReader;
074: import javax.xml.stream.XMLStreamWriter;
075:
076: import java.io.IOException;
077: import java.lang.annotation.Annotation;
078: import java.util.ArrayList;
079: import java.util.Comparator;
080: import java.util.Map;
081:
082: public abstract class XmlMapping implements Namer {
083: private static final L10N L = new L10N(XmlMapping.class);
084:
085: public static final String XML_SCHEMA_PREFIX = "xsd";
086: public static final String XML_INSTANCE_PREFIX = "xsi";
087: public static final String XML_MIME_NS = "http://www.w3.org/2005/05/xmlmime";
088:
089: protected static boolean _generateRICompatibleSchema = true;
090:
091: protected Accessor _accessor;
092: protected JAXBContextImpl _context;
093: protected Property _property;
094: protected boolean _nillable;
095:
096: protected XmlMapping() {
097: }
098:
099: protected XmlMapping(JAXBContextImpl context, Accessor accessor) {
100: _context = context;
101: _accessor = accessor;
102: }
103:
104: public static final Comparator<XmlMapping> nameComparator = new Comparator<XmlMapping>() {
105: public int compare(XmlMapping a1, XmlMapping a2) {
106: return a1._accessor.getName().compareTo(
107: a2._accessor.getName());
108: }
109:
110: public boolean equals(Object o) {
111: return this == o;
112: }
113: };
114:
115: public static void setGenerateRICompatibleSchema(boolean compatible) {
116: _generateRICompatibleSchema = compatible;
117: }
118:
119: public static XmlMapping newInstance(JAXBContextImpl context,
120: Accessor accessor) throws JAXBException {
121: XmlAnyAttribute xmlAnyAttribute = accessor
122: .getAnnotation(XmlAnyAttribute.class);
123: XmlAnyElement xmlAnyElement = accessor
124: .getAnnotation(XmlAnyElement.class);
125: XmlAttribute xmlAttribute = accessor
126: .getAnnotation(XmlAttribute.class);
127: XmlElement xmlElement = accessor
128: .getAnnotation(XmlElement.class);
129: XmlElementRef xmlElementRef = accessor
130: .getAnnotation(XmlElementRef.class);
131: XmlElements xmlElements = accessor
132: .getAnnotation(XmlElements.class);
133: XmlID xmlID = accessor.getAnnotation(XmlID.class);
134: XmlIDREF xmlIDREF = accessor.getAnnotation(XmlIDREF.class);
135: XmlValue xmlValue = accessor.getAnnotation(XmlValue.class);
136:
137: // jaxb/02d1
138: if (xmlID != null && !String.class.equals(accessor.getType()))
139: throw new JAXBException(
140: L
141: .l(
142: "Fields or properties annotated with XmlID must have type String: {0}",
143: accessor));
144:
145: // check for conflicting annotations
146: Annotation[] annotations = new Annotation[] { xmlAnyAttribute,
147: xmlAnyElement, xmlAttribute, xmlElement, xmlElementRef,
148: xmlElements, xmlIDREF, xmlValue };
149:
150: int count = 0;
151: StringBuilder sb = new StringBuilder();
152:
153: for (int i = 0; i < annotations.length; i++) {
154: if (annotations[i] != null) {
155: count++;
156: sb.append("@" + annotations[i].getClass().getName());
157: sb.append(", ");
158: }
159: }
160:
161: if (count > 1) {
162: throw new JAXBException(
163: L
164: .l(
165: "Annotations {0} cannot be used together on a single field or property",
166: sb.toString()));
167: }
168:
169: if (xmlValue != null)
170: return new XmlValueMapping(context, accessor);
171:
172: else if (xmlAnyAttribute != null)
173: return new AnyAttributeMapping(context, accessor);
174:
175: else if (xmlAttribute != null)
176: return new AttributeMapping(context, accessor);
177:
178: else if (xmlElementRef != null)
179: return new ElementRefMapping(context, accessor);
180:
181: else if (xmlElements != null)
182: return new ElementsMapping(context, accessor);
183:
184: else if (xmlAnyElement != null)
185: return new AnyElementMapping(context, accessor);
186:
187: else
188: return new ElementMapping(context, accessor);
189: }
190:
191: // accessor methods
192:
193: public Accessor getAccessor() {
194: return _accessor;
195: }
196:
197: public QName getSchemaType() {
198: return _property.getSchemaType();
199: }
200:
201: // utility methods
202:
203: public void putQNames(Map<QName, XmlMapping> map)
204: throws JAXBException {
205: }
206:
207: protected QName qnameFromXmlElement(XmlElement element) {
208: String name = _accessor.getName();
209: String namespace = null;
210:
211: XmlSchema xmlSchema = _accessor
212: .getPackageAnnotation(XmlSchema.class);
213:
214: if (xmlSchema != null
215: && xmlSchema.elementFormDefault() == XmlNsForm.QUALIFIED)
216: namespace = xmlSchema.namespace();
217:
218: if (!element.name().equals("##default"))
219: name = element.name();
220:
221: if (!element.namespace().equals("##default"))
222: namespace = element.namespace();
223:
224: if (namespace == null)
225: return new QName(name);
226: else
227: return new QName(namespace, name);
228: }
229:
230: // output methods
231:
232: public void write(Marshaller m, XMLStreamWriter out, Object obj)
233: throws IOException, JAXBException, XMLStreamException {
234: Object value = _accessor.get(obj);
235:
236: ArrayList<XmlMapping> attributes = null;
237:
238: if (value == null && _nillable) {
239: attributes = new ArrayList<XmlMapping>();
240: attributes.add(NilWrapper.INSTANCE);
241: }
242:
243: // XXX: interface/enum
244: if (_property != null)
245: _property.write(m, out, value, this , obj, attributes);
246: }
247:
248: public void write(Marshaller m, XMLStreamWriter out, Object obj,
249: ArrayList<XmlMapping> attributes) throws IOException,
250: JAXBException, XMLStreamException {
251: Object value = _accessor.get(obj);
252:
253: if (value == null && _nillable) {
254: if (attributes == null)
255: attributes = new ArrayList<XmlMapping>();
256:
257: attributes.add(NilWrapper.INSTANCE);
258: }
259:
260: _property.write(m, out, value, this , obj, attributes);
261: }
262:
263: public Node bindTo(BinderImpl binder, Node node, Object obj)
264: throws IOException, JAXBException {
265: return _property.bindTo(binder, node, _accessor.get(obj), this );
266: }
267:
268: // input methods
269:
270: public void readAttribute(XMLStreamReader in, int i, Object parent)
271: throws IOException, XMLStreamException, JAXBException {
272: throw new UnsupportedOperationException(
273: L
274: .l(
275: "Internal JAXB error: this field or property does not map to an attribute {0}",
276: _accessor));
277: }
278:
279: public void read(Unmarshaller u, XMLStreamReader in, Object parent,
280: ClassSkeleton attributed) throws IOException,
281: XMLStreamException, JAXBException {
282: Object value = _property.read(u, in, _accessor.get(parent),
283: attributed, parent);
284:
285: _accessor.set(parent, value);
286: }
287:
288: public void read(Unmarshaller u, XMLStreamReader in, Object parent)
289: throws IOException, XMLStreamException, JAXBException {
290: Object value = _property.read(u, in, _accessor.get(parent));
291: _accessor.set(parent, value);
292: }
293:
294: public void bindFrom(BinderImpl binder, NodeIterator node,
295: Object obj) throws IOException, JAXBException {
296: Object value = _property.bindFrom(binder, node, _accessor
297: .get(obj));
298: _accessor.set(obj, value);
299: }
300:
301: // abstract methods
302:
303: public abstract QName getQName(Object obj) throws JAXBException;
304:
305: public abstract void generateSchema(XMLStreamWriter out)
306: throws JAXBException, XMLStreamException;
307:
308: }
|