0001: /*
0002: * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
0003: * Copyright (C) 2006 - Javolution (http://javolution.org/)
0004: * All rights reserved.
0005: *
0006: * Permission to use, copy, modify, and distribute this software is
0007: * freely granted, provided that this notice is preserved.
0008: */
0009: package javolution.xml;
0010:
0011: import java.util.Hashtable;
0012: import j2me.lang.CharSequence;
0013: import javolution.Javolution;
0014: import javolution.text.CharArray;
0015: import javolution.text.TextBuilder;
0016: import javolution.text.TextFormat;
0017: import javolution.xml.sax.Attributes;
0018: import javolution.xml.stream.XMLStreamException;
0019: import javolution.xml.stream.XMLStreamReader;
0020: import javolution.xml.stream.XMLStreamReaderImpl;
0021: import javolution.xml.stream.XMLStreamWriter;
0022: import javolution.xml.stream.XMLStreamWriterImpl;
0023:
0024: /**
0025: * <p> This class represents the format base class for XML serialization and
0026: * deserialization.</p>
0027: *
0028: * <p> Application classes typically define a default XML format for their
0029: * instances using static {@link XMLFormat} class members.
0030: * Formats are inherited by sub-classes. For example:[code]
0031: *
0032: * public abstract class Graphic {
0033: * private boolean _isVisible;
0034: * private Paint _paint; // null if none.
0035: * private Stroke _stroke; // null if none.
0036: * private Transform _transform; // null if none.
0037: *
0038: * // XML format with positional associations (members identified by their position),
0039: * // see XML package description for examples of name associations.
0040: * private static final XMLFormat<Graphic> XML = new XMLFormat<Graphic>(Graphic.class) {
0041: * public void write(Graphic g, OutputElement xml) {
0042: * xml.setAttribute("isVisible", g._isVisible);
0043: * xml.add(g._paint); // First.
0044: * xml.add(g._stroke); // Second.
0045: * xml.add(g._transform); // Third.
0046: * }
0047: * public void read(InputElement xml, Graphic g) {
0048: * g._isVisible = xml.getAttribute("isVisible", true);
0049: * g._paint = xml.getNext();
0050: * g._stroke = xml.getNext();
0051: * g._transform = xml.getNext();
0052: * return g;
0053: * }
0054: * };
0055: * }[/code]
0056: *
0057: * <p> Due to the sequential nature of XML serialization/deserialization,
0058: * formatting/parsing of XML attributes should always be performed before
0059: * formatting/parsing of the XML content.</p>
0060: *
0061: * <p> The mapping between classes and XML formats is defined by {@link
0062: * XMLBinding} instances.
0063: * Here is an example of serialization/deserialization:[code]
0064: *
0065: * // Creates a list holding diverse objects.
0066: * List list = new ArrayList();
0067: * list.add("John Doe");
0068: * list.add(null);
0069: * Map map = new FastMap();
0070: * map.put("ONE", new Integer(1));
0071: * map.put("TWO", new Integer(2));
0072: * list.add(map);
0073: *
0074: * // Creates some aliases to use instead of class names.
0075: * XMLBinding binding = new XMLBinding();
0076: * binding.setAlias(FastMap.class, "Map");
0077: * binding.setAlias(String.class, "String");
0078: * binding.setAlias(Integer.class, "Integer");
0079: *
0080: * // Formats the list to XML .
0081: * OutputStream out = new FileOutputStream("C:/list.xml");
0082: * XMLObjectWriter writer = new XMLObjectWriter().setOutput(out).setBinding(binding);
0083: * writer.write(list, "MyList", ArrayList.class);
0084: * writer.close();[/code]
0085: *
0086: * Here is the output <code>list.xml</code> document produced:[code]
0087: *
0088: * <MyList>
0089: * <String value="John Doe"/>
0090: * <Null/>
0091: * <Map>
0092: * <Key class="String" value="ONE"/>
0093: * <Value class="Integer" value="1"/>
0094: * <Key class="String" value="TWO"/>
0095: * <Value class="Integer" value="2"/>
0096: * </Map>
0097: * </MyList>[/code]
0098: *
0099: * The list can be read back with the following code:[code]
0100: *
0101: * // Reads back to a FastTable instance.
0102: * InputStream in = new FileInputStream("C:/list.xml");
0103: * XMLObjectReader reader = new XMLObjectReader().setInput(in).setBinding(binding);
0104: * FastTable table = reader.read("MyList", FastTable.class);
0105: * reader.close();[/code]
0106: * </p>
0107: *
0108: * <p> <i>Note:</i> Any type for which a text format is
0109: * {@link TextFormat#getInstance(Class) known} can be represented as
0110: * a XML attribute.</p>
0111: *
0112: * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
0113: * @version 5.1, July 4, 2007
0114: */
0115: public abstract class XMLFormat/*<T>*/{
0116:
0117: /**
0118: * Holds <code>null</code> representation.
0119: */
0120: private static final String NULL = "Null";
0121:
0122: /**
0123: * Holds the class instances.
0124: */
0125: static volatile XMLFormat[] _ClassInstances = new XMLFormat[64];
0126:
0127: /**
0128: * Holds the number of class instances.
0129: */
0130: static volatile int _ClassInstancesLength;
0131:
0132: /**
0133: * Holds the class associated to this format (static instances only).
0134: */
0135: final Class _class;
0136:
0137: /**
0138: * Creates a XML format mapped to the specified class. If the specified
0139: * class is <code>null</code> then the format is left unmapped (
0140: * dynamic format used by custom {@link XMLBinding binding} instances).
0141: * The static binding is unique and can only be overriden by custom
0142: * {@link XMLBinding}. For example:[code]
0143: * // Overrides default binding for java.util.Collection.
0144: * class MyBinding extends XMLBinding {
0145: * XMLFormat<Collection> collectionXML = new XMLFormat<Collection>(null) { ... }; // Unmapped.
0146: * public XMLFormat getFormat(Class cls) {
0147: * if (Collection.isAssignableFrom(cls)) {
0148: * return collectionXML; // Overrides default XML format.
0149: * } else {
0150: * return super.getFormat(cls);
0151: * }
0152: * }
0153: * }[/code]
0154: *
0155: *
0156: * @param cls the root class/interface to associate to this XML format
0157: * or <code>null</code> if this format is not mapped.
0158: * @throws IllegalArgumentException if the specified class is already
0159: * bound to another format.
0160: */
0161: protected XMLFormat(Class/*<T>*/cls) {
0162: _class = cls;
0163: if (cls == null)
0164: return; // Dynamic format.
0165: synchronized (_ClassToFormat) {
0166: // Check if statically bounded.
0167: if (_ClassToFormat.containsKey(cls))
0168: throw new IllegalArgumentException(
0169: "Existing static binding for class "
0170: + cls
0171: + " can only be overriden through custom XMLBinding"
0172: + " (see XMLFormat(Class) documentation)");
0173: final int length = XMLFormat._ClassInstancesLength;
0174: final XMLFormat[] formats = XMLFormat._ClassInstances;
0175: if (length >= formats.length) { // Resizes (ImmortalMemory).
0176: XMLFormat[] tmp = new XMLFormat[length * 2];
0177: System.arraycopy(formats, 0, tmp, 0, length);
0178: XMLFormat._ClassInstances = tmp;
0179: }
0180: XMLFormat._ClassInstances[XMLFormat._ClassInstancesLength++] = this ;
0181: _ClassToFormat.put(cls, this );
0182: }
0183: }
0184:
0185: private static Hashtable _ClassToFormat = new Hashtable();
0186:
0187: /**
0188: * Returns the class/interface statically bound to this format or
0189: * <code>null</code> if none.
0190: *
0191: * @return the class/interface bound to this format.
0192: */
0193: public final Class/*<T>*/getBoundClass() {
0194: return _class;
0195: }
0196:
0197: /**
0198: * Indicates if the object serialized through this format can be referenced
0199: * to (default <code>true</code>). This method can be overriden to return
0200: * <code>false</code> if serialized objects are manipulated "by value".
0201: *
0202: * @return <code>true</code> if serialized object may hold a reference;
0203: * <code>false</code> otherwise.
0204: * @see XMLReferenceResolver
0205: */
0206: public boolean isReferenceable() {
0207: return true;
0208: }
0209:
0210: /**
0211: * Allocates a new object of the specified class from the specified
0212: * XML input element. By default, this method returns an object created
0213: * using the public no-arg constructor of the specified class.
0214: * XML formats may override this method in order to use private/multi-arg
0215: * constructors.
0216: *
0217: * @param cls the class of the object to return.
0218: * @param xml the XML input element.
0219: * @return the object corresponding to the specified XML element.
0220: */
0221: public Object/*{T}*/newInstance(Class/*<T>*/cls, InputElement xml)
0222: throws XMLStreamException {
0223: try {
0224: return cls.newInstance();
0225: } catch (InstantiationException e) {
0226: throw new XMLStreamException(e);
0227: } catch (IllegalAccessException e) {
0228: throw new XMLStreamException(e);
0229: }
0230: }
0231:
0232: /**
0233: * Formats an object into the specified XML output element.
0234: *
0235: * @param obj the object to format.
0236: * @param xml the <code>XMLElement</code> destination.
0237: */
0238: public abstract void write(Object/*{T}*/obj, OutputElement xml)
0239: throws XMLStreamException;
0240:
0241: /**
0242: * Parses an XML input element into the specified object.
0243: *
0244: * @param xml the XML element to parse.
0245: * @param obj the object created through {@link #newInstance}
0246: * and to setup from the specified XML element.
0247: */
0248: public abstract void read(InputElement xml, Object/*{T}*/obj)
0249: throws XMLStreamException;
0250:
0251: /**
0252: * This class represents an input XML element (unmarshalling).
0253: */
0254: public static final class InputElement {
0255:
0256: /**
0257: * Holds the stream reader.
0258: */
0259: final XMLStreamReaderImpl _reader = new XMLStreamReaderImpl();
0260:
0261: /**
0262: * Holds the XML binding.
0263: */
0264: private XMLBinding _binding;
0265:
0266: /**
0267: * Holds the reference resolver.
0268: */
0269: private XMLReferenceResolver _referenceResolver;
0270:
0271: /**
0272: * Indicates if the reader is currently positioned on the next element.
0273: */
0274: private boolean _isReaderAtNext;
0275:
0276: /**
0277: * Default constructor.
0278: */
0279: InputElement() {
0280: reset();
0281: }
0282:
0283: /**
0284: * Returns the StAX-like stream reader (provides complete control
0285: * over the unmarshalling process).
0286: *
0287: * @return the stream reader.
0288: */
0289: public XMLStreamReader getStreamReader() {
0290: return _reader;
0291: }
0292:
0293: /**
0294: * Indicates if more nested XML element can be read. This method
0295: * positions the {@link #getStreamReader reader} at the start of the
0296: * next XML element to be read (if any).
0297: *
0298: * @return <code>true</code> if there is more XML element to be read;
0299: * <code>false</code> otherwise.
0300: */
0301: public boolean hasNext() throws XMLStreamException {
0302: if (!_isReaderAtNext) {
0303: _isReaderAtNext = true;
0304: _reader.nextTag();
0305: }
0306: return _reader.getEventType() == XMLStreamReader.START_ELEMENT;
0307: }
0308:
0309: /**
0310: * Returns the next object whose type is identified by the local name
0311: * and URI of the current XML element (see {@link XMLBinding}).
0312: *
0313: * @return the next nested object which can be <code>null</code>.
0314: * @throws XMLStreamException if <code>hasNext() == false</code>.
0315: */
0316: public/*<T>*/Object/*{T}*/getNext() throws XMLStreamException {
0317: if (!hasNext()) // Asserts isReaderAtNext == true
0318: throw new XMLStreamException("No more element to read",
0319: _reader.getLocation());
0320:
0321: // Checks for null.
0322: if (_reader.getLocalName().equals(NULL)) {
0323: if (_reader.next() != XMLStreamReader.END_ELEMENT)
0324: throw new XMLStreamException(
0325: "Non Empty Null Element");
0326: _isReaderAtNext = false;
0327: return null;
0328: }
0329:
0330: // Checks if reference.
0331: if (_referenceResolver != null) {
0332: Object obj = _referenceResolver.readReference(this );
0333: if (obj != null) {
0334: if (_reader.next() != XMLStreamReader.END_ELEMENT)
0335: throw new XMLStreamException(
0336: "Non Empty Reference Element");
0337: _isReaderAtNext = false;
0338: return (Object/*{T}*/) obj;
0339: }
0340: }
0341:
0342: // Retrieves object's class.
0343: Class cls;
0344: try {
0345: cls = _binding.getClass(_reader.getLocalName(), _reader
0346: .getNamespaceURI());
0347: } catch (ClassNotFoundException e) {
0348: throw new XMLStreamException(e);
0349: }
0350:
0351: return (Object/*{T}*/) get(cls);
0352: }
0353:
0354: /**
0355: * Returns the object whose type is identified by a XML class attribute
0356: * only if the XML element has the specified local name.
0357: *
0358: * @param name the local name of the next element.
0359: * @return the next nested object or <code>null</code>.
0360: */
0361: public/*<T>*/Object/*{T}*/get(String name)
0362: throws XMLStreamException {
0363: if (!hasNext()// Asserts isReaderAtNext == true
0364: || !_reader.getLocalName().equals(name))
0365: return null;
0366:
0367: // Checks if reference.
0368: if (_referenceResolver != null) {
0369: Object obj = _referenceResolver.readReference(this );
0370: if (obj != null) {
0371: if (_reader.next() != XMLStreamReader.END_ELEMENT)
0372: throw new XMLStreamException(
0373: "Non Empty Reference Element");
0374: _isReaderAtNext = false;
0375: return (Object/*{T}*/) obj;
0376: }
0377: }
0378:
0379: // Retrieves object's class from class attribute.
0380: Class cls = _binding.readClassAttribute(_reader);
0381:
0382: return (Object/*{T}*/) get(cls);
0383: }
0384:
0385: /**
0386: * Returns the object whose type is identified by a XML class attribute
0387: * only if the XML element has the specified local name and URI.
0388: *
0389: * @param localName the local name.
0390: * @param uri the namespace URI or <code>null</code>.
0391: * @return the next nested object or <code>null</code>.
0392: */
0393: public/*<T>*/Object/*{T}*/get(String localName, String uri)
0394: throws XMLStreamException {
0395: if (uri == null)
0396: return (Object/*{T}*/) get(localName);
0397:
0398: if (!hasNext()// Asserts isReaderAtNext == true
0399: || !_reader.getLocalName().equals(localName)
0400: || !_reader.getNamespaceURI().equals(uri))
0401: return null;
0402:
0403: // Checks if reference.
0404: if (_referenceResolver != null) {
0405: Object obj = _referenceResolver.readReference(this );
0406: if (obj != null) {
0407: if (_reader.next() != XMLStreamReader.END_ELEMENT)
0408: throw new XMLStreamException(
0409: "Non Empty Reference Element");
0410: _isReaderAtNext = false;
0411: return (Object/*{T}*/) obj;
0412: }
0413: }
0414:
0415: // Retrieves object's class from class attribute.
0416: Class cls = _binding.readClassAttribute(_reader);
0417:
0418: return (Object/*{T}*/) get(cls);
0419: }
0420:
0421: /**
0422: * Returns the object of specified type only if the XML element has the
0423: * specified local name.
0424: *
0425: * @param name the local name of the element to match.
0426: * @param cls the class identifying the format of the object to return.
0427: * @return the next nested object or <code>null</code>.
0428: */
0429: public/*<T>*/Object/*{T}*/get(String name, Class/*<T>*/cls)
0430: throws XMLStreamException {
0431: if (!hasNext()// Asserts isReaderAtNext == true
0432: || !_reader.getLocalName().equals(name))
0433: return null;
0434:
0435: // Checks if reference.
0436: if (_referenceResolver != null) {
0437: Object obj = _referenceResolver.readReference(this );
0438: if (obj != null) {
0439: if (_reader.next() != XMLStreamReader.END_ELEMENT)
0440: throw new XMLStreamException(
0441: "Non Empty Reference Element");
0442: _isReaderAtNext = false;
0443: return (Object/*{T}*/) obj;
0444: }
0445: }
0446:
0447: return (Object/*{T}*/) get(cls);
0448: }
0449:
0450: /**
0451: * Returns the object of specified type only if the
0452: * XML element has the specified local name and namespace URI.
0453: *
0454: * @param localName the local name.
0455: * @param uri the namespace URI or <code>null</code>.
0456: * @param cls the class identifying the format of the object to return.
0457: * @return the next nested object or <code>null</code>.
0458: */
0459: public/*<T>*/Object/*{T}*/get(String localName, String uri,
0460: Class/*<T>*/cls) throws XMLStreamException {
0461: if (uri == null)
0462: return get(localName, cls);
0463:
0464: if (!hasNext()// Asserts isReaderAtNext == true
0465: || !_reader.getLocalName().equals(localName)
0466: || !_reader.getNamespaceURI().equals(uri))
0467: return null;
0468:
0469: // Checks if reference.
0470: if (_referenceResolver != null) {
0471: Object obj = _referenceResolver.readReference(this );
0472: if (obj != null) {
0473: if (_reader.next() != XMLStreamReader.END_ELEMENT)
0474: throw new XMLStreamException(
0475: "Non Empty Reference Element");
0476: _isReaderAtNext = false;
0477: return (Object/*{T}*/) obj;
0478: }
0479: }
0480:
0481: return (Object/*{T}*/) get(cls);
0482: }
0483:
0484: // Builds object of specified class.
0485: private Object get(Class cls) throws XMLStreamException {
0486:
0487: // Retrieves format.
0488: XMLFormat xmlFormat = _binding.getFormat(cls);
0489:
0490: // Creates object.
0491: _isReaderAtNext = false; // Makes attributes accessible.
0492: Object obj = xmlFormat.newInstance(cls, this );
0493:
0494: // Adds reference (before reading to support circular reference).
0495: if (_referenceResolver != null) {
0496: _referenceResolver.createReference(obj, this );
0497: }
0498:
0499: // Parses xml.
0500: xmlFormat.read(this , obj);
0501: if (hasNext()) // Asserts _isReaderAtNext == true
0502: throw new XMLStreamException(
0503: "Incomplete element reading", _reader
0504: .getLocation());
0505: _isReaderAtNext = false; // Skips end element.
0506: return obj;
0507: }
0508:
0509: /**
0510: * Returns the content of a text-only element (equivalent to
0511: * {@link javolution.xml.stream.XMLStreamReader#getElementText
0512: * getStreamReader().getElementText()}).
0513: *
0514: * @return the element text content or an empty sequence if none.
0515: */
0516: public CharArray getText() throws XMLStreamException {
0517: CharArray txt = _reader.getElementText();
0518: _isReaderAtNext = true; // End element is next.
0519: return txt;
0520: }
0521:
0522: /**
0523: * Returns the attributes for this XML input element.
0524: *
0525: * @return the attributes mapping.
0526: */
0527: public Attributes getAttributes() throws XMLStreamException {
0528: if (_isReaderAtNext)
0529: throw new XMLStreamException(
0530: "Attributes should be read before content");
0531: return _reader.getAttributes();
0532: }
0533:
0534: /**
0535: * Searches for the attribute having the specified name.
0536: *
0537: * @param name the name of the attribute.
0538: * @return the value for the specified attribute or <code>null</code>
0539: * if the attribute is not found.
0540: */
0541: public CharArray getAttribute(String name)
0542: throws XMLStreamException {
0543: if (_isReaderAtNext)
0544: throw new XMLStreamException(
0545: "Attributes should be read before reading content");
0546: return _reader.getAttributeValue(null, toCsq(name));
0547: }
0548:
0549: /**
0550: * Returns the specified <code>String</code> attribute.
0551: *
0552: * @param name the name of the attribute.
0553: * @param defaultValue a default value.
0554: * @return the value for the specified attribute or
0555: * the <code>defaultValue</code> if the attribute is not found.
0556: */
0557: public String getAttribute(String name, String defaultValue)
0558: throws XMLStreamException {
0559: CharArray value = getAttribute(name);
0560: return (value != null) ? value.toString() : defaultValue;
0561: }
0562:
0563: /**
0564: * Returns the specified <code>boolean</code> attribute.
0565: *
0566: * @param name the name of the attribute searched for.
0567: * @param defaultValue the value returned if the attribute is not found.
0568: * @return the <code>boolean</code> value for the specified attribute or
0569: * the default value if the attribute is not found.
0570: */
0571: public boolean getAttribute(String name, boolean defaultValue)
0572: throws XMLStreamException {
0573: CharArray value = getAttribute(name);
0574: return (value != null) ? value.toBoolean() : defaultValue;
0575: }
0576:
0577: /**
0578: * Returns the specified <code>char</code> attribute.
0579: *
0580: * @param name the name of the attribute searched for.
0581: * @param defaultValue the value returned if the attribute is not found.
0582: * @return the <code>char</code> value for the specified attribute or
0583: * the default value if the attribute is not found.
0584: */
0585: public char getAttribute(String name, char defaultValue)
0586: throws XMLStreamException {
0587: CharArray value = getAttribute(name);
0588: if (value == null)
0589: return defaultValue;
0590: if (value.length() != 1)
0591: throw new XMLStreamException(
0592: "Single character expected (read '" + value
0593: + "')");
0594: return value.charAt(0);
0595: }
0596:
0597: /**
0598: * Returns the specified <code>int</code> attribute. This method handles
0599: * string formats that are used to represent octal and hexadecimal numbers.
0600: *
0601: * @param name the name of the attribute searched for.
0602: * @param defaultValue the value returned if the attribute is not found.
0603: * @return the <code>int</code> value for the specified attribute or
0604: * the default value if the attribute is not found.
0605: */
0606: public int getAttribute(String name, int defaultValue)
0607: throws XMLStreamException {
0608: CharArray value = getAttribute(name);
0609: return (value != null) ? value.toInt() : defaultValue;
0610: }
0611:
0612: /**
0613: * Returns the specified <code>long</code> attribute. This method handles
0614: * string formats that are used to represent octal and hexadecimal numbers.
0615: *
0616: * @param name the name of the attribute searched for.
0617: * @param defaultValue the value returned if the attribute is not found.
0618: * @return the <code>long</code> value for the specified attribute or
0619: * the default value if the attribute is not found.
0620: */
0621: public long getAttribute(String name, long defaultValue)
0622: throws XMLStreamException {
0623: CharArray value = getAttribute(name);
0624: return (value != null) ? value.toLong() : defaultValue;
0625: }
0626:
0627: /**
0628: * Returns the specified <code>float</code> attribute.
0629: *
0630: * @param name the name of the attribute searched for.
0631: * @param defaultValue the value returned if the attribute is not found.
0632: * @return the <code>float</code> value for the specified attribute or
0633: * the default value if the attribute is not found.
0634: /*@JVM-1.1+@
0635: public float getAttribute(String name, float defaultValue) throws XMLStreamException {
0636: CharArray value = getAttribute(name);
0637: return (value != null) ? value.toFloat() : defaultValue;
0638: }
0639: /**/
0640:
0641: /**
0642: * Returns the specified <code>double</code> attribute.
0643: *
0644: * @param name the name of the attribute searched for.
0645: * @param defaultValue the value returned if the attribute is not found.
0646: * @return the <code>double</code> value for the specified attribute or
0647: * the default value if the attribute is not found.
0648: /*@JVM-1.1+@
0649: public double getAttribute(String name, double defaultValue) throws XMLStreamException {
0650: CharArray value = getAttribute(name);
0651: return (value != null) ? value.toDouble() : defaultValue;
0652: }
0653: /**/
0654:
0655: /**
0656: * Returns the attribute of same type as the specified
0657: * default value. The default value
0658: * {@link javolution.text.TextFormat#getInstance TextFormat} is
0659: * used to parse the attribute value.
0660: *
0661: * @param name the name of the attribute.
0662: * @param defaultValue the value returned if the attribute is not found.
0663: * @return the parse value for the specified attribute or
0664: * the default value if the attribute is not found.
0665: */
0666: public/*<T>*/Object/*{T}*/getAttribute(String name,
0667: Object/*{T}*/defaultValue) throws XMLStreamException {
0668: CharArray value = getAttribute(name);
0669: if (value == null)
0670: return defaultValue;
0671: // Parses attribute value.
0672: Class type = defaultValue.getClass();
0673: TextFormat format = TextFormat.getInstance(type);
0674: if (format == null)
0675: throw new XMLStreamException(
0676: "No TextFormat instance for " + type);
0677: return (Object/*{T}*/) format.parse(value);
0678: }
0679:
0680: // Sets XML binding.
0681: void setBinding(XMLBinding xmlBinding) {
0682: _binding = xmlBinding;
0683: }
0684:
0685: // Sets XML reference resolver.
0686: void setReferenceResolver(
0687: XMLReferenceResolver xmlReferenceResolver) {
0688: _referenceResolver = xmlReferenceResolver;
0689: }
0690:
0691: // Resets for reuse.
0692: void reset() {
0693: _binding = XMLBinding.DEFAULT;
0694: _isReaderAtNext = false;
0695: _reader.reset();
0696: _referenceResolver = null;
0697: }
0698: }
0699:
0700: /**
0701: * This class represents an output XML element (marshalling).
0702: */
0703: public static final class OutputElement {
0704:
0705: /**
0706: * Holds the stream writer.
0707: */
0708: final XMLStreamWriterImpl _writer = new XMLStreamWriterImpl();
0709:
0710: /**
0711: * Holds the XML binding.
0712: */
0713: private XMLBinding _binding;
0714:
0715: /**
0716: * Holds the reference resolver.
0717: */
0718: private XMLReferenceResolver _referenceResolver;
0719:
0720: /**
0721: * Default constructor.
0722: */
0723: OutputElement() {
0724: reset();
0725: }
0726:
0727: /**
0728: * Returns the StAX-like stream writer (provides complete control over
0729: * the marshalling process).
0730: *
0731: * @return the stream writer.
0732: */
0733: public XMLStreamWriter getStreamWriter() {
0734: return _writer;
0735: }
0736:
0737: /**
0738: * Adds the specified object or <code>null</code> as an anonymous
0739: * nested element of unknown type.
0740: *
0741: * @param obj the object added as nested element or <code>null</code>.
0742: */
0743: public void add(Object obj) throws XMLStreamException {
0744: if (obj == null) {
0745: _writer.writeEmptyElement(toCsq(NULL));
0746: return;
0747: }
0748:
0749: // Writes start element.
0750: Class cls = obj.getClass();
0751: String localName = _binding.getLocalName(cls);
0752: String uri = _binding.getURI(cls);
0753: if (uri == null) {
0754: _writer.writeStartElement(toCsq(localName));
0755: } else {
0756: _writer.writeStartElement(toCsq(uri), toCsq(localName));
0757: }
0758:
0759: // Check if reference to be written.
0760: XMLFormat xmlFormat = _binding.getFormat(cls);
0761: if ((_referenceResolver != null)
0762: && xmlFormat.isReferenceable()
0763: && _referenceResolver.writeReference(obj, this )) {
0764: _writer.writeEndElement();
0765: return; // Reference written.
0766: }
0767:
0768: xmlFormat.write(obj, this );
0769: _writer.writeEndElement();
0770: }
0771:
0772: /**
0773: * Adds the specified object as a named nested element of unknown type
0774: * (<code>null</code> objects are ignored).
0775: * The nested XML element contains a class attribute identifying
0776: * the object type.
0777: *
0778: * @param obj the object added as nested element or <code>null</code>.
0779: * @param name the name of the nested element.
0780: */
0781: public void add(Object obj, String name)
0782: throws XMLStreamException {
0783: if (obj == null)
0784: return;
0785:
0786: // Writes start element.
0787: _writer.writeStartElement(toCsq(name));
0788:
0789: // Writes class attribute.
0790: Class cls = obj.getClass();
0791: _binding.writeClassAttribute(_writer, cls);
0792:
0793: // Check if reference is to be written.
0794: XMLFormat xmlFormat = _binding.getFormat(cls);
0795: if ((_referenceResolver != null)
0796: && xmlFormat.isReferenceable()
0797: && _referenceResolver.writeReference(obj, this )) {
0798: _writer.writeEndElement();
0799: return; // Reference written.
0800: }
0801:
0802: xmlFormat.write(obj, this );
0803: _writer.writeEndElement();
0804: }
0805:
0806: /**
0807: * Adds the specified object as a fully qualified nested element of
0808: * unknown type (<code>null</code> objects are ignored).
0809: * The nested XML element contains a class attribute identifying
0810: * the object type.
0811: *
0812: * @param obj the object added as nested element or <code>null</code>.
0813: * @param localName the local name of the nested element.
0814: * @param uri the namespace URI of the nested element.
0815: */
0816: public void add(Object obj, String localName, String uri)
0817: throws XMLStreamException {
0818: if (obj == null)
0819: return;
0820:
0821: // Writes start element.
0822: _writer.writeStartElement(toCsq(uri), toCsq(localName));
0823:
0824: // Writes class attribute.
0825: Class cls = obj.getClass();
0826: _binding.writeClassAttribute(_writer, cls);
0827:
0828: // Check if reference is to be written.
0829: XMLFormat xmlFormat = _binding.getFormat(cls);
0830: if ((_referenceResolver != null)
0831: && xmlFormat.isReferenceable()
0832: && _referenceResolver.writeReference(obj, this )) {
0833: _writer.writeEndElement();
0834: return; // Reference written.
0835: }
0836:
0837: xmlFormat.write(obj, this );
0838: _writer.writeEndElement();
0839: }
0840:
0841: /**
0842: * Adds the specified object as a named nested element of specified
0843: * actual type (<code>null</code> objects are ignored).
0844: * The nested XML element does not contain any class attribute.
0845: *
0846: * @param obj the object added as nested element or <code>null</code>.
0847: * @param name the name of the nested element.
0848: * @param cls the class identifying the format of the specified object.
0849: */
0850: public/*<T>*/void add(Object/*{T}*/obj, String name,
0851: Class/*<T>*/cls) throws XMLStreamException {
0852: if (obj == null)
0853: return;
0854:
0855: // Writes start element.
0856: _writer.writeStartElement(toCsq(name));
0857:
0858: // Check if reference is to be written.
0859: XMLFormat xmlFormat = _binding.getFormat(cls);
0860: if ((_referenceResolver != null)
0861: && xmlFormat.isReferenceable()
0862: && _referenceResolver.writeReference(obj, this )) {
0863: _writer.writeEndElement();
0864: return; // Reference written.
0865: }
0866:
0867: xmlFormat.write(obj, this );
0868: _writer.writeEndElement();
0869: }
0870:
0871: /**
0872: * Adds the specified object as a fully qualified nested element of
0873: * specified actual type (<code>null</code> objects are ignored).
0874: * The nested XML element does not contain any class attribute.
0875: *
0876: * @param obj the object added as nested element or <code>null</code>.
0877: * @param localName the local name of the nested element.
0878: * @param uri the namespace URI of the nested element.
0879: * @param cls the class identifying the format of the specified object.
0880: */
0881: public/*<T>*/void add(Object/*{T}*/obj, String localName,
0882: String uri, Class/*<T>*/cls) throws XMLStreamException {
0883: if (obj == null)
0884: return;
0885:
0886: // Writes start element.
0887: _writer.writeStartElement(toCsq(uri), toCsq(localName));
0888:
0889: // Check if reference is to be written.
0890: XMLFormat xmlFormat = _binding.getFormat(cls);
0891: if ((_referenceResolver != null)
0892: && xmlFormat.isReferenceable()
0893: && _referenceResolver.writeReference(obj, this )) {
0894: _writer.writeEndElement();
0895: return; // Reference written.
0896: }
0897:
0898: xmlFormat.write(obj, this );
0899: _writer.writeEndElement();
0900: }
0901:
0902: /**
0903: * Adds the content of a text-only element (equivalent to {@link
0904: * javolution.xml.stream.XMLStreamWriter#writeCharacters(CharSequence)
0905: * getStreamWriter().writeCharacters(text)}).
0906: *
0907: * @param text the element text content or an empty sequence if none.
0908: */
0909: public void addText(CharSequence text)
0910: throws XMLStreamException {
0911: _writer.writeCharacters(text);
0912: }
0913:
0914: /**
0915: * Equivalent to {@link #addText(CharSequence)}
0916: * (for J2ME compatibility).
0917: *
0918: * @param text the element text content or an empty sequence if none.
0919: */
0920: public void addText(String text) throws XMLStreamException {
0921: _writer.writeCharacters(toCsq(text));
0922: }
0923:
0924: /**
0925: * Sets the specified <code>CharSequence</code> attribute
0926: * (<code>null</code> values are ignored).
0927: *
0928: * @param name the attribute name.
0929: * @param value the attribute value or <code>null</code>.
0930: */
0931: public void setAttribute(String name, CharSequence value)
0932: throws XMLStreamException {
0933: if (value == null)
0934: return;
0935: _writer.writeAttribute(toCsq(name), value);
0936: }
0937:
0938: /**
0939: * Sets the specified <code>String</code> attribute
0940: * (<code>null</code> values are ignored).
0941: *
0942: * @param name the attribute name.
0943: * @param value the attribute value.
0944: */
0945: public void setAttribute(String name, String value)
0946: throws XMLStreamException {
0947: if (value == null)
0948: return;
0949: _writer.writeAttribute(toCsq(name), toCsq(value));
0950: }
0951:
0952: /**
0953: * Sets the specified <code>boolean</code> attribute.
0954: *
0955: * @param name the attribute name.
0956: * @param value the <code>boolean</code> value for the specified attribute.
0957: */
0958: public void setAttribute(String name, boolean value)
0959: throws XMLStreamException {
0960: setAttribute(name, _tmpTextBuilder.clear().append(value));
0961: }
0962:
0963: private TextBuilder _tmpTextBuilder = new TextBuilder();
0964:
0965: /**
0966: * Sets the specified <code>char</code> attribute.
0967: *
0968: * @param name the attribute name.
0969: * @param value the <code>char</code> value for the specified attribute.
0970: */
0971: public void setAttribute(String name, char value)
0972: throws XMLStreamException {
0973: setAttribute(name, (TextBuilder) _tmpTextBuilder.clear()
0974: .append(value));
0975: }
0976:
0977: /**
0978: * Sets the specified <code>int</code> attribute.
0979: *
0980: * @param name the attribute name.
0981: * @param value the <code>int</code> value for the specified attribute.
0982: */
0983: public void setAttribute(String name, int value)
0984: throws XMLStreamException {
0985: setAttribute(name, _tmpTextBuilder.clear().append(value));
0986: }
0987:
0988: /**
0989: * Sets the specified <code>long</code> attribute.
0990: *
0991: * @param name the attribute name.
0992: * @param value the <code>long</code> value for the specified attribute.
0993: */
0994: public void setAttribute(String name, long value)
0995: throws XMLStreamException {
0996: setAttribute(name, _tmpTextBuilder.clear().append(value));
0997: }
0998:
0999: /**
1000: * Sets the specified <code>float</code> attribute.
1001: *
1002: * @param name the attribute name.
1003: * @param value the <code>float</code> value for the specified attribute.
1004: /*@JVM-1.1+@
1005: public void setAttribute(String name, float value) throws XMLStreamException {
1006: setAttribute(name, _tmpTextBuilder.clear().append(value));
1007: }
1008: /**/
1009:
1010: /**
1011: * Sets the specified <code>double</code> attribute.
1012: *
1013: * @param name the attribute name.
1014: * @param value the <code>double</code> value for the specified attribute.
1015: /*@JVM-1.1+@
1016: public void setAttribute(String name, double value) throws XMLStreamException {
1017: setAttribute(name, _tmpTextBuilder.clear().append(value));
1018: }
1019: /**/
1020:
1021: /**
1022: * Sets the specified attribute using its associated
1023: * {@link javolution.text.TextFormat#getInstance TextFormat}.
1024: *
1025: * @param name the name of the attribute.
1026: * @param value the <code>Boolean</code> value for the specified attribute
1027: * or <code>null</code> in which case the attribute is not set.
1028: */
1029: public void setAttribute(String name, Object value)
1030: throws XMLStreamException {
1031: if (value == null)
1032: return;
1033: Class type = value.getClass();
1034: TextFormat format = TextFormat.getInstance(type);
1035: if (format == null)
1036: throw new XMLStreamException(
1037: "No TextFormat instance for " + type);
1038: setAttribute(name, (TextBuilder) format.format(value,
1039: _tmpTextBuilder.clear()));
1040: }
1041:
1042: // Sets XML binding.
1043: void setBinding(XMLBinding xmlBinding) {
1044: _binding = xmlBinding;
1045: }
1046:
1047: // Sets XML reference resolver.
1048: void setReferenceResolver(
1049: XMLReferenceResolver xmlReferenceResolver) {
1050: _referenceResolver = xmlReferenceResolver;
1051: }
1052:
1053: // Resets for reuse.
1054: void reset() {
1055: _binding = XMLBinding.DEFAULT;
1056: _writer.reset();
1057: _writer.setRepairingNamespaces(true);
1058: _writer.setAutomaticEmptyElements(true);
1059: _referenceResolver = null;
1060: }
1061:
1062: }
1063:
1064: private static CharSequence toCsq/**/(Object str) {
1065: return Javolution.j2meToCharSeq(str);
1066: }
1067:
1068: /**
1069: * Creates an unmapped XML format.
1070: *
1071: * @deprecated <code>XMLFormat(null) should be used instead.
1072: */
1073: protected XMLFormat() {
1074: this(null);
1075: }
1076: }
|