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:
034: import javax.xml.parsers.DocumentBuilderFactory;
035: import javax.xml.parsers.ParserConfigurationException;
036:
037: import org.jibx.runtime.IXMLReader;
038: import org.jibx.runtime.JiBXException;
039: import org.w3c.dom.Attr;
040: import org.w3c.dom.Document;
041: import org.w3c.dom.Element;
042: import org.w3c.dom.NamedNodeMap;
043: import org.w3c.dom.Node;
044: import org.w3c.dom.NodeList;
045:
046: /**
047: * <p>Base implementation for custom marshaller/unmarshallers to DOM
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 DomMapperBase extends DocumentModelMapperBase {
056: /** Actual document instance (required by DOM). */
057: protected Document m_document;
058:
059: /** Current default namespace URI (<code>null</code> if not determined). */
060: protected String m_defaultNamespaceURI;
061:
062: /** Current default namespace index. */
063: protected int m_defaultNamespaceIndex;
064:
065: /**
066: * Constructor. Initializes the document used by this
067: * marshaller/unmarshaller instance as the owner of all DOM components.
068: *
069: * @throws JiBXException on error creating document
070: */
071:
072: protected DomMapperBase() throws JiBXException {
073: DocumentBuilderFactory dbf = DocumentBuilderFactory
074: .newInstance();
075: dbf.setNamespaceAware(true);
076: try {
077: m_document = dbf.newDocumentBuilder().newDocument();
078: } catch (ParserConfigurationException e) {
079: throw new JiBXException("Unable to create DOM document", e);
080: }
081: }
082:
083: /**
084: * Get index number for declared namespace.
085: *
086: * @param prefix namespace prefix (<code>null</code> if none)
087: * @param uri namespace URI (empty string if none)
088: * @return namespace index number, or <code>-1</code> if not declared or
089: * masked
090: */
091:
092: private int findNamespaceIndex(String prefix, String uri) {
093: if ((prefix == null || "".equals(prefix))
094: && (uri == null || "".equals(uri))) {
095: return 0;
096: } else if ("xml".equals(prefix) && XML_NAMESPACE.equals(uri)) {
097: return 1;
098: } else {
099: if (prefix == null) {
100: if (m_defaultNamespaceURI == null) {
101: int index = m_xmlWriter.getPrefixIndex("");
102: if (index >= 0) {
103: m_defaultNamespaceURI = getNamespaceUri(index);
104: m_defaultNamespaceIndex = index;
105: if (m_defaultNamespaceURI.equals(uri)) {
106: return index;
107: } else {
108: return -1;
109: }
110: } else {
111: return -1;
112: }
113: } else {
114: return m_defaultNamespaceURI.equals(uri) ? m_defaultNamespaceIndex
115: : -1;
116: }
117: } else {
118: int index = m_xmlWriter.getPrefixIndex(prefix);
119: if (index >= 0) {
120: return getNamespaceUri(index).equals(uri) ? index
121: : -1;
122: } else {
123: return -1;
124: }
125: }
126: }
127: }
128:
129: /**
130: * Marshal node.
131: *
132: * @param node node to be marshalled
133: * @exception JiBXException on error in marshalling
134: * @exception IOException on error writing to output
135: */
136:
137: protected void marshalNode(Node node) throws JiBXException,
138: IOException {
139: switch (node.getNodeType()) {
140:
141: case Node.CDATA_SECTION_NODE:
142: m_xmlWriter.writeCData(node.getNodeValue());
143: break;
144:
145: case Node.COMMENT_NODE:
146: m_xmlWriter.writeComment(node.getNodeValue());
147: break;
148:
149: case Node.ELEMENT_NODE:
150: marshalElement((Element) node);
151: break;
152:
153: case Node.ENTITY_REFERENCE_NODE:
154: m_xmlWriter.writeEntityRef(node.getNodeName());
155: break;
156:
157: case Node.PROCESSING_INSTRUCTION_NODE:
158: m_xmlWriter
159: .writePI(node.getNodeName(), node.getNodeValue());
160: break;
161:
162: case Node.TEXT_NODE:
163: m_xmlWriter.writeTextContent(node.getNodeValue());
164: break;
165:
166: default:
167: break;
168: }
169: }
170:
171: /**
172: * Marshal node list.
173: *
174: * @param content list of nodes to marshal
175: * @exception JiBXException on error in marshalling
176: * @exception IOException on error writing to output
177: */
178:
179: protected void marshalContent(NodeList content)
180: throws JiBXException, IOException {
181: int size = content.getLength();
182: for (int i = 0; i < size; i++) {
183: marshalNode(content.item(i));
184: }
185: }
186:
187: /**
188: * Marshal element with all attributes and content.
189: *
190: * @param element element to be marshalled
191: * @exception JiBXException on error in marshalling
192: * @exception IOException on error writing to output
193: */
194:
195: protected void marshalElement(Element element)
196: throws JiBXException, IOException {
197:
198: // accumulate all needed namespace declarations
199: String prefix = element.getPrefix();
200: String uri = element.getNamespaceURI();
201: int nsi = findNamespaceIndex(prefix, uri);
202: ArrayList nss = null;
203: int defind = -1;
204: String defuri = null;
205: NamedNodeMap attrs = element.getAttributes();
206: int size = attrs.getLength();
207: for (int i = 0; i < size; i++) {
208: Attr attr = (Attr) attrs.item(i);
209: if (XMLNS_NAMESPACE.equals(attr.getNamespaceURI())) {
210:
211: // found namespace declaration, convert to simple prefix
212: String declpref = attr.getLocalName();
213: if ("xmlns".equals(declpref)) {
214: declpref = null;
215: }
216: String decluri = attr.getValue();
217: if (findNamespaceIndex(declpref, decluri) < 0) {
218: if (nss == null) {
219: nss = new ArrayList();
220: }
221: nss.add(declpref == null ? "" : declpref);
222: nss.add(decluri == null ? "" : decluri);
223: if (declpref == null) {
224: defind = (nss.size() / 2) - 1;
225: defuri = decluri;
226: }
227: if (uri == decluri) {
228: nsi = defind;
229: }
230: }
231: }
232: }
233:
234: // check for namespace declarations required
235: String[] uris = null;
236: if (nss == null) {
237: m_xmlWriter.startTagOpen(nsi, element.getLocalName());
238: } else {
239: int base = getNextNamespaceIndex();
240: if (defind >= 0) {
241: m_defaultNamespaceIndex = base + defind;
242: m_defaultNamespaceURI = defuri;
243: }
244: int length = nss.size() / 2;
245: uris = new String[length];
246: int[] nums = new int[length];
247: String[] prefs = new String[length];
248: for (int i = 0; i < length; i++) {
249: prefs[i] = (String) nss.get(i * 2);
250: uris[i] = (String) nss.get(i * 2 + 1);
251: nums[i] = base + i;
252: if (nsi < 0 && uri.equals(uris[i])) {
253: if ((prefix == null && prefs[i] == "")
254: || (prefix != null && prefix
255: .equals(prefs[i]))) {
256: nsi = base + i;
257: }
258: }
259: }
260: m_xmlWriter.pushExtensionNamespaces(uris);
261: m_xmlWriter.startTagNamespaces(nsi, element.getLocalName(),
262: nums, prefs);
263: if (defind >= 0) {
264: m_defaultNamespaceIndex = defind;
265: m_defaultNamespaceURI = defuri;
266: }
267: }
268:
269: // add attributes if present
270: for (int i = 0; i < size; i++) {
271: Attr attr = (Attr) attrs.item(i);
272: if (!XMLNS_NAMESPACE.equals(attr.getNamespaceURI())) {
273: int index = 0;
274: String apref = attr.getPrefix();
275: if (apref != null) {
276: index = findNamespaceIndex(apref, attr
277: .getNamespaceURI());
278: }
279: m_xmlWriter.addAttribute(index, attr.getLocalName(),
280: attr.getValue());
281: }
282: }
283:
284: // check for content present
285: NodeList nodes = element.getChildNodes();
286: size = nodes.getLength();
287: if (size > 0) {
288: m_xmlWriter.closeStartTag();
289: marshalContent(element.getChildNodes());
290: m_xmlWriter.endTag(nsi, element.getLocalName());
291: } else {
292: m_xmlWriter.closeEmptyTag();
293: }
294:
295: // pop namespaces if defined by element
296: if (nss != null) {
297: m_xmlWriter.popExtensionNamespaces();
298: if (defind >= 0) {
299: m_defaultNamespaceURI = null;
300: }
301: }
302: }
303:
304: /**
305: * Unmarshal single node. This unmarshals the next node from the input
306: * stream, up to the close tag of the containing element.
307: *
308: * @return unmarshalled node
309: * @exception JiBXException on error in unmarshalling
310: * @exception IOException on error reading input
311: */
312:
313: protected Node unmarshalNode() throws JiBXException, IOException {
314: while (true) {
315: int cev = m_unmarshalContext.currentEvent();
316: switch (cev) {
317:
318: case IXMLReader.CDSECT: {
319: String text = m_unmarshalContext.getText();
320: m_unmarshalContext.nextToken();
321: return m_document.createCDATASection(text);
322: }
323:
324: case IXMLReader.COMMENT: {
325: String text = m_unmarshalContext.getText();
326: m_unmarshalContext.nextToken();
327: return m_document.createComment(text);
328: }
329:
330: case IXMLReader.END_TAG:
331: return null;
332:
333: case IXMLReader.ENTITY_REF:
334: if (m_unmarshalContext.getText() == null) {
335: String name = m_unmarshalContext.getName();
336: m_unmarshalContext.nextToken();
337: return m_document.createEntityReference(name);
338: } else {
339: String text = accumulateText();
340: return m_document.createTextNode(text);
341: }
342:
343: case IXMLReader.PROCESSING_INSTRUCTION: {
344: String text = m_unmarshalContext.getText();
345: m_unmarshalContext.nextToken();
346: int index = 0;
347: while (++index < text.length()
348: && !isWhitespace(text.charAt(index)))
349: ;
350: if (index < text.length()) {
351: String target = text.substring(0, index);
352: while (++index < text.length()
353: && isWhitespace(text.charAt(index)))
354: ;
355: String data = text.substring(index);
356: return m_document.createProcessingInstruction(
357: target, data);
358: } else {
359: return m_document.createProcessingInstruction(text,
360: "");
361: }
362: }
363:
364: case IXMLReader.START_TAG:
365: return unmarshalElement();
366:
367: case IXMLReader.TEXT:
368: return m_document.createTextNode(accumulateText());
369:
370: default:
371: m_unmarshalContext.nextToken();
372:
373: }
374: }
375: }
376:
377: /**
378: * Unmarshal node content. This unmarshals everything up to the containing
379: * element close tag, adding each component to the content list supplied. On
380: * return, the parse position will always be at an END_TAG.
381: *
382: * @param parent node to which children are to be added
383: * @exception JiBXException on error in unmarshalling
384: * @exception IOException on error reading input
385: */
386:
387: protected void unmarshalContent(Node parent) throws JiBXException,
388: IOException {
389: Node node;
390: while ((node = unmarshalNode()) != null) {
391: parent.appendChild(node);
392: }
393: }
394:
395: /**
396: * Unmarshal element with all attributes and content. This must be called
397: * with the unmarshalling context positioned at a START_TAG event.
398: *
399: * @return unmarshalled element
400: * @exception JiBXException on error in unmarshalling
401: * @exception IOException on error reading input
402: */
403:
404: protected Element unmarshalElement() throws JiBXException,
405: IOException {
406:
407: // start by creating the actual element
408: String uri = m_unmarshalContext.getNamespace();
409: String prefix = m_unmarshalContext.getPrefix();
410: String name = m_unmarshalContext.getName();
411: if (prefix != null) {
412: name = prefix + ':' + name;
413: }
414: Element element = m_document.createElementNS(uri, name);
415:
416: // add all namespace declarations to element
417: int ncount = m_unmarshalContext.getNamespaceCount();
418: for (int i = 0; i < ncount; i++) {
419: prefix = m_unmarshalContext.getNamespacePrefix(i);
420: uri = m_unmarshalContext.getNamespaceUri(i);
421: if (prefix == null) {
422: element.setAttributeNS(XMLNS_NAMESPACE, "xmlns", uri);
423: } else {
424: element.setAttributeNS(XMLNS_NAMESPACE, "xmlns:"
425: + prefix, uri);
426: }
427: }
428:
429: // add all attributes to element
430: int acount = m_unmarshalContext.getAttributeCount();
431: for (int i = 0; i < acount; i++) {
432: prefix = m_unmarshalContext.getAttributePrefix(i);
433: uri = m_unmarshalContext.getAttributeNamespace(i);
434: name = m_unmarshalContext.getAttributeName(i);
435: if (prefix != null) {
436: name = prefix + ':' + name;
437: }
438: String value = m_unmarshalContext.getAttributeValue(i);
439: element.setAttributeNS(uri, name, value);
440: }
441:
442: // add all content to element
443: int event = m_unmarshalContext.nextToken();
444: if (event != IXMLReader.END_TAG) {
445: unmarshalContent(element);
446: }
447: m_unmarshalContext.nextToken();
448: return element;
449: }
450: }
|