001: package com.sun.istack;
002:
003: import org.xml.sax.ContentHandler;
004: import org.xml.sax.SAXException;
005: import org.xml.sax.Locator;
006: import org.xml.sax.Attributes;
007: import org.xml.sax.helpers.AttributesImpl;
008:
009: import javax.xml.stream.XMLStreamReader;
010: import javax.xml.stream.XMLStreamException;
011: import javax.xml.stream.XMLStreamConstants;
012: import javax.xml.namespace.QName;
013:
014: /**
015: * This is a simple utility class that adapts StAX events from an
016: * {@link XMLStreamReader} to SAX events on a
017: * {@link ContentHandler}, bridging between the two
018: * parser technologies.
019: *
020: * @author Ryan.Shoemaker@Sun.COM
021: * @version 1.0
022: */
023: public class XMLStreamReaderToContentHandler {
024:
025: // StAX event source
026: private final XMLStreamReader staxStreamReader;
027:
028: // SAX event sink
029: private final ContentHandler saxHandler;
030:
031: // if true, when the conversion is completed, leave the cursor to the last
032: // event that was fired (such as end element)
033: private boolean eagerQuit;
034:
035: /**
036: * If true, not start/endDocument event.
037: */
038: private boolean fragment;
039:
040: /**
041: * Construct a new StAX to SAX adapter that will convert a StAX event
042: * stream into a SAX event stream.
043: *
044: * @param staxCore
045: * StAX event source
046: * @param saxCore
047: * SAXevent sink
048: */
049: public XMLStreamReaderToContentHandler(XMLStreamReader staxCore,
050: ContentHandler saxCore, boolean eagerQuit, boolean fragment) {
051: this .staxStreamReader = staxCore;
052: this .saxHandler = saxCore;
053: this .eagerQuit = eagerQuit;
054: this .fragment = fragment;
055: }
056:
057: /*
058: * @see StAXReaderToContentHandler#bridge()
059: */
060: public void bridge() throws XMLStreamException {
061:
062: try {
063: // remembers the nest level of elements to know when we are done.
064: int depth = 0;
065:
066: // if the parser is at the start tag, proceed to the first element
067: int event = staxStreamReader.getEventType();
068: if (event == XMLStreamConstants.START_DOCUMENT) {
069: // nextTag doesn't correctly handle DTDs
070: while (!staxStreamReader.isStartElement())
071: event = staxStreamReader.next();
072: }
073:
074: if (event != XMLStreamConstants.START_ELEMENT)
075: throw new IllegalStateException(
076: "The current event is not START_ELEMENT\n but "
077: + event);
078:
079: handleStartDocument();
080:
081: OUTER: do {
082: // These are all of the events listed in the javadoc for
083: // XMLEvent.
084: // The spec only really describes 11 of them.
085: switch (event) {
086: case XMLStreamConstants.START_ELEMENT:
087: depth++;
088: handleStartElement();
089: break;
090: case XMLStreamConstants.END_ELEMENT:
091: handleEndElement();
092: depth--;
093: if (depth == 0 && eagerQuit)
094: break OUTER;
095: break;
096: case XMLStreamConstants.CHARACTERS:
097: handleCharacters();
098: break;
099: case XMLStreamConstants.ENTITY_REFERENCE:
100: handleEntityReference();
101: break;
102: case XMLStreamConstants.PROCESSING_INSTRUCTION:
103: handlePI();
104: break;
105: case XMLStreamConstants.COMMENT:
106: handleComment();
107: break;
108: case XMLStreamConstants.DTD:
109: handleDTD();
110: break;
111: case XMLStreamConstants.ATTRIBUTE:
112: handleAttribute();
113: break;
114: case XMLStreamConstants.NAMESPACE:
115: handleNamespace();
116: break;
117: case XMLStreamConstants.CDATA:
118: handleCDATA();
119: break;
120: case XMLStreamConstants.ENTITY_DECLARATION:
121: handleEntityDecl();
122: break;
123: case XMLStreamConstants.NOTATION_DECLARATION:
124: handleNotationDecl();
125: break;
126: case XMLStreamConstants.SPACE:
127: handleSpace();
128: break;
129: default:
130: throw new InternalError("processing event: "
131: + event);
132: }
133:
134: event = staxStreamReader.next();
135: } while (depth != 0);
136:
137: handleEndDocument();
138: } catch (SAXException e) {
139: throw new XMLStreamException2(e);
140: }
141: }
142:
143: private void handleEndDocument() throws SAXException {
144: if (fragment)
145: return;
146:
147: saxHandler.endDocument();
148: }
149:
150: private void handleStartDocument() throws SAXException {
151: if (fragment)
152: return;
153:
154: saxHandler.setDocumentLocator(new Locator() {
155: public int getColumnNumber() {
156: return staxStreamReader.getLocation().getColumnNumber();
157: }
158:
159: public int getLineNumber() {
160: return staxStreamReader.getLocation().getLineNumber();
161: }
162:
163: public String getPublicId() {
164: return staxStreamReader.getLocation().getPublicId();
165: }
166:
167: public String getSystemId() {
168: return staxStreamReader.getLocation().getSystemId();
169: }
170: });
171: saxHandler.startDocument();
172: }
173:
174: private void handlePI() throws XMLStreamException {
175: try {
176: saxHandler.processingInstruction(staxStreamReader
177: .getPITarget(), staxStreamReader.getPIData());
178: } catch (SAXException e) {
179: throw new XMLStreamException2(e);
180: }
181: }
182:
183: private void handleCharacters() throws XMLStreamException {
184: try {
185: saxHandler.characters(staxStreamReader.getTextCharacters(),
186: staxStreamReader.getTextStart(), staxStreamReader
187: .getTextLength());
188: } catch (SAXException e) {
189: throw new XMLStreamException2(e);
190: }
191: }
192:
193: private void handleEndElement() throws XMLStreamException {
194: QName qName = staxStreamReader.getName();
195:
196: try {
197: String pfix = qName.getPrefix();
198: String rawname = (pfix == null || pfix.length() == 0) ? qName
199: .getLocalPart()
200: : pfix + ':' + qName.getLocalPart();
201: // fire endElement
202: saxHandler.endElement(qName.getNamespaceURI(), qName
203: .getLocalPart(), rawname);
204:
205: // end namespace bindings
206: int nsCount = staxStreamReader.getNamespaceCount();
207: for (int i = nsCount - 1; i >= 0; i--) {
208: String prefix = staxStreamReader.getNamespacePrefix(i);
209: if (prefix == null) { // true for default namespace
210: prefix = "";
211: }
212: saxHandler.endPrefixMapping(prefix);
213: }
214: } catch (SAXException e) {
215: throw new XMLStreamException2(e);
216: }
217: }
218:
219: private void handleStartElement() throws XMLStreamException {
220:
221: try {
222: // start namespace bindings
223: int nsCount = staxStreamReader.getNamespaceCount();
224: for (int i = 0; i < nsCount; i++) {
225: saxHandler.startPrefixMapping(fixNull(staxStreamReader
226: .getNamespacePrefix(i)),
227: fixNull(staxStreamReader.getNamespaceURI(i)));
228: }
229:
230: // fire startElement
231: QName qName = staxStreamReader.getName();
232: String prefix = qName.getPrefix();
233: String rawname;
234: if (prefix == null || prefix.length() == 0)
235: rawname = qName.getLocalPart();
236: else
237: rawname = prefix + ':' + qName.getLocalPart();
238: Attributes attrs = getAttributes();
239: saxHandler.startElement(qName.getNamespaceURI(), qName
240: .getLocalPart(), rawname, attrs);
241: } catch (SAXException e) {
242: throw new XMLStreamException2(e);
243: }
244: }
245:
246: private static String fixNull(String s) {
247: if (s == null)
248: return "";
249: else
250: return s;
251: }
252:
253: /**
254: * Get the attributes associated with the given START_ELEMENT or ATTRIBUTE
255: * StAXevent.
256: *
257: * @return the StAX attributes converted to an org.xml.sax.Attributes
258: */
259: private Attributes getAttributes() {
260: AttributesImpl attrs = new AttributesImpl();
261:
262: int eventType = staxStreamReader.getEventType();
263: if (eventType != XMLStreamConstants.ATTRIBUTE
264: && eventType != XMLStreamConstants.START_ELEMENT) {
265: throw new InternalError(
266: "getAttributes() attempting to process: "
267: + eventType);
268: }
269:
270: // in SAX, namespace declarations are not part of attributes by default.
271: // (there's a property to control that, but as far as we are concerned
272: // we don't use it.) So don't add xmlns:* to attributes.
273:
274: // gather non-namespace attrs
275: for (int i = 0; i < staxStreamReader.getAttributeCount(); i++) {
276: String uri = staxStreamReader.getAttributeNamespace(i);
277: if (uri == null)
278: uri = "";
279: String localName = staxStreamReader
280: .getAttributeLocalName(i);
281: String prefix = staxStreamReader.getAttributePrefix(i);
282: String qName;
283: if (prefix == null || prefix.length() == 0)
284: qName = localName;
285: else
286: qName = prefix + ':' + localName;
287: String type = staxStreamReader.getAttributeType(i);
288: String value = staxStreamReader.getAttributeValue(i);
289:
290: attrs.addAttribute(uri, localName, qName, type, value);
291: }
292:
293: return attrs;
294: }
295:
296: private void handleNamespace() {
297: // no-op ???
298: // namespace events don't normally occur outside of a startElement
299: // or endElement
300: }
301:
302: private void handleAttribute() {
303: // no-op ???
304: // attribute events don't normally occur outside of a startElement
305: // or endElement
306: }
307:
308: private void handleDTD() {
309: // no-op ???
310: // it seems like we need to pass this info along, but how?
311: }
312:
313: private void handleComment() {
314: // no-op ???
315: }
316:
317: private void handleEntityReference() {
318: // no-op ???
319: }
320:
321: private void handleSpace() {
322: // no-op ???
323: // this event is listed in the javadoc, but not in the spec.
324: }
325:
326: private void handleNotationDecl() {
327: // no-op ???
328: // this event is listed in the javadoc, but not in the spec.
329: }
330:
331: private void handleEntityDecl() {
332: // no-op ???
333: // this event is listed in the javadoc, but not in the spec.
334: }
335:
336: private void handleCDATA() {
337: // no-op ???
338: // this event is listed in the javadoc, but not in the spec.
339: }
340: }
|