001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Emil Ong
028: */
029:
030: package com.caucho.xml.stream;
031:
032: import com.caucho.util.CharBuffer;
033: import com.caucho.util.L10N;
034: import com.caucho.vfs.*;
035:
036: import org.xml.sax.*;
037: import org.xml.sax.helpers.*;
038:
039: import javax.xml.XMLConstants;
040: import javax.xml.namespace.NamespaceContext;
041: import javax.xml.namespace.QName;
042: import javax.xml.parsers.SAXParser;
043: import javax.xml.parsers.SAXParserFactory;
044: import javax.xml.parsers.ParserConfigurationException;
045: import javax.xml.stream.events.*;
046: import javax.xml.stream.Location;
047: import javax.xml.stream.XMLEventFactory;
048: import javax.xml.stream.XMLEventReader;
049: import javax.xml.stream.XMLStreamConstants;
050: import javax.xml.stream.XMLStreamException;
051: import javax.xml.stream.XMLStreamReader;
052: import javax.xml.transform.sax.SAXSource;
053: import java.io.IOException;
054: import java.io.InputStream;
055: import java.io.Reader;
056: import java.util.ArrayList;
057: import java.util.Iterator;
058: import java.util.NoSuchElementException;
059: import java.util.logging.Logger;
060:
061: /**
062: * XML pull-parser interface.
063: */
064: public class SAXSourceXMLEventReaderImpl implements XMLEventReader {
065: private static final Logger log = Logger
066: .getLogger(SAXSourceXMLEventReaderImpl.class.getName());
067: private static final L10N L = new L10N(
068: SAXSourceXMLEventReaderImpl.class);
069:
070: private static final XMLEventFactory EVENT_FACTORY = XMLEventFactory
071: .newInstance();
072:
073: private static SAXParserFactory _saxParserFactory;
074: private static final String NAMESPACE_FEATURE = "http://xml.org/sax/features/namespaces";
075: private static final String PREFIX_FEATURE = "http://xml.org/sax/features/namespace-prefixes";
076:
077: private final ArrayList<XMLEvent> _events = new ArrayList<XMLEvent>();
078:
079: private final EventGeneratingContentHandler _contentHandler;
080:
081: public static SAXParserFactory getSAXParserFactory()
082: throws ParserConfigurationException, SAXException {
083: if (_saxParserFactory == null) {
084: _saxParserFactory = SAXParserFactory.newInstance();
085: _saxParserFactory.setFeature(NAMESPACE_FEATURE, true);
086: _saxParserFactory.setFeature(PREFIX_FEATURE, true);
087: _saxParserFactory.setNamespaceAware(true);
088: }
089:
090: return _saxParserFactory;
091: }
092:
093: public SAXSourceXMLEventReaderImpl(SAXSource source)
094: throws XMLStreamException {
095: XMLReader reader = source.getXMLReader();
096:
097: if (reader == null) {
098: try {
099: reader = XMLReaderFactory.createXMLReader();
100:
101: source.setXMLReader(reader);
102: } catch (SAXException e) {
103: throw new XMLStreamException(e);
104: }
105: }
106:
107: _contentHandler = new EventGeneratingContentHandler();
108: reader.setContentHandler(_contentHandler);
109:
110: try {
111: reader.parse(source.getInputSource());
112: } catch (SAXException e) {
113: throw new XMLStreamException(e);
114: } catch (IOException e) {
115: throw new XMLStreamException(e);
116: }
117: }
118:
119: public SAXSourceXMLEventReaderImpl() {
120: _contentHandler = new EventGeneratingContentHandler();
121: }
122:
123: public ContentHandler getContentHandler() {
124: return _contentHandler;
125: }
126:
127: public void close() throws XMLStreamException {
128: }
129:
130: public String getElementText() throws XMLStreamException {
131: // XXX check precondition that the current element is start element
132:
133: StringBuilder sb = new StringBuilder();
134: XMLEvent event = null;
135:
136: for (event = peek(); !event.isEndElement(); event = peek()) {
137: if (!event.isCharacters())
138: throw new XMLStreamException("Unexpected event: "
139: + event);
140:
141: event = nextEvent();
142:
143: sb.append(((Characters) event).getData());
144: }
145:
146: return sb.toString();
147: }
148:
149: public Object getProperty(String name)
150: throws IllegalArgumentException {
151: throw new IllegalArgumentException(name);
152: }
153:
154: public boolean hasNext() {
155: return _events.size() > 0;
156: }
157:
158: public XMLEvent nextEvent() throws XMLStreamException {
159: try {
160: return _events.remove(0);
161: } catch (IndexOutOfBoundsException e) {
162: throw new NoSuchElementException();
163: }
164: }
165:
166: public XMLEvent nextTag() throws XMLStreamException {
167: XMLEvent event = null;
168:
169: for (event = nextEvent(); !event.isStartElement()
170: && !event.isEndElement(); event = nextEvent()) {
171: if (event.getEventType() != XMLStreamConstants.SPACE)
172: throw new XMLStreamException("Unexpected event: "
173: + event);
174: }
175:
176: return event;
177: }
178:
179: public XMLEvent peek() throws XMLStreamException {
180: try {
181: return _events.get(0);
182: } catch (IndexOutOfBoundsException e) {
183: throw new NoSuchElementException();
184: }
185: }
186:
187: public void remove() {
188: throw new UnsupportedOperationException();
189: }
190:
191: public XMLEvent next() {
192: try {
193: return nextEvent();
194: } catch (XMLStreamException e) {
195: return null;
196: }
197: }
198:
199: private class EventGeneratingContentHandler implements
200: ContentHandler {
201: private NamespaceReaderContext _context = new NamespaceReaderContext();
202: private ArrayList<Namespace> _newMappings = new ArrayList<Namespace>();
203: private ArrayList<Namespace> _oldMappings = new ArrayList<Namespace>();
204: private QName _pendingEndName = null;
205:
206: public void characters(char[] ch, int start, int length)
207: throws SAXException {
208: checkForPendingEndElement();
209:
210: String s = new String(ch, start, length);
211: _events.add(EVENT_FACTORY.createCharacters(s));
212: }
213:
214: public void endDocument() throws SAXException {
215: checkForPendingEndElement();
216:
217: _events.add(EVENT_FACTORY.createEndDocument());
218: }
219:
220: public void endElement(String uri, String localName,
221: String qName) {
222: int colon = qName.indexOf(':');
223:
224: if (colon < 0) {
225: if (uri == null || "".equals(uri))
226: _pendingEndName = new QName(localName);
227: else
228: _pendingEndName = new QName(uri, localName);
229: } else {
230: String prefix = qName.substring(0, colon);
231: _pendingEndName = new QName(uri, localName, prefix);
232: }
233: }
234:
235: public void endPrefixMapping(String prefix) throws SAXException {
236: String uri = _context.getUri(prefix);
237:
238: if (uri == null)
239: throw new SAXException("Unknown prefix: " + prefix);
240:
241: _oldMappings
242: .add(EVENT_FACTORY.createNamespace(prefix, uri));
243: }
244:
245: private void checkForPendingEndElement() throws SAXException {
246: if (_pendingEndName != null) {
247: Iterator iterator = _oldMappings.iterator();
248:
249: _events.add(EVENT_FACTORY.createEndElement(
250: _pendingEndName, iterator));
251:
252: _pendingEndName = null;
253: _oldMappings = new ArrayList<Namespace>();
254:
255: try {
256: _context.pop();
257: } catch (XMLStreamException e) {
258: throw new SAXException(e);
259: }
260: }
261: }
262:
263: public void ignorableWhitespace(char[] ch, int start, int length)
264: throws SAXException {
265: checkForPendingEndElement();
266:
267: String s = new String(ch, start, length);
268: _events.add(EVENT_FACTORY.createIgnorableSpace(s));
269: }
270:
271: public void processingInstruction(String target, String data)
272: throws SAXException {
273: checkForPendingEndElement();
274:
275: _events.add(EVENT_FACTORY.createProcessingInstruction(
276: target, data));
277: }
278:
279: public void setDocumentLocator(Locator locator) {
280: // XXX
281: }
282:
283: public void skippedEntity(String name) {
284: // XXX
285: }
286:
287: public void startDocument() {
288: _events.add(EVENT_FACTORY.createStartDocument());
289: }
290:
291: public void startElement(String uri, String localName,
292: String qName, Attributes atts) throws SAXException {
293: checkForPendingEndElement();
294: _context.push();
295:
296: Iterator attributeIterator = null;
297: Iterator namespaceIterator = null;
298:
299: if (atts.getLength() > 0) {
300: ArrayList<Attribute> attributes = new ArrayList<Attribute>();
301: ArrayList<Namespace> namespaces = new ArrayList<Namespace>();
302:
303: namespaces.addAll(_newMappings);
304: _newMappings.clear();
305:
306: for (int i = 0; i < atts.getLength(); i++) {
307: Attribute attribute = null;
308: Namespace namespace = null;
309:
310: String qualified = atts.getQName(i);
311: String local = atts.getLocalName(i);
312: String namespaceURI = atts.getURI(i);
313: String value = atts.getValue(i);
314:
315: int colon = qualified.indexOf(':');
316:
317: if (colon < 0) {
318: if (namespaceURI == null
319: || "".equals(namespaceURI))
320: attribute = EVENT_FACTORY.createAttribute(
321: local, value);
322: else {
323: if (XMLConstants.XMLNS_ATTRIBUTE
324: .equals(local))
325: namespace = EVENT_FACTORY
326: .createNamespace(value);
327: else {
328: QName name = new QName(namespaceURI,
329: local);
330: attribute = EVENT_FACTORY
331: .createAttribute(name, value);
332: }
333: }
334: } else {
335: String prefix = qualified.substring(0, colon);
336:
337: if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix))
338: namespace = EVENT_FACTORY.createNamespace(
339: local, value);
340: else {
341: attribute = EVENT_FACTORY.createAttribute(
342: prefix, namespaceURI, local, value);
343: }
344: }
345:
346: if (attribute != null)
347: attributes.add(attribute);
348:
349: if (namespace != null) {
350: _context.declare(namespace.getPrefix(),
351: namespace.getNamespaceURI());
352:
353: namespaces.add(namespace);
354: }
355: }
356:
357: attributeIterator = attributes.iterator();
358: namespaceIterator = namespaces.iterator();
359: }
360:
361: QName name = null;
362:
363: int colon = qName.indexOf(':');
364:
365: if (colon < 0) {
366: if (localName == null)
367: localName = qName;
368:
369: if (uri == null || "".equals(uri))
370: name = new QName(localName);
371: else
372: name = new QName(uri, localName);
373: } else {
374: String prefix = qName.substring(0, colon);
375: name = new QName(uri, localName, prefix);
376: }
377:
378: StartElement start = EVENT_FACTORY.createStartElement(name,
379: attributeIterator, namespaceIterator);
380: _events.add(start);
381: }
382:
383: public void startPrefixMapping(String prefix, String uri)
384: throws SAXException {
385: checkForPendingEndElement();
386:
387: _newMappings
388: .add(EVENT_FACTORY.createNamespace(prefix, uri));
389: _context.declare(prefix, uri);
390: }
391: }
392: }
|