001: /*
002: Copyright (c) 2004, Dennis M. Sosnoski
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.extras;
030:
031: import java.io.IOException;
032: import java.util.ArrayList;
033: import java.util.List;
034:
035: import org.dom4j.Attribute;
036: import org.dom4j.DocumentFactory;
037: import org.dom4j.Element;
038: import org.dom4j.Namespace;
039: import org.dom4j.Node;
040: import org.dom4j.ProcessingInstruction;
041: import org.dom4j.QName;
042: import org.jibx.runtime.IXMLReader;
043: import org.jibx.runtime.JiBXException;
044: import org.jibx.runtime.impl.UnmarshallingContext;
045:
046: /**
047: * <p>Base implementation for custom marshaller/unmarshallers to dom4j
048: * representation. This provides the basic code used for both single element and
049: * content list handling.</p>
050: *
051: * @author Dennis M. Sosnoski
052: * @version 1.0
053: */
054:
055: public class Dom4JMapperBase extends DocumentModelMapperBase {
056: /** dom4j component construction factory. */
057: private static DocumentFactory s_factory = DocumentFactory
058: .getInstance();
059:
060: /** Current default namespace URI (<code>null</code> if not determined). */
061: protected String m_defaultNamespaceURI;
062:
063: /** Current default namespace index. */
064: protected int m_defaultNamespaceIndex;
065:
066: /**
067: * Get index number for declared namespace.
068: *
069: * @param ns namespace of interest
070: * @return namespace index number, or <code>-1</code> if not declared or
071: * masked
072: */
073:
074: private int findNamespaceIndex(Namespace ns) {
075: if (Namespace.NO_NAMESPACE.equals(ns)) {
076: return 0;
077: } else if (Namespace.XML_NAMESPACE.equals(ns)) {
078: return 1;
079: } else {
080: String prefix = ns.getPrefix();
081: if (prefix == null || prefix.length() == 0) {
082: if (m_defaultNamespaceURI == null) {
083: int index = m_xmlWriter.getPrefixIndex("");
084: if (index >= 0) {
085: m_defaultNamespaceURI = getNamespaceUri(index);
086: m_defaultNamespaceIndex = index;
087: if (m_defaultNamespaceURI.equals(ns.getURI())) {
088: return index;
089: } else {
090: return -1;
091: }
092: } else {
093: return -1;
094: }
095: } else {
096: return m_defaultNamespaceURI.equals(ns.getURI()) ? m_defaultNamespaceIndex
097: : -1;
098: }
099: } else {
100: int index = m_xmlWriter.getPrefixIndex(prefix);
101: if (index >= 0) {
102: return getNamespaceUri(index).equals(ns.getURI()) ? index
103: : -1;
104: } else {
105: return -1;
106: }
107: }
108: }
109: }
110:
111: /**
112: * Marshal content list.
113: *
114: * @param content list of content items to marshal
115: * @exception JiBXException on error in marshalling
116: * @exception IOException on error writing to output
117: */
118:
119: protected void marshalContent(List content) throws JiBXException,
120: IOException {
121: int size = content.size();
122: for (int i = 0; i < size; i++) {
123: Node node = (Node) content.get(i);
124: switch (node.getNodeType()) {
125:
126: case Node.CDATA_SECTION_NODE:
127: m_xmlWriter.writeCData(node.getText());
128: break;
129:
130: case Node.COMMENT_NODE:
131: m_xmlWriter.writeComment(node.getText());
132: break;
133:
134: case Node.ELEMENT_NODE:
135: marshalElement((Element) node);
136: break;
137:
138: case Node.ENTITY_REFERENCE_NODE:
139: m_xmlWriter.writeEntityRef(node.getName());
140: break;
141:
142: case Node.PROCESSING_INSTRUCTION_NODE:
143: m_xmlWriter.writePI(((ProcessingInstruction) node)
144: .getTarget(), node.getText());
145: break;
146:
147: case Node.TEXT_NODE:
148: m_xmlWriter.writeTextContent(node.getText());
149: break;
150:
151: default:
152: break;
153: }
154: }
155: }
156:
157: /**
158: * Marshal element with all attributes and content.
159: *
160: * @param element element to be marshalled
161: * @exception JiBXException on error in marshalling
162: * @exception IOException on error writing to output
163: */
164:
165: protected void marshalElement(Element element)
166: throws JiBXException, IOException {
167:
168: // accumulate all needed namespace declarations
169: int size = element.nodeCount();
170: Namespace ns = element.getNamespace();
171: int nsi = findNamespaceIndex(ns);
172: ArrayList nss = null;
173: boolean hascontent = false;
174: int defind = -1;
175: String defuri = null;
176: for (int i = 0; i < size; i++) {
177: Node node = element.node(i);
178: if (node instanceof Namespace) {
179: Namespace dns = (Namespace) node;
180: if (findNamespaceIndex(dns) < 0) {
181: if (nss == null) {
182: nss = new ArrayList();
183: }
184: nss.add(dns);
185: String prefix = dns.getPrefix();
186: if (prefix == null || prefix.length() == 0) {
187: defind = nss.size() - 1;
188: defuri = dns.getURI();
189: }
190: }
191: } else {
192: hascontent = true;
193: }
194: }
195:
196: // check for namespace declarations required
197: String[] uris = null;
198: if (nss == null) {
199: m_xmlWriter.startTagOpen(nsi, element.getName());
200: } else {
201: int base = getNextNamespaceIndex();
202: if (defind >= 0) {
203: m_defaultNamespaceIndex = base + defind;
204: m_defaultNamespaceURI = defuri;
205: }
206: uris = new String[nss.size()];
207: int[] nums = new int[nss.size()];
208: String[] prefs = new String[nss.size()];
209: for (int i = 0; i < uris.length; i++) {
210: Namespace addns = (Namespace) nss.get(i);
211: uris[i] = addns.getURI();
212: nums[i] = base + i;
213: prefs[i] = addns.getPrefix();
214: if (nsi < 0 && ns.equals(addns)) {
215: nsi = base + i;
216: }
217: }
218: m_xmlWriter.pushExtensionNamespaces(uris);
219: m_xmlWriter.startTagNamespaces(nsi, element.getName(),
220: nums, prefs);
221: if (defind >= 0) {
222: m_defaultNamespaceIndex = defind;
223: m_defaultNamespaceURI = defuri;
224: }
225: }
226:
227: // add attributes if present
228: if (element.attributeCount() > 0) {
229: for (int i = 0; i < element.attributeCount(); i++) {
230: Attribute attr = element.attribute(i);
231: int index = findNamespaceIndex(attr.getNamespace());
232: m_xmlWriter.addAttribute(index, attr.getName(), attr
233: .getValue());
234: }
235: }
236:
237: // check for content present
238: if (hascontent) {
239: m_xmlWriter.closeStartTag();
240: marshalContent(element.content());
241: m_xmlWriter.endTag(nsi, element.getName());
242: } else {
243: m_xmlWriter.closeEmptyTag();
244: }
245:
246: // pop namespaces if defined by element
247: if (nss != null) {
248: m_xmlWriter.popExtensionNamespaces();
249: if (defind >= 0) {
250: m_defaultNamespaceURI = null;
251: }
252: }
253: }
254:
255: /**
256: * Unmarshal element content. This unmarshals everything up to the
257: * containing element close tag, adding each component to the content list
258: * supplied. On return, the parse position will always be at an END_TAG.
259: *
260: * @param content list for unmarshalled content
261: * @exception JiBXException on error in unmarshalling
262: * @exception IOException on error reading input
263: */
264:
265: protected void unmarshalContent(List content) throws JiBXException,
266: IOException {
267:
268: // loop until end of containing element found
269: loop: while (true) {
270: int cev = m_unmarshalContext.currentEvent();
271: switch (cev) {
272:
273: case IXMLReader.CDSECT:
274: content.add(s_factory.createCDATA(m_unmarshalContext
275: .getText()));
276: break;
277:
278: case IXMLReader.COMMENT:
279: content.add(s_factory.createComment(m_unmarshalContext
280: .getText()));
281: break;
282:
283: case IXMLReader.END_TAG:
284: break loop;
285:
286: case IXMLReader.ENTITY_REF:
287: if (m_unmarshalContext.getText() == null) {
288: content.add(s_factory.createEntity(
289: m_unmarshalContext.getName(), null));
290: break;
291: } else {
292: content.add(s_factory.createText(accumulateText()));
293: continue loop;
294: }
295:
296: case IXMLReader.PROCESSING_INSTRUCTION: {
297: String text = m_unmarshalContext.getText();
298: int index = 0;
299: while (++index < text.length()
300: && !isWhitespace(text.charAt(index)))
301: ;
302: if (index < text.length()) {
303: String target = text.substring(0, index);
304: while (++index < text.length()
305: && isWhitespace(text.charAt(index)))
306: ;
307: String data = text.substring(index);
308: content.add(s_factory.createProcessingInstruction(
309: target, data));
310: } else {
311: content.add(s_factory.createProcessingInstruction(
312: text, ""));
313: }
314: }
315: break;
316:
317: case IXMLReader.START_TAG:
318: content.add(unmarshalElement());
319: continue loop;
320:
321: case IXMLReader.TEXT:
322: content.add(s_factory.createText(accumulateText()));
323: continue loop;
324:
325: }
326: m_unmarshalContext.nextToken();
327: }
328: }
329:
330: /**
331: * Unmarshal element with all attributes and content. This must be called
332: * with the unmarshalling context positioned at a START_TAG event.
333: *
334: * @return unmarshalled element
335: * @exception JiBXException on error in unmarshalling
336: * @exception IOException on error reading input
337: */
338:
339: protected Element unmarshalElement() throws JiBXException,
340: IOException {
341:
342: // start by creating the actual element
343: QName qname = QName.get(m_unmarshalContext.getName(),
344: m_unmarshalContext.getPrefix(), m_unmarshalContext
345: .getNamespace());
346: Element element = s_factory.createElement(qname);
347:
348: // add all namespace declarations to element
349: int ncount = m_unmarshalContext.getNamespaceCount();
350: for (int i = 0; i < ncount; i++) {
351: String prefix = m_unmarshalContext.getNamespacePrefix(i);
352: String uri = m_unmarshalContext.getNamespaceUri(i);
353: element.addNamespace(prefix, uri);
354: }
355:
356: // add all attributes to element
357: int acount = m_unmarshalContext.getAttributeCount();
358: for (int i = 0; i < acount; i++) {
359: String prefix = m_unmarshalContext.getAttributePrefix(i);
360: String uri = m_unmarshalContext.getAttributeNamespace(i);
361: String name = m_unmarshalContext.getAttributeName(i);
362: String value = m_unmarshalContext.getAttributeValue(i);
363: qname = QName.get(name, prefix, uri);
364: element.addAttribute(qname, value);
365: }
366:
367: // add all content to element
368: int event = m_unmarshalContext.nextToken();
369: if (event != IXMLReader.END_TAG) {
370: unmarshalContent(element.content());
371: }
372: m_unmarshalContext.nextToken();
373: return element;
374: }
375: }
|