001: /* $Id: XMLEventReaderToContentHandler.java,v 1.10 2006/01/23 18:50:02 sandoz 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 java.util.Iterator;
037: import javanet.staxutils.helpers.XMLFilterImplEx;
038:
039: import javax.xml.namespace.QName;
040: import javax.xml.stream.XMLEventReader;
041: import javax.xml.stream.XMLStreamConstants;
042: import javax.xml.stream.XMLStreamException;
043: import javax.xml.stream.Location;
044: import javax.xml.stream.events.Attribute;
045: import javax.xml.stream.events.Characters;
046: import javax.xml.stream.events.Comment;
047: import javax.xml.stream.events.EndElement;
048: import javax.xml.stream.events.Namespace;
049: import javax.xml.stream.events.ProcessingInstruction;
050: import javax.xml.stream.events.StartElement;
051: import javax.xml.stream.events.XMLEvent;
052:
053: import org.xml.sax.Attributes;
054: import org.xml.sax.Locator;
055: import org.xml.sax.SAXException;
056: import org.xml.sax.helpers.AttributesImpl;
057:
058: /**
059: * This is a simple utility class that adapts StAX events from an
060: * {@link javax.xml.stream.XMLEventReader} to SAX events on a
061: * {@link org.xml.sax.ContentHandler}, bridging between the two
062: * parser technologies.
063: *
064: * @author Ryan.Shoemaker@Sun.COM
065: * @version 1.0
066: */
067: public class XMLEventReaderToContentHandler implements
068: StAXReaderToContentHandler {
069:
070: // StAX event source
071: private final XMLEventReader staxEventReader;
072:
073: // SAX event sinks
074: private XMLFilterImplEx filter;
075:
076: /**
077: * Construct a new StAX to SAX adapter that will convert a StAX event
078: * stream into a SAX event stream.
079: *
080: * @param staxCore
081: * StAX event source
082: * @param filter
083: * SAX event sink
084: */
085: public XMLEventReaderToContentHandler(XMLEventReader staxCore,
086: XMLFilterImplEx filter) {
087: staxEventReader = staxCore;
088:
089: this .filter = filter;
090: }
091:
092: /*
093: * @see StAXReaderToContentHandler#bridge()
094: */
095: public void bridge() throws XMLStreamException {
096:
097: try {
098: // remembers the nest level of elements to know when we are done.
099: int depth = 0;
100:
101: XMLEvent event = staxEventReader.peek();
102:
103: boolean readWhole = false;
104:
105: if (event.isStartDocument()) {
106: readWhole = true;
107: } else if (!event.isStartElement())
108: throw new IllegalStateException();
109:
110: // if the parser is on START_DOCUMENT, skip ahead to the first element
111: do {
112: event = staxEventReader.nextEvent();
113: } while (!event.isStartElement());
114:
115: handleStartDocument(event);
116:
117: OUTER: while (true) {
118: // These are all of the events listed in the javadoc for
119: // XMLEvent.
120: // The spec only really describes 11 of them.
121: switch (event.getEventType()) {
122: case XMLStreamConstants.START_ELEMENT:
123: depth++;
124: handleStartElement(event.asStartElement());
125: break;
126: case XMLStreamConstants.END_ELEMENT:
127: handleEndElement(event.asEndElement());
128: depth--;
129: if (depth == 0)
130: break OUTER;
131: break;
132: case XMLStreamConstants.CHARACTERS:
133: handleCharacters(event.asCharacters());
134: break;
135: case XMLStreamConstants.ENTITY_REFERENCE:
136: handleEntityReference();
137: break;
138: case XMLStreamConstants.PROCESSING_INSTRUCTION:
139: handlePI((ProcessingInstruction) event);
140: break;
141: case XMLStreamConstants.COMMENT:
142: handleComment((Comment) event);
143: break;
144: case XMLStreamConstants.DTD:
145: handleDTD();
146: break;
147: case XMLStreamConstants.ATTRIBUTE:
148: handleAttribute();
149: break;
150: case XMLStreamConstants.NAMESPACE:
151: handleNamespace();
152: break;
153: case XMLStreamConstants.CDATA:
154: handleCDATA();
155: break;
156: case XMLStreamConstants.ENTITY_DECLARATION:
157: handleEntityDecl();
158: break;
159: case XMLStreamConstants.NOTATION_DECLARATION:
160: handleNotationDecl();
161: break;
162: case XMLStreamConstants.SPACE:
163: handleSpace();
164: break;
165: default:
166: throw new InternalError("processing event: "
167: + event);
168: }
169:
170: event = staxEventReader.nextEvent();
171: }
172:
173: handleEndDocument();
174:
175: if (readWhole) {
176: // if we started from START_DOCUMENT, read the whole document
177: while (staxEventReader.hasNext())
178: staxEventReader.nextEvent();
179: }
180: } catch (SAXException e) {
181: throw new XMLStreamException(e);
182: }
183: }
184:
185: private void handleEndDocument() throws SAXException {
186: filter.endDocument();
187: }
188:
189: private void handleStartDocument(final XMLEvent event)
190: throws SAXException {
191: final Location location = event.getLocation();
192: if (location != null) {
193: filter.setDocumentLocator(new Locator() {
194: public int getColumnNumber() {
195: return location.getColumnNumber();
196: }
197:
198: public int getLineNumber() {
199: return location.getLineNumber();
200: }
201:
202: public String getPublicId() {
203: return location.getPublicId();
204: }
205:
206: public String getSystemId() {
207: return location.getSystemId();
208: }
209: });
210: } else {
211: filter.setDocumentLocator(new DummyLocator());
212: }
213: filter.startDocument();
214: }
215:
216: private void handlePI(ProcessingInstruction event)
217: throws XMLStreamException {
218: try {
219: filter.processingInstruction(event.getTarget(), event
220: .getData());
221: } catch (SAXException e) {
222: throw new XMLStreamException(e);
223: }
224: }
225:
226: private void handleCharacters(Characters event)
227: throws XMLStreamException {
228: try {
229: filter.characters(event.getData().toCharArray(), 0, event
230: .getData().length());
231: } catch (SAXException e) {
232: throw new XMLStreamException(e);
233: }
234: }
235:
236: private void handleEndElement(EndElement event)
237: throws XMLStreamException {
238: QName qName = event.getName();
239:
240: try {
241: // fire endElement
242: String prefix = qName.getPrefix();
243: String rawname;
244: if (prefix == null || prefix.length() == 0)
245: rawname = qName.getLocalPart();
246: else
247: rawname = prefix + ':' + qName.getLocalPart();
248:
249: filter.endElement(qName.getNamespaceURI(), qName
250: .getLocalPart(), rawname);
251:
252: // end namespace bindings
253: for (Iterator i = event.getNamespaces(); i.hasNext();) {
254: String nsprefix = ((Namespace) i.next()).getPrefix();
255: if (nsprefix == null) { // true for default namespace
256: nsprefix = "";
257: }
258: filter.endPrefixMapping(nsprefix);
259: }
260: } catch (SAXException e) {
261: throw new XMLStreamException(e);
262: }
263: }
264:
265: private void handleStartElement(StartElement event)
266: throws XMLStreamException {
267: try {
268: // start namespace bindings
269: for (Iterator i = event.getNamespaces(); i.hasNext();) {
270: String prefix = ((Namespace) i.next()).getPrefix();
271: if (prefix == null) { // true for default namespace
272: prefix = "";
273: }
274: filter.startPrefixMapping(prefix, event
275: .getNamespaceURI(prefix));
276: }
277:
278: // fire startElement
279: QName qName = event.getName();
280: String prefix = qName.getPrefix();
281: String rawname;
282: if (prefix == null || prefix.length() == 0)
283: rawname = qName.getLocalPart();
284: else
285: rawname = prefix + ':' + qName.getLocalPart();
286: Attributes saxAttrs = getAttributes(event);
287: filter.startElement(qName.getNamespaceURI(), qName
288: .getLocalPart(), rawname, saxAttrs);
289: } catch (SAXException e) {
290: throw new XMLStreamException(e);
291: }
292: }
293:
294: /**
295: * Get the attributes associated with the given START_ELEMENT StAXevent.
296: *
297: * @return the StAX attributes converted to an org.xml.sax.Attributes
298: */
299: private Attributes getAttributes(StartElement event) {
300: AttributesImpl attrs = new AttributesImpl();
301:
302: if (!event.isStartElement()) {
303: throw new InternalError(
304: "getAttributes() attempting to process: " + event);
305: }
306:
307: // Add namspace declarations if required
308: if (filter.getNamespacePrefixes()) {
309: for (Iterator i = event.getNamespaces(); i.hasNext();) {
310: Namespace staxNamespace = (javax.xml.stream.events.Namespace) i
311: .next();
312: String uri = staxNamespace.getNamespaceURI();
313: if (uri == null)
314: uri = "";
315:
316: String prefix = staxNamespace.getPrefix();
317: if (prefix == null)
318: prefix = "";
319:
320: String qName = "xmlns";
321: if (prefix.length() == 0) {
322: prefix = qName;
323: } else {
324: qName = qName + ':' + prefix;
325: }
326: attrs.addAttribute("http://www.w3.org/2000/xmlns/",
327: prefix, qName, "CDATA", uri);
328: }
329: }
330:
331: // gather non-namespace attrs
332: for (Iterator i = event.getAttributes(); i.hasNext();) {
333: Attribute staxAttr = (javax.xml.stream.events.Attribute) i
334: .next();
335:
336: String uri = staxAttr.getName().getNamespaceURI();
337: if (uri == null)
338: uri = "";
339: String localName = staxAttr.getName().getLocalPart();
340: String prefix = staxAttr.getName().getPrefix();
341: String qName;
342: if (prefix == null || prefix.length() == 0)
343: qName = localName;
344: else
345: qName = prefix + ':' + localName;
346: String type = staxAttr.getDTDType();
347: String value = staxAttr.getValue();
348:
349: attrs.addAttribute(uri, localName, qName, type, value);
350: }
351:
352: return attrs;
353: }
354:
355: private void handleNamespace() {
356: // no-op ???
357: // namespace events don't normally occur outside of a startElement
358: // or endElement
359: }
360:
361: private void handleAttribute() {
362: // no-op ???
363: // attribute events don't normally occur outside of a startElement
364: // or endElement
365: }
366:
367: private void handleDTD() {
368: // no-op ???
369: // it seems like we need to pass this info along, but how?
370: }
371:
372: private void handleComment(Comment comment)
373: throws XMLStreamException {
374: try {
375: String text = comment.getText();
376: filter.comment(text.toCharArray(), 0, text.length());
377: } catch (SAXException e) {
378: throw new XMLStreamException(e);
379: }
380: }
381:
382: private void handleEntityReference() {
383: // no-op ???
384: }
385:
386: private void handleSpace() {
387: // no-op ???
388: // this event is listed in the javadoc, but not in the spec.
389: }
390:
391: private void handleNotationDecl() {
392: // no-op ???
393: // this event is listed in the javadoc, but not in the spec.
394: }
395:
396: private void handleEntityDecl() {
397: // no-op ???
398: // this event is listed in the javadoc, but not in the spec.
399: }
400:
401: private void handleCDATA() {
402: // no-op ???
403: // this event is listed in the javadoc, but not in the spec.
404: }
405: }
|