/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Emil Ong */ package com.caucho.xml.stream; import com.caucho.util.CharBuffer; import com.caucho.util.L10N; import com.caucho.vfs.*; import org.xml.sax.*; import org.xml.sax.helpers.*; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.QName; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.events.*; import javax.xml.stream.Location; import javax.xml.stream.XMLEventFactory; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.transform.sax.SAXSource; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.util.ArrayList; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.logging.Logger; /** * XML pull-parser interface. */ public class SAXSourceXMLEventReaderImpl implements XMLEventReader { private static final Logger log = Logger.getLogger(SAXSourceXMLEventReaderImpl.class.getName()); private static final L10N L = new L10N(SAXSourceXMLEventReaderImpl.class); private static final XMLEventFactory EVENT_FACTORY = XMLEventFactory.newInstance(); private static SAXParserFactory _saxParserFactory; private static final String NAMESPACE_FEATURE = "http://xml.org/sax/features/namespaces"; private static final String PREFIX_FEATURE = "http://xml.org/sax/features/namespace-prefixes"; private final ArrayList<XMLEvent> _events = new ArrayList<XMLEvent>(); private final EventGeneratingContentHandler _contentHandler; public static SAXParserFactory getSAXParserFactory() throws ParserConfigurationException, SAXException { if (_saxParserFactory == null) { _saxParserFactory = SAXParserFactory.newInstance(); _saxParserFactory.setFeature(NAMESPACE_FEATURE, true); _saxParserFactory.setFeature(PREFIX_FEATURE, true); _saxParserFactory.setNamespaceAware(true); } return _saxParserFactory; } public SAXSourceXMLEventReaderImpl(SAXSource source) throws XMLStreamException { XMLReader reader = source.getXMLReader(); if (reader == null) { try { reader = XMLReaderFactory.createXMLReader(); source.setXMLReader(reader); } catch (SAXException e) { throw new XMLStreamException(e); } } _contentHandler = new EventGeneratingContentHandler(); reader.setContentHandler(_contentHandler); try { reader.parse(source.getInputSource()); } catch (SAXException e) { throw new XMLStreamException(e); } catch (IOException e) { throw new XMLStreamException(e); } } public SAXSourceXMLEventReaderImpl() { _contentHandler = new EventGeneratingContentHandler(); } public ContentHandler getContentHandler() { return _contentHandler; } public void close() throws XMLStreamException { } public String getElementText() throws XMLStreamException { // XXX check precondition that the current element is start element StringBuilder sb = new StringBuilder(); XMLEvent event = null; for (event = peek(); ! event.isEndElement(); event = peek()) { if (! event.isCharacters()) throw new XMLStreamException("Unexpected event: " + event); event = nextEvent(); sb.append(((Characters) event).getData()); } return sb.toString(); } public Object getProperty(String name) throws IllegalArgumentException { throw new IllegalArgumentException(name); } public boolean hasNext() { return _events.size() > 0; } public XMLEvent nextEvent() throws XMLStreamException { try { return _events.remove(0); } catch (IndexOutOfBoundsException e) { throw new NoSuchElementException(); } } public XMLEvent nextTag() throws XMLStreamException { XMLEvent event = null; for (event = nextEvent(); ! event.isStartElement() && ! event.isEndElement(); event = nextEvent()) { if (event.getEventType() != XMLStreamConstants.SPACE) throw new XMLStreamException("Unexpected event: " + event); } return event; } public XMLEvent peek() throws XMLStreamException { try { return _events.get(0); } catch (IndexOutOfBoundsException e) { throw new NoSuchElementException(); } } public void remove() { throw new UnsupportedOperationException(); } public XMLEvent next() { try { return nextEvent(); } catch (XMLStreamException e) { return null; } } private class EventGeneratingContentHandler implements ContentHandler { private NamespaceReaderContext _context = new NamespaceReaderContext(); private ArrayList<Namespace> _newMappings = new ArrayList<Namespace>(); private ArrayList<Namespace> _oldMappings = new ArrayList<Namespace>(); private QName _pendingEndName = null; public void characters(char[] ch, int start, int length) throws SAXException { checkForPendingEndElement(); String s = new String(ch, start, length); _events.add(EVENT_FACTORY.createCharacters(s)); } public void endDocument() throws SAXException { checkForPendingEndElement(); _events.add(EVENT_FACTORY.createEndDocument()); } public void endElement(String uri, String localName, String qName) { int colon = qName.indexOf(':'); if (colon < 0) { if (uri == null || "".equals(uri)) _pendingEndName = new QName(localName); else _pendingEndName = new QName(uri, localName); } else { String prefix = qName.substring(0, colon); _pendingEndName = new QName(uri, localName, prefix); } } public void endPrefixMapping(String prefix) throws SAXException { String uri = _context.getUri(prefix); if (uri == null) throw new SAXException("Unknown prefix: " + prefix); _oldMappings.add(EVENT_FACTORY.createNamespace(prefix, uri)); } private void checkForPendingEndElement() throws SAXException { if (_pendingEndName != null) { Iterator iterator = _oldMappings.iterator(); _events.add(EVENT_FACTORY.createEndElement(_pendingEndName, iterator)); _pendingEndName = null; _oldMappings = new ArrayList<Namespace>(); try { _context.pop(); } catch (XMLStreamException e) { throw new SAXException(e); } } } public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { checkForPendingEndElement(); String s = new String(ch, start, length); _events.add(EVENT_FACTORY.createIgnorableSpace(s)); } public void processingInstruction(String target, String data) throws SAXException { checkForPendingEndElement(); _events.add(EVENT_FACTORY.createProcessingInstruction(target, data)); } public void setDocumentLocator(Locator locator) { // XXX } public void skippedEntity(String name) { // XXX } public void startDocument() { _events.add(EVENT_FACTORY.createStartDocument()); } public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { checkForPendingEndElement(); _context.push(); Iterator attributeIterator = null; Iterator namespaceIterator = null; if (atts.getLength() > 0) { ArrayList<Attribute> attributes = new ArrayList<Attribute>(); ArrayList<Namespace> namespaces = new ArrayList<Namespace>(); namespaces.addAll(_newMappings); _newMappings.clear(); for (int i = 0; i < atts.getLength(); i++) { Attribute attribute = null; Namespace namespace = null; String qualified = atts.getQName(i); String local = atts.getLocalName(i); String namespaceURI = atts.getURI(i); String value = atts.getValue(i); int colon = qualified.indexOf(':'); if (colon < 0) { if (namespaceURI == null || "".equals(namespaceURI)) attribute = EVENT_FACTORY.createAttribute(local, value); else { if (XMLConstants.XMLNS_ATTRIBUTE.equals(local)) namespace = EVENT_FACTORY.createNamespace(value); else { QName name = new QName(namespaceURI, local); attribute = EVENT_FACTORY.createAttribute(name, value); } } } else { String prefix = qualified.substring(0, colon); if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) namespace = EVENT_FACTORY.createNamespace(local, value); else { attribute = EVENT_FACTORY.createAttribute(prefix, namespaceURI, local, value); } } if (attribute != null) attributes.add(attribute); if (namespace != null) { _context.declare(namespace.getPrefix(), namespace.getNamespaceURI()); namespaces.add(namespace); } } attributeIterator = attributes.iterator(); namespaceIterator = namespaces.iterator(); } QName name = null; int colon = qName.indexOf(':'); if (colon < 0) { if (localName == null) localName = qName; if (uri == null || "".equals(uri)) name = new QName(localName); else name = new QName(uri, localName); } else { String prefix = qName.substring(0, colon); name = new QName(uri, localName, prefix); } StartElement start = EVENT_FACTORY.createStartElement(name, attributeIterator, namespaceIterator); _events.add(start); } public void startPrefixMapping(String prefix, String uri) throws SAXException { checkForPendingEndElement(); _newMappings.add(EVENT_FACTORY.createNamespace(prefix, uri)); _context.declare(prefix, uri); } } }