0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.xtest.xmlserializer;
0043:
0044: import org.netbeans.xtest.util.XMLFactoryUtil;
0045: import org.netbeans.xtest.xmlserializer.ClassMappingRegistry.ContainerFieldMapping;
0046:
0047: import java.lang.reflect.Array;
0048: import java.lang.reflect.Field;
0049: import java.math.BigDecimal;
0050: import java.math.BigInteger;
0051: import java.sql.Date;
0052: import java.sql.Timestamp;
0053: import java.util.ArrayList;
0054: import java.util.Collection;
0055: import java.util.HashMap;
0056: import java.util.Iterator;
0057:
0058: // XML DOM imports
0059: import javax.xml.parsers.ParserConfigurationException;
0060: import org.w3c.dom.*;
0061:
0062: public class XMLSerializer {
0063:
0064: public static final int ALL = -1;
0065:
0066: // private contructor - this class cannot be instintiated
0067: private XMLSerializer() {
0068: }
0069:
0070: // return true if the argument class is XML serializable
0071: public static boolean isClassSerializable(Class clazz) {
0072: // is the class XML serializable
0073: if (XMLSerializable.class.isAssignableFrom(clazz)) {
0074: return true;
0075: }
0076:
0077: for (int i = 0; i < SERIALIZABLE_CLASSES.length; i++) {
0078: if (SERIALIZABLE_CLASSES[i].getName().equals(
0079: clazz.getName())) {
0080: return true;
0081: }
0082: }
0083: // class was not found as a serializable
0084: return false;
0085: }
0086:
0087: /*
0088: * returns true if given class implements
0089: * XMLBean interface
0090: */
0091: public static boolean implements XMLSerializable(Class clazz) {
0092: Class[] interfaces = clazz.getInterfaces();
0093: for (int i = 0; i < interfaces.length; i++) {
0094: if (interfaces[i].equals(XMLSerializable.class)) {
0095: return true;
0096: }
0097: }
0098: return false;
0099: }
0100:
0101: // serialize to DOM document
0102: public static Document toDOMDocument(XMLSerializable object)
0103: throws XMLSerializeException, ParserConfigurationException {
0104: return toDOMDocument(object, ALL);
0105: }
0106:
0107: // serialize to DOM document
0108: public static Document toDOMDocument(XMLSerializable object,
0109: int depth) throws XMLSerializeException,
0110: ParserConfigurationException {
0111: Document doc = XMLFactoryUtil.newDocumentBuilder()
0112: .newDocument();
0113: doc.appendChild(toDOMElement(object, doc, depth));
0114: return doc;
0115: }
0116:
0117: // serialize to DOM element
0118: public static Element toDOMElement(XMLSerializable object,
0119: Document doc, int depth) throws XMLSerializeException {
0120:
0121: String elementName = GlobalMappingRegistry
0122: .getElementNameForClass(object.getClass());
0123:
0124: if (elementName == null) {
0125: // cannot find element name - cannot serialize this bean
0126: // throw exception
0127: throw new XMLSerializeException(
0128: "Root element corresponding to "
0129: + object.getClass()
0130: + " is not registered. Cannot serialize to XML.");
0131: }
0132:
0133: Element element = doc.createElement(elementName);
0134: appendToDOMElement(doc, element, object, depth);
0135:
0136: return element;
0137: }
0138:
0139: // serialize from DOM document
0140: public static XMLSerializable getXMLSerializable(Document doc)
0141: throws XMLSerializeException {
0142: return getXMLSerializable(doc, ALL);
0143: }
0144:
0145: // serialize from DOM document
0146: public static XMLSerializable getXMLSerializable(Document doc,
0147: int depth) throws XMLSerializeException {
0148: Element element = doc.getDocumentElement();
0149: return getXMLSerializable(element, depth);
0150: }
0151:
0152: // serialize from DOM element
0153: public static XMLSerializable getXMLSerializable(Element element,
0154: int depth) throws XMLSerializeException {
0155: // if depth is zero, we don't want dig deeper
0156: if (depth == 0) {
0157: // there is nothing to be done ...
0158: return null;
0159: }
0160: // otherwise continue with getting the bean
0161: String elementName = element.getTagName();
0162: // find a class to be loaded from this element
0163: Class elementClass = GlobalMappingRegistry
0164: .getClassForElementName(elementName);
0165: if (elementClass == null) {
0166: throw new XMLSerializeException(
0167: "Cannot find registered class for element "
0168: + elementName);
0169: }
0170: // process the class
0171: XMLSerializable xmlSerializable = processXMLSerializableClass(
0172: element, elementClass, depth);
0173: if (xmlSerializable instanceof Validation) {
0174: ((Validation) xmlSerializable).validate();
0175: }
0176: if (xmlSerializable instanceof PostInitialization) {
0177: ((PostInitialization) xmlSerializable).postInitialize();
0178: }
0179: return xmlSerializable;
0180: }
0181:
0182: //
0183: // private constants
0184: //
0185:
0186: // list of classes directly (no subclasses)
0187: // serializable by xmlserializable package
0188: private static final Class[] SERIALIZABLE_CLASSES = {
0189: // primitive numbers
0190: Byte.TYPE, Short.TYPE,
0191: Integer.TYPE,
0192: Long.TYPE,
0193: Float.TYPE,
0194: Double.TYPE,
0195: // other primitives
0196: Boolean.TYPE,
0197: Character.TYPE,
0198: // number classes
0199: Byte.class, Short.class, Integer.class, Long.class,
0200: Float.class, Double.class, BigInteger.class,
0201: BigDecimal.class,
0202: // other classes
0203: Boolean.class, Character.class, String.class, Date.class,
0204: Timestamp.class };
0205:
0206: //
0207: // private methods - serialize to XML
0208: //
0209:
0210: private static void appendToDOMElement(Document doc,
0211: Element element, XMLSerializable object, int depth)
0212: throws XMLSerializeException {
0213:
0214: // check the depth
0215: if (depth == 0) {
0216: // return , there is nothing to do
0217: return;
0218: }
0219:
0220: // get the registered field names for this object
0221: ClassMappingRegistry registry = object.registerXMLMapping();
0222: Class clazz = object.getClass();
0223: ClassMappingRegistry.FieldMapping[] fieldMappings = registry
0224: .getFieldMappingsFromRegistry();
0225:
0226: for (int i = 0; i < fieldMappings.length; i++) {
0227:
0228: Field mappedField = fieldMappings[i].getField();
0229: mappedField.setAccessible(true);
0230:
0231: try {
0232: // get the value of the mapped field
0233: Object fieldValue = mappedField.get(object);
0234:
0235: if (fieldValue != null) {
0236: String xmlName = fieldMappings[i].getXMLName();
0237: if (fieldMappings[i].isSimple()) {
0238: // simple field serialization
0239: serializeSimpleField(
0240: doc,
0241: element,
0242: (ClassMappingRegistry.SimpleFieldMapping) fieldMappings[i],
0243: fieldValue, depth);
0244:
0245: } else if (fieldMappings[i].isContainer()) {
0246: // container field serialization
0247: serializeContainerField(
0248: doc,
0249: element,
0250: (ClassMappingRegistry.ContainerFieldMapping) fieldMappings[i],
0251: fieldValue, depth);
0252: }
0253: } // fieldValue != null
0254: } catch (IllegalAccessException iae) {
0255: throw new XMLSerializeException("Cannot access field "
0256: + mappedField.getName() + " in class "
0257: + clazz.getName(), iae);
0258: }
0259: } // for cycle
0260: }
0261:
0262: // simple field type serializing method
0263: private static void serializeSimpleField(Document doc,
0264: Element element,
0265: ClassMappingRegistry.SimpleFieldMapping fieldMapping,
0266: Object fieldValue, int depth) throws XMLSerializeException {
0267: switch (fieldMapping.getXMLType()) {
0268: case ClassMappingRegistry.ATTRIBUTE:
0269: insertAttribute(element, fieldMapping.getXMLName(),
0270: fieldValue);
0271: break;
0272:
0273: case ClassMappingRegistry.ELEMENT:
0274: insertElement(doc, element, fieldMapping.getXMLName(),
0275: fieldValue, depth);
0276: break;
0277:
0278: case ClassMappingRegistry.PCDATA:
0279: insertPCDATA(doc, element, fieldValue);
0280: break;
0281:
0282: case ClassMappingRegistry.CDATA:
0283: insertCDATA(doc, element, fieldValue);
0284: break;
0285:
0286: default:
0287: throw new XMLSerializeException(
0288: "Unknown xml mapping type - should not happen");
0289: }
0290: }
0291:
0292: // container field type serializing method
0293: private static void serializeContainerField(
0294: Document doc,
0295: Element element,
0296: ClassMappingRegistry.ContainerFieldMapping containerMapping,
0297: Object fieldValue, int depth) throws XMLSerializeException {
0298: String containerRootElementName = containerMapping.getXMLName();
0299: switch (containerMapping.getContainerMappingType()) {
0300: case ClassMappingRegistry.DIRECT:
0301:
0302: if (fieldValue.getClass().isArray()) {
0303:
0304: int arraySize = Array.getLength(fieldValue);
0305: for (int i = 0; i < arraySize; i++) {
0306: Object oneItem = Array.get(fieldValue, i);
0307: if (oneItem != null) {
0308: insertElement(doc, element,
0309: containerRootElementName, oneItem,
0310: depth);
0311: }
0312: }
0313:
0314: } else if (Collection.class.isAssignableFrom(fieldValue
0315: .getClass())) {
0316:
0317: Iterator i = ((Collection) fieldValue).iterator();
0318: while (i.hasNext()) {
0319: Object oneItem = i.next();
0320: if (oneItem != null) {
0321: insertElement(doc, element,
0322: containerRootElementName, oneItem,
0323: depth);
0324: }
0325: }
0326:
0327: }
0328:
0329: break;
0330: case ClassMappingRegistry.SUBELEMENT:
0331: Element containerRootElement = doc
0332: .createElement(containerRootElementName);
0333:
0334: if (fieldValue.getClass().isArray()) {
0335:
0336: int arraySize = Array.getLength(fieldValue);
0337: for (int i = 0; i < arraySize; i++) {
0338: Object oneItem = Array.get(fieldValue, i);
0339: if (oneItem != null) {
0340: // find record in the containerMappingRegistry
0341: String subElementName = containerMapping
0342: .getElementNameForClass(oneItem
0343: .getClass());
0344: if (subElementName != null) {
0345: insertElement(doc, containerRootElement,
0346: subElementName, oneItem, depth);
0347: } else {
0348: throw new XMLSerializeException(
0349: "Cannot find appropriate mapping for class "
0350: + oneItem.getClass()
0351: .getName()
0352: + " when serializing container field "
0353: + containerMapping
0354: .getField()
0355: .getName());
0356:
0357: }
0358: }
0359: }
0360:
0361: } else if (Collection.class.isAssignableFrom(fieldValue
0362: .getClass())) {
0363:
0364: Iterator i = ((Collection) fieldValue).iterator();
0365: while (i.hasNext()) {
0366: Object oneItem = i.next();
0367: if (oneItem != null) {
0368: String subElementName = containerMapping
0369: .getElementNameForClass(oneItem
0370: .getClass());
0371: if (subElementName != null) {
0372: insertElement(doc, containerRootElement,
0373: subElementName, oneItem, depth);
0374: } else {
0375: throw new XMLSerializeException(
0376: "Cannot find appropriate mapping for class "
0377: + oneItem.getClass()
0378: .getName()
0379: + " when serializing container field "
0380: + containerMapping
0381: .getField()
0382: .getName());
0383: }
0384: }
0385: }
0386: }
0387: element.appendChild(containerRootElement);
0388: break;
0389: default:
0390: throw new XMLSerializeException(
0391: "Unknown container mapping type - should not happen");
0392: } // switch
0393: }
0394:
0395: // serialize attribute
0396: private static void insertAttribute(Element element,
0397: String attributeName, Object attributeValue)
0398: throws XMLSerializeException {
0399: element.setAttribute(attributeName, attributeValue.toString());
0400: }
0401:
0402: // serialize element (only a single element)
0403: private static void insertElement(Document doc, Element element,
0404: String elementName, Object elementValue, int depth)
0405: throws XMLSerializeException {
0406: Element newElement = doc.createElement(elementName);
0407: element.appendChild(newElement);
0408: // check whether elementValue is either collection or array
0409:
0410: if (implements XMLSerializable(elementValue.getClass())) {
0411: // recursive iteration
0412: appendToDOMElement(doc, newElement,
0413: (XMLSerializable) elementValue, depth - 1);
0414: } else {
0415: // just save the field value in the element
0416: Text textNode = doc.createTextNode(elementValue.toString());
0417: newElement.appendChild(textNode);
0418: }
0419: }
0420:
0421: // serialize PCDATA
0422: private static void insertPCDATA(Document doc, Element element,
0423: Object pcdataValue) {
0424: Text aText = doc.createTextNode(pcdataValue.toString());
0425: element.appendChild(aText);
0426: }
0427:
0428: // serialize CDATA
0429: private static void insertCDATA(Document doc, Element element,
0430: Object cdataValue) {
0431: CDATASection cdataSection = doc.createCDATASection(cdataValue
0432: .toString());
0433: element.appendChild(cdataSection);
0434: }
0435:
0436: //
0437: // private methods - deserialize from XML
0438: //
0439:
0440: /**
0441: * Method processXMLSerializable.
0442: * @param xmlSerializable
0443: * @param element
0444: * @param depth
0445: */
0446:
0447: private static XMLSerializable processXMLSerializableClass(
0448: Element element, Class clazz, int depth)
0449: throws XMLSerializeException {
0450: // instintiate this class
0451: try {
0452: /* Constructor aConstructor = clazz.getConstructor();
0453: aConstructor.setAccessible(true);
0454: XMLSerializable xmlSerializable = (XMLSerializable) aConstructor.newInstance(null);
0455: **/
0456: XMLSerializable xmlSerializable = (XMLSerializable) clazz
0457: .newInstance();
0458: // now work on the rest of the XMLSerializable object
0459: processXMLSerializable(xmlSerializable, element, depth);
0460: return xmlSerializable;
0461: } catch (InstantiationException ie) {
0462: throw new XMLSerializeException("Cannot instintiate class "
0463: + clazz.getName(), ie);
0464: } catch (IllegalAccessException iae) {
0465: throw new XMLSerializeException("Cannot instintiate class "
0466: + clazz.getName() + " - illegal access", iae);
0467: }
0468: }
0469:
0470: private static void processXMLSerializable(
0471: XMLSerializable xmlSerializable, Element element, int depth)
0472: throws XMLSerializeException {
0473: // start with attributes
0474: NamedNodeMap attributes = element.getAttributes();
0475: if (attributes != null) {
0476: processAttributes(xmlSerializable, attributes);
0477: }
0478: // continue with elements/PCDATA/CDATA
0479: NodeList subElements = element.getChildNodes();
0480: if (subElements != null) {
0481: FieldObjectsMap fieldObjectsMap = new FieldObjectsMap();
0482: for (int i = 0; i < subElements.getLength(); i++) {
0483: Node elementNode = subElements.item(i);
0484: switch (elementNode.getNodeType()) {
0485: case Node.ELEMENT_NODE:
0486: // process subelement
0487: processElement(xmlSerializable,
0488: (Element) elementNode, fieldObjectsMap,
0489: depth);
0490: break;
0491: case Node.TEXT_NODE:
0492: // process textnode (PCDATA)
0493: processPCDATA(xmlSerializable, elementNode);
0494: break;
0495: case Node.CDATA_SECTION_NODE:
0496: // process CDATA
0497: processCDATA(xmlSerializable, elementNode);
0498: break;
0499: default:
0500: // cannot recoginze the rest of nodes
0501: // currently do nothing
0502: // or may be better throw an exception
0503: }
0504: } // end for
0505: // fill the container field with gathered objects
0506: Field[] processedFields = fieldObjectsMap.getFields();
0507: for (int i = 0; i < processedFields.length; i++) {
0508: Object[] objects = fieldObjectsMap
0509: .getObjectInstances(processedFields[i]);
0510: try {
0511: insertObjectsIntoArray(xmlSerializable,
0512: processedFields[i], objects);
0513: } catch (IllegalAccessException iae) {
0514: throw new XMLSerializeException(
0515: "Caught IllegalAccessException when setting objects into array");
0516: }
0517: }
0518: }
0519: }
0520:
0521: /**
0522: * Method insertObjectsIntoArray.
0523: * @param xmlSerializable
0524: * @param field
0525: * @param objects
0526: */
0527: private static void insertObjectsIntoArray(
0528: XMLSerializable xmlSerializable, Field arrayField,
0529: Object[] objects) throws IllegalAccessException {
0530: Class arrayComponentClass = arrayField.getType()
0531: .getComponentType();
0532: Object resultingArray = Array.newInstance(arrayComponentClass,
0533: objects.length);
0534: for (int i = 0; i < objects.length; i++) {
0535: Object object = objects[i];
0536: if (object instanceof XMLPrimitiveWrapper) {
0537: ((XMLPrimitiveWrapper) object).setItemInArray(
0538: resultingArray, i);
0539: } else {
0540: // set the field alone
0541: Array.set(resultingArray, i, object);
0542: }
0543: }
0544: // now the array is done -> set it to the field
0545: arrayField.setAccessible(true);
0546: arrayField.set(xmlSerializable, resultingArray);
0547: }
0548:
0549: // process PCDATA
0550: private static void processPCDATA(XMLSerializable xmlSerializable,
0551: Node pcdata) throws XMLSerializeException {
0552: String value = pcdata.getNodeValue();
0553: if (value != null) {
0554: String trimmedValue = value.trim();
0555: if (trimmedValue.length() > 0) {
0556: ClassMappingRegistry registry = xmlSerializable
0557: .registerXMLMapping();
0558: // need to do
0559: throw new UnsupportedOperationException(
0560: "processPCDATA - not yet implemented:'"
0561: + pcdata.getNodeValue() + "'");
0562: }
0563: }
0564: }
0565:
0566: // process CDATA
0567: private static void processCDATA(XMLSerializable xmlSerializable,
0568: Node cdata) throws XMLSerializeException {
0569: throw new UnsupportedOperationException(
0570: "processCDATA - not yet implemented");
0571: }
0572:
0573: // process elements
0574: private static void processElement(XMLSerializable xmlSerializable,
0575: Element element, FieldObjectsMap fieldObjectsMap, int depth)
0576: throws XMLSerializeException {
0577: ClassMappingRegistry registry = xmlSerializable
0578: .registerXMLMapping();
0579: String elementName = element.getNodeName();
0580:
0581: ClassMappingRegistry.FieldMapping fieldMapping = registry
0582: .getFieldMappingFromRegistry(elementName);
0583: if (fieldMapping == null) {
0584: // no field mapping found !!!
0585: throw new XMLSerializeException(
0586: "Cannot find appropriate mapping for element "
0587: + elementName + " in class "
0588: + xmlSerializable.getClass().getName());
0589: }
0590: if (fieldMapping.getXMLType() != ClassMappingRegistry.ELEMENT) {
0591: throw new XMLSerializeException("Field "
0592: + fieldMapping.getField().getName() + " in class "
0593: + xmlSerializable.getClass().getName()
0594: + " is not registered as xml element");
0595: }
0596:
0597: if (fieldMapping.isSimple()) {
0598: // field is simple - load the simple field
0599: loadSimpleField(
0600: element,
0601: (ClassMappingRegistry.SimpleFieldMapping) fieldMapping,
0602: xmlSerializable, depth);
0603: } else if (fieldMapping.isContainer()) {
0604: // field is complex - load the complex field
0605: ClassMappingRegistry.ContainerFieldMapping containerMapping = (ClassMappingRegistry.ContainerFieldMapping) fieldMapping;
0606: loadContainerField(element, containerMapping,
0607: xmlSerializable, fieldObjectsMap, depth);
0608: } else {
0609: // this should not happeen !!!!
0610: throw new XMLSerializeException(
0611: "Unknown fieldMapping class:"
0612: + fieldMapping.getClass().getName()
0613: + " - should not happen");
0614: }
0615: }
0616:
0617: // process attributes
0618: private static void processAttributes(
0619: XMLSerializable xmlSerializable, NamedNodeMap attributes)
0620: throws XMLSerializeException {
0621: ClassMappingRegistry registry = xmlSerializable
0622: .registerXMLMapping();
0623: for (int i = 0; i < attributes.getLength(); i++) {
0624: Node attribute = attributes.item(i);
0625: String attributeName = attribute.getNodeName();
0626: // do we have such a attribute in XMLSerializable registry
0627: ClassMappingRegistry.FieldMapping fieldMapping = registry
0628: .getFieldMappingFromRegistry(attributeName);
0629: if (fieldMapping == null) {
0630: // no field mapping found !!!
0631: throw new XMLSerializeException(
0632: "Cannot find appropriate mapping for attribute "
0633: + attributeName + " in class"
0634: + xmlSerializable.getClass().getName());
0635: }
0636: if (fieldMapping.getXMLType() != ClassMappingRegistry.ATTRIBUTE) {
0637: throw new XMLSerializeException("Field "
0638: + fieldMapping.getField().getName()
0639: + " in class "
0640: + xmlSerializable.getClass().getName()
0641: + " is not registered as xml attribute");
0642: }
0643: // everything looks ok - get the value
0644: String value = attribute.getNodeValue();
0645: // set the field in the object with obtained value
0646: if (value != null) {
0647: try {
0648: XMLPrimitiveWrapper xmlPrimitive = new XMLPrimitiveWrapper(
0649: fieldMapping.getField().getType(), value);
0650: xmlPrimitive.setFieldInObject(fieldMapping
0651: .getField(), xmlSerializable);
0652: } catch (IllegalAccessException iae) {
0653: throw new XMLSerializeException("Cannot set field "
0654: + fieldMapping.getField().getName()
0655: + " with value " + value + " in class "
0656: + xmlSerializable.getClass().getName(), iae);
0657: } catch (IllegalArgumentException iae) {
0658: throw new XMLSerializeException("Cannot set field "
0659: + fieldMapping.getField().getName()
0660: + " with value " + value + " in class "
0661: + xmlSerializable.getClass().getName(), iae);
0662: }
0663: }
0664: }
0665: }
0666:
0667: // fill in simple field with value of pcdata from this element
0668: private static void loadSimpleField(Element parentElement,
0669: ClassMappingRegistry.SimpleFieldMapping mapping,
0670: XMLSerializable parentObject, int depth)
0671: throws XMLSerializeException {
0672: Field mappedField = mapping.getField();
0673: Class fieldClass = mappedField.getType();
0674: String elementName = parentElement.getNodeName();
0675: if (XMLSerializable.class.isAssignableFrom(fieldClass)) {
0676: // xmlserializable class
0677: XMLSerializable xmlSerializable = processXMLSerializableClass(
0678: parentElement, fieldClass, depth);
0679: try {
0680: mappedField.setAccessible(true);
0681: mappedField.set(parentObject, xmlSerializable);
0682: return;
0683: } catch (IllegalAccessException iae) {
0684: throw new XMLSerializeException("Cannot load field "
0685: + mappedField.getName() + "in class "
0686: + parentObject.getClass().getName()
0687: + " - illegal access", iae);
0688: }
0689: } else if (isClassSerializable(fieldClass)) {
0690: // primitive class - get PCDATA child node
0691: NodeList children = parentElement.getChildNodes();
0692: if (children != null) {
0693: if (children.getLength() == 1) {
0694: Node pcData = children.item(0);
0695: if (pcData.getNodeType() == Node.TEXT_NODE) {
0696: // get the value and insert in the field
0697: String value = pcData.getNodeValue();
0698: if (value != null) {
0699: try {
0700: XMLPrimitiveWrapper xmlPrimitive = new XMLPrimitiveWrapper(
0701: mappedField.getType(), value);
0702: xmlPrimitive.setFieldInObject(
0703: mappedField, parentObject);
0704: return;
0705: } catch (IllegalAccessException iae) {
0706: throw new XMLSerializeException(
0707: "Cannot load field "
0708: + mappedField.getName()
0709: + "in class "
0710: + parentObject
0711: .getClass()
0712: .getName()
0713: + " - illegal access",
0714: iae);
0715: }
0716: }
0717: }
0718: }
0719: }
0720: // thereis something wrong
0721: throw new XMLSerializeException(
0722: "Unrecoverable problem when deserializing element "
0723: + elementName + " to field "
0724: + mappedField.getName() + " in class "
0725: + fieldClass.getClass().getName());
0726: } else {
0727: // cannot deserialize class
0728: throw new XMLSerializeException("Field "
0729: + mappedField.getName() + "is of class "
0730: + fieldClass.getName()
0731: + " which cannot be xml serialized ");
0732: }
0733: }
0734:
0735: private static Object loadSimpleField(Element element,
0736: Class elementClass, int depth) throws XMLSerializeException {
0737: if (XMLSerializable.class.isAssignableFrom(elementClass)) {
0738: // xmlserializable class
0739: return processXMLSerializableClass(element, elementClass,
0740: depth);
0741: } else if (isClassSerializable(elementClass)) {
0742: // primitive class - get PCDATA child node
0743: NodeList children = element.getChildNodes();
0744: if (children != null) {
0745: if (children.getLength() == 1) {
0746: Node pcData = children.item(0);
0747: if (pcData.getNodeType() == Node.TEXT_NODE) {
0748: // get the value and insert in the field
0749: String value = pcData.getNodeValue();
0750: if (value != null) {
0751: try {
0752: return new XMLPrimitiveWrapper(
0753: elementClass, value);
0754: } catch (IllegalArgumentException iae) {
0755: throw new XMLSerializeException(
0756: "Cannot create XMLPrimitiveWrapper for class "
0757: + elementClass
0758: .getName()
0759: + " and value "
0760: + value
0761: + " - NumberFormatException",
0762: iae);
0763: }
0764: }
0765: }
0766: }
0767: }
0768: // thereis something wrong
0769: throw new XMLSerializeException(
0770: "Unrecoverable problem when deserializing element "
0771: + element + " to class "
0772: + elementClass.getName());
0773: } else {
0774: // cannot deserialize class
0775: throw new XMLSerializeException("Class "
0776: + elementClass.getName()
0777: + " is not supported for XML serialization");
0778: }
0779: }
0780:
0781: /**
0782: * Method loadContainerField.
0783: * @param element
0784: * @param containerMapping
0785: * @param xmlSerializable
0786: * @param depth
0787: */
0788: private static void loadContainerField(Element parentElement,
0789: ContainerFieldMapping containerMapping,
0790: XMLSerializable xmlSerializable,
0791: FieldObjectsMap fieldObjectsMap, int depth)
0792: throws XMLSerializeException {
0793: Field field = containerMapping.getField();
0794: Class fieldClass = field.getType();
0795:
0796: switch (containerMapping.getContainerMappingType()) {
0797: case ClassMappingRegistry.DIRECT:
0798: if (fieldClass.isArray()) {
0799: Class componentClass = fieldClass.getComponentType();
0800: Object object;
0801: if (XMLSerializable.class
0802: .isAssignableFrom(componentClass)) {
0803: object = processXMLSerializableClass(parentElement,
0804: componentClass, depth);
0805: } else {
0806: object = loadSimpleField(parentElement,
0807: componentClass, depth);
0808: }
0809: fieldObjectsMap.addObjectInstance(field, object);
0810: } else if (Collection.class.isAssignableFrom(fieldClass)) {
0811: throw new UnsupportedOperationException(
0812: "direct mapping collection - not yet implemented");
0813: } else {
0814: //this should neve happend ....
0815: throw new XMLSerializeException(
0816: "Field contains unrecognized type");
0817: }
0818: ;
0819: break;
0820: case ClassMappingRegistry.SUBELEMENT:
0821: if (fieldClass.isArray()) {
0822: Class componentClass = fieldClass.getComponentType();
0823: NodeList children = parentElement.getChildNodes();
0824: if (children != null) {
0825: int length = children.getLength();
0826: // create the array
0827: Object array = Array.newInstance(componentClass,
0828: length);
0829: // insert objects
0830: for (int i = 0; i < children.getLength(); i++) {
0831: Node childElement = children.item(i);
0832: if (childElement.getNodeType() == Node.ELEMENT_NODE) {
0833: Object object;
0834: if (XMLSerializable.class
0835: .isAssignableFrom(componentClass)) {
0836: object = processXMLSerializableClass(
0837: (Element) childElement,
0838: componentClass, depth);
0839: } else {
0840: object = loadSimpleField(
0841: (Element) childElement,
0842: componentClass, depth);
0843: }
0844: fieldObjectsMap.addObjectInstance(field,
0845: object);
0846: } else {
0847: // other node types are ignored ....
0848: }
0849: }
0850: } else {
0851: // nothing to do - may throw exception
0852: }
0853: // for each subelement create
0854: } else if (Collection.class.isAssignableFrom(fieldClass)) {
0855: throw new UnsupportedOperationException(
0856: "subelement mapping collection - not yet implemented");
0857: } else {
0858: //this should neve happend ....
0859: throw new XMLSerializeException(
0860: "Field contains unrecognized type");
0861: }
0862: break;
0863: }
0864:
0865: }
0866:
0867: // helper class for hodling element - xmlserializable objects
0868: private static class FieldObjectsMap {
0869:
0870: private HashMap map;
0871:
0872: public FieldObjectsMap() {
0873: map = new HashMap();
0874: }
0875:
0876: public void addObjectInstance(Field field, Object objectInstance) {
0877: ArrayList objects = (ArrayList) map.get(field);
0878: if (objects != null) {
0879: objects.add(objectInstance);
0880: } else {
0881: objects = new ArrayList();
0882: objects.add(objectInstance);
0883: map.put(field, objects);
0884: }
0885: }
0886:
0887: public Object[] getObjectInstances(Field field) {
0888: ArrayList objectInstances = (ArrayList) map.get(field);
0889: if (objectInstances != null) {
0890: return objectInstances.toArray();
0891: } else {
0892: return null;
0893: }
0894: }
0895:
0896: public Field[] getFields() {
0897: return (Field[]) (map.keySet().toArray(new Field[0]));
0898: }
0899:
0900: public boolean removeField(Field field) {
0901: if (map.containsKey(field)) {
0902: if (map.remove(field) != null) {
0903: return true;
0904: }
0905: }
0906: return false;
0907: }
0908:
0909: }
0910:
0911: static class XMLPrimitiveWrapper {
0912:
0913: private Class primitiveType;
0914:
0915: private byte xmlByte;
0916: private short xmlShort;
0917: private int xmlInt;
0918: private long xmlLong;
0919: private float xmlFloat;
0920: private double xmlDouble;
0921: private boolean xmlBoolean;
0922: private char xmlChar;
0923: private Object xmlObject;
0924:
0925: // forbidden no argument constructor
0926: private XMLPrimitiveWrapper() {
0927: }
0928:
0929: // constructor from class/string value pair
0930: public XMLPrimitiveWrapper(Class primitiveType, String value) {
0931: /*
0932: if (!primitiveType.isPrimitive()) {
0933: // throw an exception - not a primitive
0934: throw new IllegalArgumentException("supplied class :"+primitiveType.getName()+" is not of a primitive type");
0935: }
0936: */
0937: this .primitiveType = primitiveType;
0938: String typeName = primitiveType.getName();
0939:
0940: // java primitives
0941: if (typeName.equals("byte")) {
0942: xmlByte = Byte.parseByte(value);
0943: } else if (typeName.equals("short")) {
0944: xmlShort = Short.parseShort(value);
0945: } else if (typeName.equals("int")) {
0946: xmlInt = Integer.parseInt(value);
0947: } else if (typeName.equals("long")) {
0948: xmlLong = Long.parseLong(value);
0949: } else if (typeName.equals("float")) {
0950: xmlFloat = Float.parseFloat(value);
0951: } else if (typeName.equals("double")) {
0952: xmlDouble = Double.parseDouble(value);
0953: } else if (typeName.equals("boolean")) {
0954: xmlBoolean = Boolean.valueOf(value).booleanValue();
0955: } else if (typeName.equals("character")) {
0956: if (value.length() > 0) {
0957: xmlChar = value.charAt(0);
0958: } else {
0959: throw new NumberFormatException(
0960: "value string does not contain any character");
0961: }
0962: } else
0963:
0964: // number classes
0965: if (typeName.equals("java.lang.Byte")) {
0966: xmlObject = new Byte(value);
0967: } else if (typeName.equals("java.lang.Short")) {
0968: xmlObject = new Short(value);
0969: } else if (typeName.equals("java.lang.Integer")) {
0970: xmlObject = new Integer(value);
0971: } else if (typeName.equals("java.lang.Long")) {
0972: xmlObject = new Long(value);
0973: } else if (typeName.equals("java.lang.Float")) {
0974: xmlObject = new Float(value);
0975: } else if (typeName.equals("java.lang.Double")) {
0976: xmlObject = new Double(value);
0977: } else if (typeName.equals("java.math.BigInteger")) {
0978: xmlObject = new BigInteger(value);
0979: } else if (typeName.equals("java.math.BigDecimal")) {
0980: xmlObject = new BigDecimal(value);
0981: } else
0982:
0983: // other classes
0984: if (typeName.equals("java.lang.Boolean")) {
0985: xmlObject = new Boolean(value);
0986: } else if (typeName.equals("java.lang.Character")) {
0987: if (value.length() > 0) {
0988: xmlObject = new Character(value.charAt(0));
0989: } else {
0990: throw new NumberFormatException(
0991: "value string does not contain any character");
0992: }
0993: } else if (typeName.equals("java.lang.String")) {
0994: xmlObject = new String(value);
0995: } else if (typeName.equals("java.sql.Date")) {
0996: xmlObject = java.sql.Date.valueOf(value);
0997: } else if (typeName.equals("java.sql.Timestamp")) {
0998: xmlObject = java.sql.Timestamp.valueOf(value);
0999: } else {
1000: throw new IllegalArgumentException("Class '" + typeName
1001: + "' not supported");
1002: }
1003:
1004: }
1005:
1006: public Object getWrappedXMLPrimitive() {
1007: // java classes
1008: if (xmlObject != null)
1009: return xmlObject;
1010: // java primitives
1011: if (primitiveType.equals(Byte.TYPE))
1012: return new Byte(xmlByte);
1013: if (primitiveType.equals(Short.TYPE))
1014: return new Short(xmlShort);
1015: if (primitiveType.equals(Integer.TYPE))
1016: return new Integer(xmlInt);
1017: if (primitiveType.equals(Long.TYPE))
1018: return new Long(xmlLong);
1019: if (primitiveType.equals(Float.TYPE))
1020: return new Float(xmlFloat);
1021: if (primitiveType.equals(Double.TYPE))
1022: return new Double(xmlDouble);
1023: if (primitiveType.equals(Boolean.TYPE))
1024: return new Boolean(xmlBoolean);
1025: if (primitiveType.equals(Character.TYPE))
1026: return new Character(xmlChar);
1027: // no wrapper was found (strange ...)
1028: return null;
1029: }
1030:
1031: // set field in a supplied object to the object wrapped by this class
1032: public void setFieldInObject(Field field, Object obj)
1033: throws IllegalAccessException {
1034: if ((field == null) | (obj == null)) {
1035: throw new IllegalArgumentException(
1036: "arguments cannot be null");
1037: }
1038: field.setAccessible(true);
1039: Class fieldType = field.getType();
1040: if (!primitiveType.equals(fieldType)) {
1041: // problem - field is not of required type
1042: return;
1043: }
1044: String fieldTypeName = fieldType.getName();
1045: if (fieldTypeName.equals("byte")) {
1046: field.setByte(obj, xmlByte);
1047: } else if (fieldTypeName.equals("short")) {
1048: field.setShort(obj, xmlShort);
1049: } else if (fieldTypeName.equals("int")) {
1050: field.setInt(obj, xmlInt);
1051: } else if (fieldTypeName.equals("long")) {
1052: field.setLong(obj, xmlLong);
1053: } else if (fieldTypeName.equals("float")) {
1054: field.setFloat(obj, xmlFloat);
1055: } else if (fieldTypeName.equals("double")) {
1056: field.setDouble(obj, xmlDouble);
1057: } else if (fieldTypeName.equals("boolean")) {
1058: field.setBoolean(obj, xmlBoolean);
1059: } else if (fieldTypeName.equals("character")) {
1060: field.setChar(obj, xmlChar);
1061: } else if (xmlObject != null) {
1062: field.set(obj, xmlObject);
1063: }
1064: }
1065:
1066: // set item in an array
1067: public void setItemInArray(Object array, int index)
1068: throws IllegalAccessException {
1069: if (array == null) {
1070: throw new IllegalArgumentException(
1071: "array cannot be null");
1072: }
1073: if (!array.getClass().isArray()) {
1074: throw new IllegalArgumentException(
1075: "supplied array is not of array type");
1076: }
1077:
1078: Class componentType = array.getClass().getComponentType();
1079: if (!primitiveType.equals(componentType)) {
1080: // problem - field is not of required type
1081: return;
1082: }
1083:
1084: String componentTypeName = componentType.getName();
1085: if (componentTypeName.equals("byte")) {
1086: Array.setByte(array, index, xmlByte);
1087: } else if (componentTypeName.equals("short")) {
1088: Array.setShort(array, index, xmlShort);
1089: } else if (componentTypeName.equals("int")) {
1090: Array.setInt(array, index, xmlInt);
1091: } else if (componentTypeName.equals("long")) {
1092: Array.setLong(array, index, xmlLong);
1093: } else if (componentTypeName.equals("float")) {
1094: Array.setFloat(array, index, xmlFloat);
1095: } else if (componentTypeName.equals("double")) {
1096: Array.setDouble(array, index, xmlDouble);
1097: } else if (componentTypeName.equals("boolean")) {
1098: Array.setBoolean(array, index, xmlBoolean);
1099: } else if (componentTypeName.equals("character")) {
1100: Array.setChar(array, index, xmlChar);
1101: } else if (xmlObject != null) {
1102: Array.set(array, index, xmlObject);
1103: }
1104: }
1105: }
1106:
1107: }
|