0001: /*
0002: * Copyright (c) 1998-2007 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Emil Ong, Adam Megacz
0028: */
0029:
0030: package com.caucho.jaxb.skeleton;
0031:
0032: import org.w3c.dom.Node;
0033:
0034: import static javax.xml.XMLConstants.*;
0035:
0036: import javax.xml.bind.JAXBException;
0037: import javax.xml.bind.Marshaller;
0038: import javax.xml.bind.Unmarshaller;
0039: import javax.xml.bind.UnmarshalException;
0040: import javax.xml.bind.annotation.*;
0041:
0042: import javax.xml.namespace.QName;
0043:
0044: import javax.xml.stream.Location;
0045: import javax.xml.stream.XMLStreamException;
0046: import javax.xml.stream.XMLStreamReader;
0047: import javax.xml.stream.XMLStreamWriter;
0048:
0049: import java.io.IOException;
0050:
0051: import java.lang.reflect.AccessibleObject;
0052: import java.lang.reflect.Constructor;
0053: import java.lang.reflect.Field;
0054: import java.lang.reflect.InvocationTargetException;
0055: import java.lang.reflect.Method;
0056: import java.lang.reflect.Modifier;
0057:
0058: import java.util.ArrayList;
0059: import java.util.Collection;
0060: import java.util.Collections;
0061: import java.util.Comparator;
0062: import java.util.HashMap;
0063: import java.util.HashSet;
0064: import java.util.Set;
0065: import java.util.TreeSet;
0066: import java.util.logging.Level;
0067: import java.util.logging.Logger;
0068:
0069: import com.caucho.jaxb.BinderImpl;
0070: import com.caucho.jaxb.JAXBContextImpl;
0071: import com.caucho.jaxb.JAXBUtil;
0072: import com.caucho.jaxb.NodeIterator;
0073: import com.caucho.jaxb.annotation.XmlLocation;
0074:
0075: import com.caucho.jaxb.accessor.Accessor;
0076: import com.caucho.jaxb.accessor.FieldAccessor;
0077: import com.caucho.jaxb.accessor.GetterSetterAccessor;
0078:
0079: import com.caucho.jaxb.mapping.AnyAttributeMapping;
0080: import com.caucho.jaxb.mapping.AttributeMapping;
0081: import com.caucho.jaxb.mapping.AnyElementMapping;
0082: import com.caucho.jaxb.mapping.ElementMapping;
0083: import com.caucho.jaxb.mapping.ElementRefMapping;
0084: import com.caucho.jaxb.mapping.ElementsMapping;
0085: import com.caucho.jaxb.mapping.Namer;
0086: import com.caucho.jaxb.mapping.XmlMapping;
0087: import com.caucho.jaxb.mapping.XmlValueMapping;
0088:
0089: import com.caucho.util.L10N;
0090:
0091: import com.caucho.xml.stream.StaxUtil;
0092:
0093: public class ClassSkeleton<C> {
0094: public static final String XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema";
0095: public static final String XML_SCHEMA_PREFIX = "xsd";
0096:
0097: private static final L10N L = new L10N(ClassSkeleton.class);
0098: private static final Logger log = Logger
0099: .getLogger(ClassSkeleton.class.getName());
0100:
0101: private static final Class[] NO_PARAMS = new Class[0];
0102: private static final Object[] NO_ARGS = new Object[0];
0103:
0104: protected JAXBContextImpl _context;
0105: protected QName _elementName;
0106: private Class<C> _class;
0107: private ClassSkeleton _parent;
0108: private Package _package;
0109: private Method _createMethod;
0110: private Object _factory;
0111: private QName _typeName;
0112:
0113: private HashMap<QName, XmlMapping> _attributeQNameToMappingMap = new HashMap<QName, XmlMapping>();
0114:
0115: private HashMap<QName, XmlMapping> _elementQNameToMappingMap = new HashMap<QName, XmlMapping>();
0116:
0117: private ArrayList<XmlMapping> _attributeMappings = new ArrayList<XmlMapping>();
0118:
0119: private ArrayList<XmlMapping> _elementMappings = new ArrayList<XmlMapping>();
0120:
0121: private AnyElementMapping _anyElementMapping;
0122: private AnyAttributeMapping _anyAttributeMapping;
0123:
0124: private Method _beforeUnmarshal;
0125: private Method _afterUnmarshal;
0126: private Method _beforeMarshal;
0127: private Method _afterMarshal;
0128:
0129: private Constructor _constructor;
0130:
0131: /** Special hook to allow injecting the location where an instance was
0132: unmarshalled from. */
0133: private Accessor _locationAccessor;
0134:
0135: /**
0136: * The value @XmlValue.
0137: *
0138: **/
0139: protected XmlValueMapping _value;
0140:
0141: public Class<C> getType() {
0142: return _class;
0143: }
0144:
0145: public String toString() {
0146: return "ClassSkeleton[" + _class + "]";
0147: }
0148:
0149: protected ClassSkeleton(JAXBContextImpl context) {
0150: _context = context;
0151: }
0152:
0153: public ClassSkeleton(JAXBContextImpl context, Class<C> c) {
0154: this (context);
0155: _class = c;
0156: }
0157:
0158: public void init() throws JAXBException {
0159: try {
0160: _package = _class.getPackage();
0161:
0162: // check for special before and after methods
0163: try {
0164: _beforeUnmarshal = _class.getMethod("beforeUnmarshal",
0165: Unmarshaller.class, Object.class);
0166: } catch (NoSuchMethodException _) {
0167: // deliberate
0168: }
0169:
0170: try {
0171: _afterUnmarshal = _class.getMethod("afterUnmarshal",
0172: Unmarshaller.class, Object.class);
0173: } catch (NoSuchMethodException _) {
0174: // deliberate
0175: }
0176:
0177: try {
0178: _beforeMarshal = _class.getMethod("beforeMarshal",
0179: Marshaller.class);
0180: } catch (NoSuchMethodException _) {
0181: // deliberate
0182: }
0183:
0184: try {
0185: _afterMarshal = _class.getMethod("afterMarshal",
0186: Marshaller.class);
0187: } catch (NoSuchMethodException _) {
0188: // deliberate
0189: }
0190:
0191: if (Set.class.isAssignableFrom(_class)) {
0192: // XXX:
0193: }
0194:
0195: // XXX: @XmlJavaTypeAdapter
0196:
0197: // Find the zero-parameter constructor
0198: try {
0199: _constructor = _class.getConstructor(NO_PARAMS);
0200: _constructor.setAccessible(true);
0201: } catch (Exception e1) {
0202: try {
0203: _constructor = _class
0204: .getDeclaredConstructor(NO_PARAMS);
0205: _constructor.setAccessible(true);
0206: } catch (Exception e2) {
0207: throw new JAXBException(L.l(
0208: "{0}: Zero-arg constructor not found",
0209: _class.getName()), e2);
0210: }
0211: }
0212:
0213: _typeName = JAXBUtil.getXmlSchemaDatatype(_class, _context);
0214:
0215: // Special case: when name="", this is an "anonymous" type, bound
0216: // exclusively to a particular element name
0217: if (!"".equals(_typeName.getLocalPart()))
0218: _context.addXmlType(_typeName, this );
0219:
0220: // Check for the complete name of the element...
0221: String namespace = _context.getTargetNamespace();
0222:
0223: // look at package defaults first...
0224: XmlSchema schema = (XmlSchema) _package
0225: .getAnnotation(XmlSchema.class);
0226:
0227: if (schema != null && !"".equals(schema.namespace()))
0228: namespace = schema.namespace();
0229:
0230: // then look at class specific overrides.
0231: XmlRootElement xre = _class
0232: .getAnnotation(XmlRootElement.class);
0233:
0234: if (xre != null) {
0235: String localName = null;
0236:
0237: if ("##default".equals(xre.name()))
0238: localName = JAXBUtil.identifierToXmlName(_class);
0239: else
0240: localName = xre.name();
0241:
0242: if (!"##default".equals(xre.namespace()))
0243: namespace = xre.namespace();
0244:
0245: if (namespace == null)
0246: _elementName = new QName(localName);
0247: else
0248: _elementName = new QName(namespace, localName);
0249:
0250: _context.addRootElement(this );
0251: }
0252:
0253: // order the elements, if specified
0254:
0255: XmlAccessOrder accessOrder = XmlAccessOrder.UNDEFINED;
0256:
0257: XmlAccessorOrder packageOrder = _package
0258: .getAnnotation(XmlAccessorOrder.class);
0259:
0260: XmlAccessorOrder classOrder = _class
0261: .getAnnotation(XmlAccessorOrder.class);
0262:
0263: if (packageOrder != null)
0264: accessOrder = packageOrder.value();
0265:
0266: if (classOrder != null)
0267: accessOrder = classOrder.value();
0268:
0269: // try property orders too
0270:
0271: XmlType xmlType = (XmlType) _class
0272: .getAnnotation(XmlType.class);
0273: HashMap<String, Integer> orderMap = null;
0274:
0275: if (xmlType != null
0276: && (xmlType.propOrder().length != 1 || !""
0277: .equals(xmlType.propOrder()[0]))) {
0278: // non-default propOrder
0279: orderMap = new HashMap<String, Integer>();
0280:
0281: for (int i = 0; i < xmlType.propOrder().length; i++)
0282: orderMap.put(xmlType.propOrder()[i], i);
0283: }
0284:
0285: // Collect the fields/properties of the class
0286: if (orderMap != null) {
0287: for (int i = 0; i < orderMap.size(); i++)
0288: _elementMappings.add(null);
0289: }
0290:
0291: XmlAccessorType accessorType = _class
0292: .getAnnotation(XmlAccessorType.class);
0293:
0294: XmlAccessType accessType = (accessorType == null ? XmlAccessType.PUBLIC_MEMBER
0295: : accessorType.value());
0296:
0297: if (accessType != XmlAccessType.FIELD) {
0298: // getter/setter
0299: TreeSet<Method> methodSet = new TreeSet<Method>(
0300: methodComparator);
0301:
0302: Method[] declared = _class.getDeclaredMethods();
0303:
0304: for (Method m : declared)
0305: methodSet.add(m);
0306:
0307: Method[] methods = new Method[methodSet.size()];
0308: methodSet.toArray(methods);
0309:
0310: AccessibleObject.setAccessible(methods, true);
0311:
0312: while (methodSet.size() > 0) {
0313: Method m = methodSet.first();
0314: methodSet.remove(m);
0315:
0316: String name = null;
0317: Method get = null;
0318: Method set = null;
0319:
0320: if (m.getName().startsWith("get")) {
0321: get = m;
0322:
0323: if (Void.TYPE.equals(get.getReturnType()))
0324: continue;
0325:
0326: name = get.getName().substring(3); // 3 == "get".length());
0327:
0328: Class cl = get.getDeclaringClass();
0329:
0330: try {
0331: set = cl.getDeclaredMethod("set" + name,
0332: get.getReturnType());
0333: } catch (NoSuchMethodException e) {
0334: continue;
0335: }
0336:
0337: if (!methodSet.remove(set))
0338: continue;
0339: } else if (m.getName().startsWith("set")) {
0340: set = m;
0341:
0342: Class[] parameterTypes = set
0343: .getParameterTypes();
0344:
0345: if (parameterTypes.length != 1)
0346: continue;
0347:
0348: name = set.getName().substring(3); // 3 == "set".length());
0349:
0350: Class cl = set.getDeclaringClass();
0351:
0352: try {
0353: get = cl.getDeclaredMethod("get" + name);
0354: } catch (NoSuchMethodException e) {
0355: continue;
0356: }
0357:
0358: if (!parameterTypes[0].equals(get
0359: .getReturnType()))
0360: continue;
0361:
0362: if (!methodSet.remove(get))
0363: continue;
0364: } else
0365: continue;
0366:
0367: name = Character.toLowerCase(name.charAt(0))
0368: + name.substring(1);
0369:
0370: // JAXB specifies that a "class" property must be specified as "clazz"
0371: // because of Object.getClass()
0372: if ("class".equals(name))
0373: continue;
0374:
0375: // XXX special cases for Throwable specified in JAX-WS
0376: // Should it be in the general JAXB?
0377: if (Throwable.class.isAssignableFrom(_class)
0378: && ("stackTrace".equals(name)
0379: || "cause".equals(name) || "localizedMessage"
0380: .equals(name)))
0381: continue;
0382:
0383: // XXX PUBLIC_MEMBER
0384:
0385: if (accessType == XmlAccessType.NONE
0386: && !JAXBUtil.isJAXBAnnotated(get)
0387: && !JAXBUtil.isJAXBAnnotated(set))
0388: continue;
0389:
0390: // jaxb/0456
0391: if (Modifier.isStatic(get.getModifiers())
0392: && JAXBUtil.isJAXBAnnotated(get))
0393: throw new JAXBException(
0394: L
0395: .l(
0396: "JAXB annotations cannot be applied to static methods: {0}",
0397: get));
0398:
0399: // jaxb/0457
0400: if (Modifier.isStatic(set.getModifiers())
0401: && JAXBUtil.isJAXBAnnotated(set))
0402: throw new JAXBException(
0403: L
0404: .l(
0405: "JAXB annotations cannot be applied to static methods: {0}",
0406: set));
0407:
0408: // jaxb/0374
0409: if (Modifier.isStatic(set.getModifiers())
0410: || Modifier.isStatic(get.getModifiers()))
0411: continue;
0412:
0413: if (get != null
0414: && get
0415: .isAnnotationPresent(XmlTransient.class))
0416: continue;
0417: if (set != null
0418: && set
0419: .isAnnotationPresent(XmlTransient.class))
0420: continue;
0421:
0422: get.setAccessible(true);
0423: set.setAccessible(true);
0424:
0425: Accessor a = new GetterSetterAccessor(name, get,
0426: set);
0427:
0428: if (orderMap != null) {
0429: Integer i = orderMap.remove(name);
0430:
0431: if (i != null)
0432: a.setOrder(i.intValue());
0433: // XXX else throw something?
0434: }
0435:
0436: processAccessor(a);
0437: }
0438: }
0439:
0440: if (accessType != XmlAccessType.PROPERTY) {
0441: // XXX Don't overwrite property accessors
0442: HashSet<Field> fieldSet = new HashSet<Field>();
0443:
0444: Field[] declared = _class.getDeclaredFields();
0445:
0446: for (Field f : declared)
0447: fieldSet.add(f);
0448:
0449: Field[] fields = new Field[fieldSet.size()];
0450: fieldSet.toArray(fields);
0451:
0452: AccessibleObject.setAccessible(fields, true);
0453:
0454: for (Field f : fields) {
0455: if (f.isAnnotationPresent(XmlLocation.class)) {
0456: if (!f.getType().equals(Location.class))
0457: throw new JAXBException(
0458: L
0459: .l("Fields annotated by @Location must have type javax.xml.stream.Location"));
0460:
0461: _locationAccessor = new FieldAccessor(f);
0462: }
0463:
0464: // special case: jaxb/0250
0465: // fields which are static are skipped _unless_ they are also
0466: // both final and attributes
0467: if (Modifier.isStatic(f.getModifiers())
0468: && !(Modifier.isFinal(f.getModifiers()) && f
0469: .isAnnotationPresent(XmlAttribute.class)))
0470: continue;
0471:
0472: if (f.isAnnotationPresent(XmlTransient.class))
0473: continue;
0474: // jaxb/0176: transient modifier ignored
0475:
0476: if (accessType == XmlAccessType.PUBLIC_MEMBER
0477: && !Modifier.isPublic(f.getModifiers())
0478: && !JAXBUtil.isJAXBAnnotated(f))
0479: continue;
0480:
0481: if (accessType == XmlAccessType.NONE
0482: && !JAXBUtil.isJAXBAnnotated(f))
0483: continue;
0484:
0485: Accessor a = new FieldAccessor(f);
0486:
0487: if (orderMap != null) {
0488: Integer i = orderMap.remove(f.getName());
0489:
0490: if (i != null)
0491: a.setOrder(i.intValue());
0492: // XXX else throw something?
0493: }
0494:
0495: processAccessor(a);
0496: }
0497: }
0498:
0499: // do ordering if necessary
0500: if (orderMap == null
0501: && accessOrder == XmlAccessOrder.ALPHABETICAL)
0502: Collections.sort(_elementMappings,
0503: XmlMapping.nameComparator);
0504: } catch (JAXBException e) {
0505: throw e;
0506: } catch (Exception e) {
0507: throw new JAXBException(L.l("{0}: Initialization error",
0508: _class.getName()), e);
0509: }
0510:
0511: if (!Object.class.equals(_class.getSuperclass()))
0512: _parent = _context.getSkeleton(_class.getSuperclass());
0513: }
0514:
0515: /**
0516: * Handles any processing that needs to happen after all ClassSkeletons
0517: * have been created and all classes have been discovered.
0518: **/
0519: public void postProcess() throws JAXBException {
0520: if (log.isLoggable(Level.FINEST))
0521: log.finest("JAXB: " + _class.getName() + " has children: ");
0522:
0523: for (int i = 0; i < _elementMappings.size(); i++)
0524: _elementMappings.get(i)
0525: .putQNames(_elementQNameToMappingMap);
0526: }
0527:
0528: /**
0529: * Create an XmlMapping for this accessor and insert it in the correct
0530: * mapping or field.
0531: **/
0532: private void processAccessor(Accessor accessor)
0533: throws JAXBException {
0534: XmlMapping mapping = XmlMapping.newInstance(_context, accessor);
0535:
0536: if (mapping instanceof XmlValueMapping) {
0537: if (_value != null)
0538: throw new JAXBException(
0539: L
0540: .l("Cannot have two @XmlValue annotated fields or properties"));
0541:
0542: if (_elementMappings.size() > 0) {
0543: // in case of propOrder & XmlValue
0544: if (_elementMappings.size() != 1
0545: || _elementMappings.get(0) != null)
0546: throw new JAXBException(
0547: L
0548: .l(
0549: "Cannot have both @XmlValue and elements in a JAXB element (e.g. {0})",
0550: _elementMappings.get(0)));
0551:
0552: _elementMappings.clear();
0553: }
0554:
0555: _value = (XmlValueMapping) mapping;
0556: } else if (mapping instanceof AttributeMapping) {
0557: mapping.putQNames(_attributeQNameToMappingMap);
0558: _attributeMappings.add((AttributeMapping) mapping);
0559: } else if (mapping instanceof AnyAttributeMapping) {
0560: if (_anyAttributeMapping != null)
0561: throw new JAXBException(
0562: L
0563: .l("Cannot have two fields or properties with @XmlAnyAttribute annotation"));
0564:
0565: _anyAttributeMapping = (AnyAttributeMapping) mapping;
0566: _attributeMappings.add(mapping);
0567: } else if ((mapping instanceof ElementMapping)
0568: || (mapping instanceof ElementRefMapping)
0569: || (mapping instanceof ElementsMapping)) {
0570: if (_value != null)
0571: throw new JAXBException(
0572: L
0573: .l(
0574: "{0}: Cannot have both @XmlValue and elements in a JAXB element",
0575: _class.getName()));
0576:
0577: if (mapping.getAccessor().getOrder() >= 0)
0578: _elementMappings.set(mapping.getAccessor().getOrder(),
0579: mapping);
0580: else
0581: _elementMappings.add(mapping);
0582: } else if (mapping instanceof AnyElementMapping) {
0583: if (_anyElementMapping != null)
0584: throw new JAXBException(
0585: L
0586: .l(
0587: "{0}: Cannot have two @XmlAnyElement annotations in a single class",
0588: _class.getName()));
0589:
0590: _anyElementMapping = (AnyElementMapping) mapping;
0591: } else {
0592: throw new RuntimeException(L.l("Unknown mapping type {0}",
0593: mapping.getClass()));
0594: }
0595: }
0596:
0597: private XmlMapping getElementMapping(QName q) throws JAXBException {
0598: XmlMapping mapping = _elementQNameToMappingMap.get(q);
0599:
0600: if (mapping != null)
0601: return mapping;
0602:
0603: if (_anyElementMapping != null)
0604: return _anyElementMapping;
0605:
0606: if (_parent != null)
0607: return _parent.getElementMapping(q);
0608:
0609: return null;
0610: }
0611:
0612: public XmlMapping getAttributeMapping(QName q) throws JAXBException {
0613: XmlMapping mapping = _attributeQNameToMappingMap.get(q);
0614:
0615: if (mapping != null)
0616: return mapping;
0617:
0618: if (_anyAttributeMapping != null)
0619: return _anyAttributeMapping;
0620:
0621: if (_parent != null)
0622: return _parent.getAttributeMapping(q);
0623:
0624: return null;
0625: }
0626:
0627: public QName getElementName() {
0628: if (_elementName != null)
0629: return _elementName;
0630: else
0631: return _typeName;
0632: }
0633:
0634: public void setElementName(QName elementName) {
0635: _elementName = elementName;
0636: }
0637:
0638: public QName getTypeName() {
0639: return _typeName;
0640: }
0641:
0642: public void setCreateMethod(Method createMethod, Object factory) {
0643: _createMethod = createMethod;
0644: _factory = factory;
0645: }
0646:
0647: public C newInstance() throws JAXBException {
0648: try {
0649: if (_createMethod != null && _factory != null) {
0650: return (C) _createMethod.invoke(_factory);
0651: } else {
0652: // XXX move into constructor
0653: XmlType xmlType = getXmlType();
0654:
0655: if (xmlType != null) {
0656: Class factoryClass = xmlType.factoryClass();
0657:
0658: if (xmlType.factoryClass() == XmlType.DEFAULT.class)
0659: factoryClass = _class;
0660:
0661: if (!"".equals(xmlType.factoryMethod())) {
0662: Method m = factoryClass.getMethod(xmlType
0663: .factoryMethod(), NO_PARAMS);
0664:
0665: if (!Modifier.isStatic(m.getModifiers()))
0666: throw new JAXBException(L
0667: .l("Factory method not static"));
0668:
0669: return (C) m.invoke(null);
0670: }
0671: }
0672:
0673: Constructor con = _class.getConstructor(NO_PARAMS);
0674:
0675: return (C) con.newInstance(NO_ARGS);
0676: }
0677: } catch (JAXBException e) {
0678: throw e;
0679: } catch (Exception e) {
0680: throw new JAXBException(e);
0681: }
0682: }
0683:
0684: public XmlType getXmlType() {
0685: return (XmlType) _class.getAnnotation(XmlType.class);
0686: }
0687:
0688: public Object read(Unmarshaller u, XMLStreamReader in)
0689: throws IOException, XMLStreamException, JAXBException {
0690: try {
0691: C ret = null;
0692:
0693: String nil = in.getAttributeValue(
0694: W3C_XML_SCHEMA_INSTANCE_NS_URI, "nil");
0695:
0696: if (!"true".equals(nil))
0697: ret = newInstance();
0698:
0699: if (_locationAccessor != null)
0700: _locationAccessor.set(ret, in.getLocation());
0701:
0702: if (_beforeUnmarshal != null)
0703: _beforeUnmarshal.invoke(ret, u, null);
0704:
0705: if (u.getListener() != null)
0706: u.getListener().beforeUnmarshal(ret, null);
0707:
0708: if (_value != null) {
0709: _value.read(u, in, ret, this );
0710: } else {
0711: // process the attributes
0712: for (int i = 0; i < in.getAttributeCount(); i++) {
0713: QName attributeName = in.getAttributeName(i);
0714: XmlMapping mapping = getAttributeMapping(attributeName);
0715:
0716: if (mapping == null)
0717: throw new UnmarshalException(L.l(
0718: "Attribute {0} not found in {1}",
0719: attributeName, getType()));
0720:
0721: mapping.readAttribute(in, i, ret);
0722: }
0723:
0724: int i = 0;
0725: in.nextTag();
0726:
0727: while (in.getEventType() == in.START_ELEMENT) {
0728: XmlMapping mapping = getElementMapping(in.getName());
0729:
0730: if (mapping == null) {
0731: throw new UnmarshalException(L.l(
0732: "Child <{0}> not found in {1}", in
0733: .getName(), getType()));
0734: }
0735:
0736: if (!mapping.getAccessor().checkOrder(i++,
0737: u.getEventHandler())) {
0738: throw new UnmarshalException(L.l(
0739: "Child <{0}> misordered in {1}", in
0740: .getName(), getType()));
0741: }
0742:
0743: mapping.read(u, in, ret);
0744: }
0745:
0746: // essentially a nextTag() that handles end of document gracefully
0747: while (in.hasNext()) {
0748: in.next();
0749:
0750: if (in.getEventType() == in.START_ELEMENT
0751: || in.getEventType() == in.END_ELEMENT)
0752: break;
0753: }
0754: }
0755:
0756: if (_afterUnmarshal != null)
0757: _afterUnmarshal.invoke(ret, u, null);
0758:
0759: if (u.getListener() != null)
0760: u.getListener().afterUnmarshal(ret, null);
0761:
0762: return ret;
0763: } catch (InvocationTargetException e) {
0764: if (e.getTargetException() != null)
0765: throw new UnmarshalException(e.getTargetException());
0766:
0767: throw new UnmarshalException(e);
0768: } catch (IllegalAccessException e) {
0769: throw new UnmarshalException(e);
0770: }
0771: }
0772:
0773: public Object bindFrom(BinderImpl binder, Object existing,
0774: NodeIterator node) throws IOException, JAXBException {
0775: Node root = node.getNode();
0776: C ret = (C) existing;
0777:
0778: if (ret == null)
0779: ret = newInstance();
0780:
0781: if (_value != null) {
0782: _value.bindFrom(binder, node, ret);
0783: } else {
0784: int i = 0;
0785: Node child = node.firstChild();
0786:
0787: while (child != null) {
0788: if (child.getNodeType() == Node.ELEMENT_NODE) {
0789: QName name = JAXBUtil.qnameFromNode(child);
0790:
0791: XmlMapping mapping = getElementMapping(name);
0792:
0793: if (mapping == null)
0794: throw new UnmarshalException(L.l(
0795: "Child <{0}> not found", name));
0796:
0797: if (!mapping.getAccessor().checkOrder(i++,
0798: binder.getEventHandler()))
0799: throw new UnmarshalException(L.l(
0800: "Child <{0}> misordered", name));
0801:
0802: mapping.bindFrom(binder, node, ret);
0803: }
0804:
0805: child = node.nextSibling();
0806: }
0807: }
0808:
0809: node.setNode(root);
0810: binder.bind(ret, root);
0811:
0812: return ret;
0813: }
0814:
0815: public void write(Marshaller m, XMLStreamWriter out, Object obj,
0816: Namer namer, ArrayList<XmlMapping> attributes)
0817: throws IOException, XMLStreamException, JAXBException {
0818: if (obj == null)
0819: return;
0820:
0821: try {
0822: if (_beforeMarshal != null)
0823: _beforeMarshal.invoke(obj, m);
0824:
0825: if (m.getListener() != null)
0826: m.getListener().beforeMarshal(obj);
0827:
0828: QName tagName = null;
0829:
0830: if (namer != null)
0831: tagName = namer.getQName(obj);
0832:
0833: if (tagName == null)
0834: tagName = _elementName;
0835:
0836: if (_value != null) {
0837: _value.setQName(tagName);
0838: _value.write(m, out, obj, _attributeMappings);
0839: } else {
0840: if (tagName.getNamespaceURI() == null
0841: || "".equals(tagName.getNamespaceURI()))
0842: out.writeStartElement(tagName.getLocalPart());
0843: else if (tagName.getPrefix() == null
0844: || "".equals(tagName.getPrefix()))
0845: out.writeStartElement(tagName.getNamespaceURI(),
0846: tagName.getLocalPart());
0847: else
0848: out.writeStartElement(tagName.getPrefix(), tagName
0849: .getLocalPart(), tagName.getNamespaceURI());
0850:
0851: if (attributes != null) {
0852: for (int i = 0; i < attributes.size(); i++)
0853: attributes.get(i).write(m, out, obj);
0854: }
0855:
0856: for (XmlMapping mapping : _attributeMappings)
0857: mapping.write(m, out, obj);
0858:
0859: for (XmlMapping mapping : _elementMappings)
0860: mapping.write(m, out, obj);
0861:
0862: if (_anyElementMapping != null) // XXX ordering!
0863: _anyElementMapping.write(m, out, obj);
0864:
0865: out.writeEndElement();
0866: }
0867:
0868: if (_afterMarshal != null)
0869: _afterMarshal.invoke(obj, m);
0870:
0871: if (m.getListener() != null)
0872: m.getListener().afterMarshal(obj);
0873: } catch (InvocationTargetException e) {
0874: throw new JAXBException(e);
0875: } catch (IllegalAccessException e) {
0876: throw new JAXBException(e);
0877: }
0878: }
0879:
0880: public Node bindTo(BinderImpl binder, Node node, Object obj,
0881: Namer namer, ArrayList<XmlMapping> attributes)
0882: throws IOException, JAXBException {
0883: if (obj == null)
0884: return null;
0885:
0886: QName tagName = null;
0887:
0888: if (namer != null)
0889: tagName = namer.getQName(obj);
0890:
0891: if (tagName == null)
0892: tagName = _elementName;
0893:
0894: if (_value != null) {
0895: Node newNode = _value.bindTo(binder, node, obj);
0896:
0897: if (newNode != node) {
0898: binder.invalidate(node);
0899: node = newNode;
0900: }
0901: } else {
0902: QName nodeName = JAXBUtil.qnameFromNode(node);
0903:
0904: if (tagName.equals(nodeName)) {
0905: Node child = node.getFirstChild();
0906:
0907: child = JAXBUtil.skipIgnorableNodes(child);
0908:
0909: if (attributes != null) {
0910: // XXX
0911: }
0912:
0913: for (XmlMapping mapping : _elementMappings) {
0914: if (child != null) {
0915: // try to reuse as many of the child nodes as possible
0916: Node newNode = mapping.bindTo(binder, child,
0917: obj);
0918:
0919: if (newNode != child) {
0920: node.replaceChild(newNode, child);
0921: binder.invalidate(child);
0922: child = newNode;
0923: }
0924:
0925: child = child.getNextSibling();
0926: child = JAXBUtil.skipIgnorableNodes(child);
0927: } else {
0928: Node newNode = JAXBUtil.elementFromQName(
0929: mapping.getQName(obj), node);
0930: node.appendChild(mapping.bindTo(binder,
0931: newNode, obj));
0932: }
0933: }
0934: } else {
0935: binder.invalidate(node);
0936:
0937: node = JAXBUtil.elementFromQName(tagName, node);
0938:
0939: for (XmlMapping mapping : _elementMappings) {
0940: Node child = JAXBUtil.elementFromQName(mapping
0941: .getQName(obj), node);
0942: node
0943: .appendChild(mapping.bindTo(binder, child,
0944: obj));
0945: }
0946: }
0947: }
0948:
0949: binder.bind(obj, node);
0950:
0951: return node;
0952: }
0953:
0954: public boolean isRootElement() {
0955: return _elementName != null;
0956: }
0957:
0958: public void generateSchema(XMLStreamWriter out)
0959: throws JAXBException, XMLStreamException {
0960: if (_elementName != null) {
0961:
0962: if ("".equals(_typeName.getLocalPart()))
0963: out.writeStartElement(XML_SCHEMA_PREFIX, "element",
0964: XML_SCHEMA_NS);
0965:
0966: else {
0967: out.writeEmptyElement(XML_SCHEMA_PREFIX, "element",
0968: XML_SCHEMA_NS);
0969: out.writeAttribute("type", _typeName.getLocalPart());
0970: }
0971:
0972: out.writeAttribute("name", _elementName.getLocalPart());
0973: }
0974:
0975: generateSchemaType(out);
0976:
0977: if (_elementName != null && "".equals(_typeName.getLocalPart()))
0978: out.writeEndElement(); // element
0979: }
0980:
0981: public void generateSchemaType(XMLStreamWriter out)
0982: throws JAXBException, XMLStreamException {
0983: if (_value != null) {
0984: out.writeStartElement(XML_SCHEMA_PREFIX, "simpleType",
0985: XML_SCHEMA_NS);
0986:
0987: if (!"".equals(_typeName.getLocalPart()))
0988: out.writeAttribute("name", _typeName.getLocalPart());
0989:
0990: if (Collection.class.isAssignableFrom(_value.getAccessor()
0991: .getType())) {
0992: out.writeEmptyElement(XML_SCHEMA_PREFIX, "list",
0993: XML_SCHEMA_NS);
0994:
0995: String itemType = StaxUtil.qnameToString(out, _value
0996: .getSchemaType());
0997:
0998: out.writeAttribute("itemType", itemType);
0999: } else {
1000: out.writeEmptyElement(XML_SCHEMA_PREFIX, "restriction",
1001: XML_SCHEMA_NS);
1002:
1003: String base = StaxUtil.qnameToString(out, _value
1004: .getSchemaType());
1005:
1006: out.writeAttribute("base", base);
1007: }
1008:
1009: for (XmlMapping mapping : _attributeMappings)
1010: mapping.generateSchema(out);
1011:
1012: out.writeEndElement(); // simpleType
1013: } else {
1014: out.writeStartElement(XML_SCHEMA_PREFIX, "complexType",
1015: XML_SCHEMA_NS);
1016:
1017: if (Modifier.isAbstract(_class.getModifiers()))
1018: out.writeAttribute("abstract", "true");
1019:
1020: if (!"".equals(_typeName.getLocalPart()))
1021: out.writeAttribute("name", _typeName.getLocalPart());
1022:
1023: out.writeStartElement(XML_SCHEMA_PREFIX, "sequence",
1024: XML_SCHEMA_NS);
1025:
1026: for (XmlMapping mapping : _elementMappings)
1027: mapping.generateSchema(out);
1028:
1029: if (_anyElementMapping != null)
1030: _anyElementMapping.generateSchema(out);
1031:
1032: out.writeEndElement(); // sequence
1033:
1034: for (XmlMapping mapping : _attributeMappings)
1035: mapping.generateSchema(out);
1036:
1037: out.writeEndElement(); // complexType
1038: }
1039: }
1040:
1041: //XXX The TreeSet needs this for some reason
1042: private static final Comparator methodComparator = new java.util.Comparator<Method>() {
1043: public int compare(Method m1, Method m2) {
1044: return m1.toGenericString().compareTo(m2.toGenericString());
1045: }
1046:
1047: public boolean equals(Object obj) {
1048: return obj == this;
1049: }
1050: };
1051: }
|