001: /*
002: * $Id: StAXEventContentHandler.java,v 1.5 2004/07/05 23:15:11 cniles Exp $
003: *
004: * Copyright (c) 2004, Christian Niles, Unit12
005: * All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions are 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 copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: *
017: * * Neither the name of Christian Niles, Unit12, nor the names of its
018: * contributors may be used to endorse or promote products derived from
019: * this software without specific prior written permission.
020: *
021: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
022: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
023: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
024: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
025: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
026: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
027: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
028: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
029: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
030: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
031: * POSSIBILITY OF SUCH DAMAGE.
032: *
033: */
034: package javanet.staxutils;
035:
036: import java.util.ArrayList;
037: import java.util.Collection;
038: import java.util.Collections;
039: import java.util.HashMap;
040: import java.util.Iterator;
041: import java.util.List;
042: import java.util.Map;
043:
044: import javax.xml.stream.XMLEventFactory;
045: import javax.xml.stream.XMLStreamException;
046: import javax.xml.stream.events.*;
047: import javax.xml.stream.util.XMLEventConsumer;
048:
049: import org.xml.sax.Attributes;
050: import org.xml.sax.SAXException;
051:
052: /**
053: * SAX ContentHandler that writes events to a StAX {@link XMLEventConsumer}.
054: *
055: * @author Christian Niles
056: * @version $Revision: 1.5 $
057: */
058: public class StAXEventContentHandler extends StAXContentHandler {
059:
060: /** The consumer to which events will be written. */
061: private XMLEventConsumer consumer;
062:
063: /** The factory used to construct events. */
064: private XMLEventFactory eventFactory;
065:
066: /**
067: * A stack of {@link List}s, each containing {@link Namespace}events
068: * constructed from a {@link StartElement}event. It is necessary to keep
069: * these namespaces so we can report them to the {@link EndElement}event.
070: */
071: private List namespaceStack = new ArrayList();
072:
073: /**
074: * Constructs a default instance with a default event factory. You must set
075: * the {@link XMLEventConsumer}via the
076: * {@link #setEventConsumer(XMLEventConsumer)}method.
077: */
078: public StAXEventContentHandler() {
079:
080: eventFactory = XMLEventFactory.newInstance();
081:
082: }
083:
084: /**
085: * Constructs an instance that writes events to the provided
086: * XMLEventConsumer. Events will be constructed from a default
087: * XMLEventFactory instance.
088: *
089: * @param consumer The {@link XMLEventConsumer}to which events will be
090: * written.
091: */
092: public StAXEventContentHandler(XMLEventConsumer consumer) {
093:
094: this .consumer = consumer;
095: eventFactory = XMLEventFactory.newInstance();
096:
097: }
098:
099: /**
100: * Constructs an instance that writes events constructed with the provided
101: * XMLEventFactory to the provided XMLEventConsumer
102: *
103: * @param consumer The {@link XMLEventConsumer} to which events will be
104: * written.
105: * @param factory The {@link XMLEventFactory} used to construct events. If
106: * <code>null</code>, a default instance will be constructed.
107: */
108: public StAXEventContentHandler(XMLEventConsumer consumer,
109: XMLEventFactory factory) {
110:
111: this .consumer = consumer;
112: if (factory != null) {
113:
114: this .eventFactory = factory;
115:
116: } else {
117:
118: eventFactory = XMLEventFactory.newInstance();
119:
120: }
121:
122: }
123:
124: /**
125: * Returns a reference to the {@link XMLEventConsumer} to which events will
126: * be written.
127: *
128: * @return The {@link XMLEventConsumer} to which events will be written.
129: */
130: public XMLEventConsumer getEventConsumer() {
131:
132: return consumer;
133:
134: }
135:
136: /**
137: * Sets the {@link XMLEventConsumer} to which events are written.
138: *
139: * @param consumer The {@link XMLEventConsumer} to which events will be
140: * written.
141: */
142: public void setEventConsumer(XMLEventConsumer consumer) {
143:
144: this .consumer = consumer;
145:
146: }
147:
148: /**
149: * Returns a reference to the {@link XMLEventFactory} used to construct
150: * events.
151: *
152: * @return The {@link XMLEventFactory} used to construct events.
153: */
154: public XMLEventFactory getEventFactory() {
155:
156: return eventFactory;
157:
158: }
159:
160: /**
161: * Sets the {@link XMLEventFactory} used to create events.
162: *
163: * @param factory The {@link XMLEventFactory} used to create events.
164: */
165: public void setEventFactory(XMLEventFactory factory) {
166:
167: this .eventFactory = factory;
168:
169: }
170:
171: public void startDocument() throws SAXException {
172:
173: super .startDocument();
174:
175: // clear the namespaces in case we ended in error before.
176: namespaceStack.clear();
177:
178: eventFactory.setLocation(getCurrentLocation());
179: try {
180:
181: consumer.add(eventFactory.createStartDocument());
182:
183: } catch (XMLStreamException e) {
184:
185: throw new SAXException(e);
186:
187: }
188:
189: }
190:
191: public void endDocument() throws SAXException {
192:
193: eventFactory.setLocation(getCurrentLocation());
194:
195: try {
196:
197: consumer.add(eventFactory.createEndDocument());
198:
199: } catch (XMLStreamException e) {
200:
201: throw new SAXException(e);
202:
203: }
204:
205: super .endDocument();
206:
207: // clear the namespaces
208: namespaceStack.clear();
209:
210: }
211:
212: public void startElement(String uri, String localName,
213: String qName, Attributes attributes) throws SAXException {
214:
215: // set document location
216: eventFactory.setLocation(getCurrentLocation());
217:
218: // create attribute and namespace events
219: Collection[] events = { null, null };
220: createStartEvents(attributes, events);
221:
222: // save a reference to the namespace collection so we can use them
223: // again
224: // in the end element
225: namespaceStack.add(events[0]);
226:
227: try {
228:
229: String[] qname = { null, null };
230: parseQName(qName, qname);
231:
232: consumer.add(eventFactory.createStartElement(qname[0], uri,
233: qname[1], events[1].iterator(), events[0]
234: .iterator()));
235:
236: } catch (XMLStreamException e) {
237:
238: throw new SAXException(e);
239:
240: } finally {
241:
242: super .startElement(uri, localName, qName, attributes);
243:
244: }
245:
246: }
247:
248: public void endElement(String uri, String localName, String qName)
249: throws SAXException {
250:
251: super .endElement(uri, localName, qName);
252:
253: eventFactory.setLocation(getCurrentLocation());
254:
255: // parse name
256: String[] qname = { null, null };
257: parseQName(qName, qname);
258:
259: // get namespaces
260: Collection nsList = (Collection) namespaceStack
261: .remove(namespaceStack.size() - 1);
262: Iterator nsIter = nsList.iterator();
263:
264: try {
265:
266: consumer.add(eventFactory.createEndElement(qname[0], uri,
267: qname[1], nsIter));
268:
269: } catch (XMLStreamException e) {
270:
271: throw new SAXException(e);
272:
273: }
274:
275: }
276:
277: public void comment(char[] ch, int start, int length)
278: throws SAXException {
279:
280: super .comment(ch, start, length);
281:
282: eventFactory.setLocation(getCurrentLocation());
283: try {
284:
285: consumer.add(eventFactory.createComment(new String(ch,
286: start, length)));
287:
288: } catch (XMLStreamException e) {
289:
290: throw new SAXException(e);
291:
292: }
293:
294: }
295:
296: public void characters(char[] ch, int start, int length)
297: throws SAXException {
298:
299: super .characters(ch, start, length);
300:
301: try {
302:
303: if (!isCDATA) {
304:
305: eventFactory.setLocation(getCurrentLocation());
306: consumer.add(eventFactory.createCharacters(new String(
307: ch, start, length)));
308:
309: }
310:
311: } catch (XMLStreamException e) {
312:
313: throw new SAXException(e);
314:
315: }
316:
317: }
318:
319: public void ignorableWhitespace(char[] ch, int start, int length)
320: throws SAXException {
321:
322: super .ignorableWhitespace(ch, start, length);
323: characters(ch, start, length);
324:
325: }
326:
327: public void processingInstruction(String target, String data)
328: throws SAXException {
329:
330: super .processingInstruction(target, data);
331: try {
332:
333: consumer.add(eventFactory.createProcessingInstruction(
334: target, data));
335:
336: } catch (XMLStreamException e) {
337:
338: throw new SAXException(e);
339:
340: }
341:
342: }
343:
344: public void endCDATA() throws SAXException {
345:
346: eventFactory.setLocation(getCurrentLocation());
347: try {
348:
349: consumer.add(eventFactory.createCData(CDATABuffer
350: .toString()));
351:
352: } catch (XMLStreamException e) {
353:
354: throw new SAXException(e);
355:
356: }
357:
358: super .endCDATA();
359:
360: }
361:
362: /**
363: * Creates the {@link Namespace}and {@link Attribute}events associated
364: * with a {@link StartElement}.
365: *
366: * @param attributes The SAX attributes object.
367: * @param events An array used to return the two collections of
368: * {@link Namespace}and {@link Attribute}events. The
369: * namespaces will be placed at <code>events[0]</code> and the
370: * attributes as <code>events[1]</code>.
371: */
372: protected void createStartEvents(Attributes attributes,
373: Collection[] events) {
374:
375: Map nsMap = null;
376: List attrs = null;
377:
378: // create namespaces
379: if (namespaces != null) {
380:
381: Iterator prefixes = namespaces.getDeclaredPrefixes();
382: while (prefixes.hasNext()) {
383:
384: String prefix = (String) prefixes.next();
385: String uri = namespaces.getNamespaceURI(prefix);
386:
387: Namespace ns = createNamespace(prefix, uri);
388: if (nsMap == null) {
389:
390: nsMap = new HashMap();
391:
392: }
393: nsMap.put(prefix, ns);
394:
395: }
396:
397: }
398:
399: // create attributes
400: String[] qname = { null, null };
401: for (int i = 0, s = attributes.getLength(); i < s; i++) {
402:
403: parseQName(attributes.getQName(i), qname);
404:
405: String attrPrefix = qname[0];
406: String attrLocal = qname[1];
407:
408: String attrQName = attributes.getQName(i);
409: String attrValue = attributes.getValue(i);
410: String attrURI = attributes.getURI(i);
411:
412: if ("xmlns".equals(attrQName) || "xmlns".equals(attrPrefix)) {
413:
414: // namespace declaration disguised as an attribute. If the
415: // namespace has already been declared, skip it, otherwise
416: // write it as an namespace
417:
418: if (!nsMap.containsKey(attrPrefix)) {
419:
420: Namespace ns = createNamespace(attrPrefix,
421: attrValue);
422: if (nsMap == null) {
423:
424: nsMap = new HashMap();
425:
426: }
427: nsMap.put(attrPrefix, ns);
428:
429: }
430:
431: } else {
432:
433: Attribute attribute;
434: if (attrPrefix.length() > 0) {
435:
436: attribute = eventFactory.createAttribute(
437: attrPrefix, attrURI, attrLocal, attrValue);
438:
439: } else {
440:
441: attribute = eventFactory.createAttribute(attrLocal,
442: attrValue);
443:
444: }
445:
446: if (attrs == null) {
447:
448: attrs = new ArrayList();
449:
450: }
451: attrs.add(attribute);
452:
453: }
454:
455: }
456:
457: events[0] = (nsMap == null ? Collections.EMPTY_LIST : nsMap
458: .values());
459: events[1] = (attrs == null ? Collections.EMPTY_LIST : attrs);
460:
461: }
462:
463: protected Namespace createNamespace(String prefix, String uri) {
464:
465: if (prefix == null || prefix.length() == 0) {
466:
467: return eventFactory.createNamespace(uri);
468:
469: } else {
470:
471: return eventFactory.createNamespace(prefix, uri);
472:
473: }
474:
475: }
476:
477: }
|