001: /*
002: * Copyright (C) 2004, 2005, 2006 Joe Walnes.
003: * Copyright (C) 2006, 2007, 2008 XStream Committers.
004: * All rights reserved.
005: *
006: * The software in this package is published under the terms of the BSD
007: * style license a copy of which has been included with this distribution in
008: * the LICENSE.txt file.
009: *
010: * Created on 29. September 2004 by James Strachan
011: */
012: package com.thoughtworks.xstream.io.xml;
013:
014: import com.thoughtworks.xstream.io.StreamException;
015:
016: import javax.xml.namespace.QName;
017: import javax.xml.stream.XMLStreamException;
018: import javax.xml.stream.XMLStreamWriter;
019:
020: /**
021: * A stream writing that outputs to a StAX stream writer
022: *
023: * @author James Strachan
024: * @version $Revision: 1388 $
025: */
026: public class StaxWriter extends AbstractXmlWriter {
027:
028: private final QNameMap qnameMap;
029: private final XMLStreamWriter out;
030: private final boolean writeEnclosingDocument;
031: private boolean namespaceRepairingMode;
032:
033: private int tagDepth;
034:
035: public StaxWriter(QNameMap qnameMap, XMLStreamWriter out)
036: throws XMLStreamException {
037: this (qnameMap, out, true, true);
038: }
039:
040: /**
041: * Allows a StaxWriter to be created for partial XML output
042: *
043: * @param qnameMap is the mapper of Java class names to QNames
044: * @param out the stream to output to
045: * @param writeEnclosingDocument a flag to indicate whether or not the start/end document events should be written
046: * @throws XMLStreamException if the events could not be written to the output
047: */
048: public StaxWriter(QNameMap qnameMap, XMLStreamWriter out,
049: boolean writeEnclosingDocument,
050: boolean namespaceRepairingMode) throws XMLStreamException {
051: this (qnameMap, out, writeEnclosingDocument,
052: namespaceRepairingMode, new XmlFriendlyReplacer());
053: }
054:
055: /**
056: * Allows a StaxWriter to be created for partial XML output
057: *
058: * @param qnameMap is the mapper of Java class names to QNames
059: * @param out the stream to output to
060: * @param writeEnclosingDocument a flag to indicate whether or not the start/end document events should be written
061: * @param replacer the xml-friendly replacer to escape Java names
062: * @throws XMLStreamException if the events could not be written to the output
063: * @since 1.2
064: */
065: public StaxWriter(QNameMap qnameMap, XMLStreamWriter out,
066: boolean writeEnclosingDocument,
067: boolean namespaceRepairingMode, XmlFriendlyReplacer replacer)
068: throws XMLStreamException {
069: super (replacer);
070: this .qnameMap = qnameMap;
071: this .out = out;
072: this .writeEnclosingDocument = writeEnclosingDocument;
073: this .namespaceRepairingMode = namespaceRepairingMode;
074: if (writeEnclosingDocument) {
075: out.writeStartDocument();
076: }
077: }
078:
079: public void flush() {
080: try {
081: out.flush();
082: } catch (XMLStreamException e) {
083: throw new StreamException(e);
084: }
085: }
086:
087: /**
088: * Call this method when you're finished with me
089: */
090: public void close() {
091: try {
092: out.close();
093: } catch (XMLStreamException e) {
094: throw new StreamException(e);
095: }
096: }
097:
098: public void addAttribute(String name, String value) {
099: try {
100: out.writeAttribute(escapeXmlName(name), value);
101: } catch (XMLStreamException e) {
102: throw new StreamException(e);
103: }
104: }
105:
106: public void endNode() {
107: try {
108: tagDepth--;
109: out.writeEndElement();
110: if (tagDepth == 0 && writeEnclosingDocument) {
111: out.writeEndDocument();
112: }
113: } catch (XMLStreamException e) {
114: throw new StreamException(e);
115: }
116: }
117:
118: public void setValue(String text) {
119: try {
120: out.writeCharacters(text);
121: } catch (XMLStreamException e) {
122: throw new StreamException(e);
123: }
124: }
125:
126: public void startNode(String name) {
127: try {
128: QName qname = qnameMap.getQName(escapeXmlName(name));
129: String prefix = qname.getPrefix();
130: String uri = qname.getNamespaceURI();
131:
132: // before you ask - yes it really is this complicated to output QNames to StAX
133: // handling both repair namespace modes :)
134:
135: boolean hasPrefix = prefix != null && prefix.length() > 0;
136: boolean hasURI = uri != null && uri.length() > 0;
137: boolean writeNamespace = false;
138:
139: if (hasURI) {
140: if (hasPrefix) {
141: String currentNamespace = out.getNamespaceContext()
142: .getNamespaceURI(prefix);
143: if (currentNamespace == null
144: || !currentNamespace.equals(uri)) {
145: writeNamespace = true;
146: }
147: } else {
148: String defaultNamespace = out.getNamespaceContext()
149: .getNamespaceURI("");
150: if (defaultNamespace == null
151: || !defaultNamespace.equals(uri)) {
152: writeNamespace = true;
153: }
154: }
155: }
156:
157: if (hasPrefix) {
158: out.setPrefix(prefix, uri);
159: } else if (hasURI) {
160: if (writeNamespace) {
161: out.setDefaultNamespace(uri);
162: }
163: }
164: out.writeStartElement(prefix, qname.getLocalPart(), uri);
165: if (hasURI && writeNamespace && !isNamespaceRepairingMode()) {
166: if (hasPrefix) {
167: out.writeNamespace(prefix, uri);
168: } else {
169: out.writeDefaultNamespace(uri);
170: }
171: }
172: tagDepth++;
173: } catch (XMLStreamException e) {
174: throw new StreamException(e);
175: }
176: }
177:
178: /**
179: * Is StAX namespace repairing mode on or off?
180: */
181: public boolean isNamespaceRepairingMode() {
182: return namespaceRepairingMode;
183: }
184:
185: }
|