001: /* $Id: ContentHandlerToXMLStreamWriter.java,v 1.6 2005/10/25 18:36:24 ryan_shoemaker Exp $
002: *
003: * Copyright (c) 2004, Sun Microsystems, Inc.
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are
008: * met:
009: *
010: * * Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * * Redistributions in binary form must reproduce the above
014: * copyright notice, this list of conditions and the following
015: * disclaimer in the documentation and/or other materials provided
016: * with the distribution.
017: *
018: * * Neither the name of Sun Microsystems, Inc. nor the names of its
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
023: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
024: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
025: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
026: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
027: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
028: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
029: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
030: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
031: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
032: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
033: */
034: package javanet.staxutils;
035:
036: import org.xml.sax.Attributes;
037: import org.xml.sax.Locator;
038: import org.xml.sax.SAXException;
039: import org.xml.sax.helpers.DefaultHandler;
040:
041: import javax.xml.stream.XMLStreamException;
042: import javax.xml.stream.XMLStreamWriter;
043: import java.util.Stack;
044:
045: /**
046: * This is a simple utility class that adapts SAX events into StAX
047: * {@link javax.xml.stream.XMLStreamWriter} events, bridging between
048: * the two parser technologies.
049: *
050: * This ContentHandler does not own the XMLStreamWriter. Therefore, it will
051: * not close or flush the writer at any point.
052: *
053: * @author Ryan.Shoemaker@Sun.COM
054: * @version 1.0
055: */
056: public class ContentHandlerToXMLStreamWriter extends DefaultHandler {
057:
058: // SAX events will be sent to this XMLStreamWriter
059: private final XMLStreamWriter staxWriter;
060:
061: // storage for prefix bindings
062: private final Stack prefixBindings;
063:
064: public ContentHandlerToXMLStreamWriter(XMLStreamWriter staxCore) {
065: this .staxWriter = staxCore;
066: prefixBindings = new Stack(); // default of 10 seems reasonable
067: }
068:
069: /*
070: * (non-Javadoc)
071: *
072: * @see org.xml.sax.ContentHandler#endDocument()
073: */
074: public void endDocument() throws SAXException {
075: try {
076: staxWriter.writeEndDocument();
077: staxWriter.flush();
078: } catch (XMLStreamException e) {
079: throw new SAXException(e);
080: }
081: }
082:
083: /*
084: * (non-Javadoc)
085: *
086: * @see org.xml.sax.ContentHandler#startDocument()
087: */
088: public void startDocument() throws SAXException {
089: try {
090: staxWriter.writeStartDocument();
091: } catch (XMLStreamException e) {
092: throw new SAXException(e);
093: }
094: }
095:
096: /*
097: * (non-Javadoc)
098: *
099: * @see org.xml.sax.ContentHandler#characters(char[], int, int)
100: */
101: public void characters(char[] ch, int start, int length)
102: throws SAXException {
103:
104: try {
105: staxWriter.writeCharacters(ch, start, length);
106: } catch (XMLStreamException e) {
107: throw new SAXException(e);
108: }
109:
110: }
111:
112: /*
113: * (non-Javadoc)
114: *
115: * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
116: */
117: public void ignorableWhitespace(char[] ch, int start, int length)
118: throws SAXException {
119:
120: characters(ch, start, length);
121: }
122:
123: /*
124: * (non-Javadoc)
125: *
126: * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String)
127: */
128: public void endPrefixMapping(String prefix) throws SAXException {
129: // TODO: no-op?
130:
131: // I think we can ignore these SAX events because StAX
132: // automatically scopes the prefix bindings.
133: }
134:
135: /*
136: * (non-Javadoc)
137: *
138: * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String)
139: */
140: public void skippedEntity(String name) throws SAXException {
141: try {
142: staxWriter.writeEntityRef(name);
143: } catch (XMLStreamException e) {
144: throw new SAXException(e);
145: }
146: }
147:
148: /*
149: * (non-Javadoc)
150: *
151: * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator)
152: */
153: public void setDocumentLocator(Locator locator) {
154: // TODO: no-op?
155: // there doesn't seem to be any way to pass location info
156: // along to the XMLStreamWriter. On the XMLEventWriter side, you
157: // can set the location info on the event objects.
158: }
159:
160: /*
161: * (non-Javadoc)
162: *
163: * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String,
164: * java.lang.String)
165: */
166: public void processingInstruction(String target, String data)
167: throws SAXException {
168:
169: try {
170: staxWriter.writeProcessingInstruction(target, data);
171: } catch (XMLStreamException e) {
172: throw new SAXException(e);
173: }
174:
175: }
176:
177: /*
178: * (non-Javadoc)
179: *
180: * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String,
181: * java.lang.String)
182: */
183: public void startPrefixMapping(String prefix, String uri)
184: throws SAXException {
185:
186: if (prefix.equals("xml")) {
187: return;
188: }
189:
190: // defend against parsers that pass null in for "xmlns" prefix
191: if (prefix == null) {
192: prefix = "";
193: }
194:
195: prefixBindings.add(prefix);
196: prefixBindings.add(uri);
197: }
198:
199: /*
200: * (non-Javadoc)
201: *
202: * @see org.xml.sax.ContentHandler#endElement(java.lang.String,
203: * java.lang.String, java.lang.String)
204: */
205: public void endElement(String namespaceURI, String localName,
206: String qName) throws SAXException {
207:
208: try {
209: // TODO: is this all we have to do?
210: staxWriter.writeEndElement();
211: } catch (XMLStreamException e) {
212: throw new SAXException(e);
213: }
214: }
215:
216: /*
217: * (non-Javadoc)
218: *
219: * @see org.xml.sax.ContentHandler#startElement(java.lang.String,
220: * java.lang.String, java.lang.String, org.xml.sax.Attributes)
221: */
222: public void startElement(String namespaceURI, String localName,
223: String qName, Attributes atts) throws SAXException {
224:
225: try {
226: staxWriter.writeStartElement(getPrefix(qName), localName,
227: namespaceURI);
228:
229: String uri, prefix;
230: while (prefixBindings.size() != 0) {
231: uri = (String) prefixBindings.pop();
232: prefix = (String) prefixBindings.pop();
233: if (prefix.length() == 0) {
234: staxWriter.setDefaultNamespace(uri);
235: } else {
236: staxWriter.setPrefix(prefix, uri);
237: }
238:
239: // this method handles "", null, and "xmlns" prefixes properly
240: staxWriter.writeNamespace(prefix, uri);
241: }
242:
243: writeAttributes(atts);
244: } catch (XMLStreamException e) {
245: throw new SAXException(e);
246: }
247:
248: }
249:
250: /**
251: * Generate a StAX writeAttribute event for each attribute
252: *
253: * @param atts
254: * attributes from the SAX event
255: */
256: private void writeAttributes(Attributes atts)
257: throws XMLStreamException {
258: for (int i = 0; i < atts.getLength(); i++) {
259: final String prefix = getPrefix(atts.getQName(i));
260: if (!prefix.equals("xmlns")) { // defend againts broken transformers that report xmlns decls as attrs
261: staxWriter.writeAttribute(prefix, atts.getURI(i), atts
262: .getLocalName(i), atts.getValue(i));
263: }
264: }
265: }
266:
267: /**
268: * Pull the prefix off of the specified QName.
269: *
270: * @param qName
271: * the QName
272: * @return the prefix or the empty string if it doesn't exist.
273: */
274: private String getPrefix(String qName) {
275: int idx = qName.indexOf(':');
276: if (idx == -1) {
277: return "";
278: } else {
279: return qName.substring(0, idx);
280: }
281: }
282:
283: }
|