001: /*
002: * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
003: * Copyright (C) 2006 - Javolution (http://javolution.org/)
004: * All rights reserved.
005: *
006: * Permission to use, copy, modify, and distribute this software is
007: * freely granted, provided that this notice is preserved.
008: */
009: package javolution.xml;
010:
011: import j2me.lang.CharSequence;
012: import javolution.Javolution;
013: import javolution.lang.Reusable;
014: import javolution.text.CharArray;
015: import javolution.text.TextBuilder;
016: import javolution.util.FastComparator;
017: import javolution.util.FastMap;
018: import javolution.util.FastTable;
019: import javolution.util.Index;
020: import javolution.xml.stream.XMLStreamException;
021:
022: /**
023: * <p> This class represents a resolver for XML cross references during
024: * the marshalling/unmarshalling process.</p>
025: *
026: * <p> Instances of this class may only be shared by {@link XMLObjectReader}/
027: * {@link XMLObjectWriter} running sequentially (for cross references
028: * spawning multiple documents).</p>
029: *
030: * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
031: * @version 4.0, September 4, 2006
032: */
033: public class XMLReferenceResolver implements Reusable {
034:
035: /**
036: * Holds object to identifier (FastTable.Index) mapping.
037: */
038: private FastMap _objectToId = new FastMap()
039: .setKeyComparator(FastComparator.IDENTITY);
040:
041: /**
042: * Holds the objects (index to object mapping).
043: */
044: private FastTable _idToObject = new FastTable();
045:
046: /**
047: * Holds the id counter.
048: */
049: private int _counter;
050:
051: /**
052: * Holds the identifier attribute name.
053: */
054: private String _idName = "id";
055:
056: /**
057: * Holds the identifier attribute URI if any.
058: */
059: private String _idURI = null;
060:
061: /**
062: * Holds the reference attribute name.
063: */
064: private String _refName = "ref";
065:
066: /**
067: * Holds the reference attribute URI if any.
068: */
069: private String _refURI = null;
070:
071: /**
072: * Default constructor.
073: */
074: public XMLReferenceResolver() {
075: }
076:
077: /**
078: * Sets the name of the identifier attribute (by default<code>"id"</code>).
079: * If the name is <code>null</code> then the identifier attribute
080: * is never read/written (which may prevent unmarshalling).
081: *
082: * @param name the name of the attribute or <code>null</code>.
083: */
084: public void setIdentifierAttribute(String name) {
085: setIdentifierAttribute(name, null);
086: }
087:
088: /**
089: * Sets the local name and namespace URI of the identifier attribute.
090: *
091: * @param localName the local name of the attribute or <code>null</code>.
092: * @param uri the URI of the attribute or <code>null</code> if the attribute
093: * has no namespace URI.
094: */
095: public void setIdentifierAttribute(String localName, String uri) {
096: _idName = localName;
097: _idURI = uri;
098: }
099:
100: /**
101: * Sets the name of the reference attribute (by default<code>"ref"</code>).
102: * If the name is <code>null</code> then the reference attribute
103: * is never read/written (which may prevent unmarshalling).
104: *
105: * @param name the name of the attribute or <code>null</code>.
106: */
107: public void setReferenceAttribute(String name) {
108: setReferenceAttribute(name, null);
109: }
110:
111: /**
112: * Sets the local name and namespace URI of the identifier attribute.
113: *
114: * @param localName the local name of the attribute or <code>null</code>.
115: * @param uri the URI of the attribute or <code>null</code> if the attribute
116: * has no namespace URI.
117: */
118: public void setReferenceAttribute(String localName, String uri) {
119: _refName = localName;
120: _refURI = uri;
121: }
122:
123: /**
124: * Writes a reference to the specified object into the specified XML
125: * element. The default implementation writes the reference into the
126: * reference attribute and for the first occurences an identifier
127: * (counter starting at 1) is written into the identifier attribute.
128: *
129: * @param obj the object for which the reference is written.
130: * @param xml the output XML element.
131: * @return <code>true</code> if a reference is written;
132: * <code>false</code> if a new identifier is written.
133: */
134: public boolean writeReference(Object obj,
135: XMLFormat.OutputElement xml) throws XMLStreamException {
136: Index id = (Index) _objectToId.get(obj);
137: if (id == null) { // New identifier.
138: id = Index.valueOf(_counter++);
139: _objectToId.put(obj, id);
140: _tmp.clear().append(id.intValue());
141: if (_idURI == null) {
142: xml.getStreamWriter().writeAttribute(toCsq(_idName),
143: _tmp);
144: } else {
145: xml.getStreamWriter().writeAttribute(toCsq(_idURI),
146: toCsq(_idName), _tmp);
147: }
148: return false;
149: }
150: _tmp.clear().append(id.intValue());
151: if (_refURI == null) {
152: xml._writer.writeAttribute(toCsq(_refName), _tmp);
153: } else {
154: xml._writer.writeAttribute(toCsq(_refURI), toCsq(_refName),
155: _tmp);
156: }
157: return true;
158: }
159:
160: private TextBuilder _tmp = new TextBuilder();
161:
162: /**
163: * Reads the object referenced by the specified xml input element if any.
164: * The default implementation reads the reference attribute to retrieve
165: * the object.
166: *
167: * @param xml the input XML element.
168: * @return the referenced object or <code>null</code> if the specified
169: * XML input does not have a reference attribute.
170: */
171: public Object readReference(XMLFormat.InputElement xml)
172: throws XMLStreamException {
173: CharArray value = xml._reader.getAttributeValue(toCsq(_refURI),
174: toCsq(_refName));
175: if (value == null)
176: return null;
177: int ref = value.toInt();
178: if (ref >= _idToObject.size())
179: throw new XMLStreamException("Reference: " + value
180: + " not found");
181: return _idToObject.get(ref);
182: }
183:
184: /**
185: * Creates a reference for the specified object (the identifier
186: * being specified by the input XML element).
187: * The default implementation reads the identifier attribute (if any)
188: * and associates it to the specified object.
189: *
190: * @param obj the object being referenced.
191: * @param xml the input XML element holding the reference identifier.
192: */
193: public void createReference(Object obj, XMLFormat.InputElement xml)
194: throws XMLStreamException {
195: CharArray value = xml._reader.getAttributeValue(toCsq(_idURI),
196: toCsq(_idName));
197: if (value == null)
198: return;
199: int i = value.toInt();
200: if (_idToObject.size() != i)
201: throw new XMLStreamException(
202: "Identifier discontinuity detected " + "(expected "
203: + _idToObject.size() + " found " + i + ")");
204: _idToObject.add(obj);
205: }
206:
207: // Implements Reusable.
208: public void reset() {
209: _idName = "id";
210: _idURI = null;
211: _refName = "ref";
212: _refURI = null;
213: _idToObject.clear();
214: _objectToId.clear();
215: _counter = 0;
216: }
217:
218: private static CharSequence toCsq/**/(Object str) {
219: return Javolution.j2meToCharSeq(str);
220: }
221:
222: }
|