blob: 7c83129082bd0bb4e495c2bb1d88148baff7dfcc [file] [log] [blame]
/*
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.istack.internal;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.Locator;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.AttributesImpl;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.namespace.QName;
/**
* This is a simple utility class that adapts StAX events from an
* {@link XMLStreamReader} to SAX events on a
* {@link ContentHandler}, bridging between the two
* parser technologies.
*
* @author Ryan.Shoemaker@Sun.COM
* @version 1.0
*/
public class XMLStreamReaderToContentHandler {
// StAX event source
private final XMLStreamReader staxStreamReader;
// SAX event sink
private final ContentHandler saxHandler;
// if true, when the conversion is completed, leave the cursor to the last
// event that was fired (such as end element)
private final boolean eagerQuit;
/**
* If true, not start/endDocument event.
*/
private final boolean fragment;
// array of the even length of the form { prefix0, uri0, prefix1, uri1, ... }
private final String[] inscopeNamespaces;
/**
* @see #XMLStreamReaderToContentHandler(XMLStreamReader, ContentHandler, boolean, boolean, String[])
*/
public XMLStreamReaderToContentHandler(XMLStreamReader staxCore, ContentHandler saxCore, boolean eagerQuit, boolean fragment) {
this(staxCore, saxCore, eagerQuit, fragment, new String[0]);
}
/**
* Construct a new StAX to SAX adapter that will convert a StAX event
* stream into a SAX event stream.
*
* @param staxCore
* StAX event source
* @param saxCore
* SAXevent sink
* @param eagerQuit
* @param fragment
* @param inscopeNamespaces
* array of the even length of the form { prefix0, uri0, prefix1, uri1, ... }
*/
public XMLStreamReaderToContentHandler(XMLStreamReader staxCore, ContentHandler saxCore,
boolean eagerQuit, boolean fragment, String[] inscopeNamespaces) {
this.staxStreamReader = staxCore;
this.saxHandler = saxCore;
this.eagerQuit = eagerQuit;
this.fragment = fragment;
this.inscopeNamespaces = inscopeNamespaces;
assert inscopeNamespaces.length%2 == 0;
}
/*
* @see StAXReaderToContentHandler#bridge()
*/
public void bridge() throws XMLStreamException {
try {
// remembers the nest level of elements to know when we are done.
int depth=0;
// if the parser is at the start tag, proceed to the first element
int event = staxStreamReader.getEventType();
if(event == XMLStreamConstants.START_DOCUMENT) {
// nextTag doesn't correctly handle DTDs
while( !staxStreamReader.isStartElement() )
event = staxStreamReader.next();
}
if( event!=XMLStreamConstants.START_ELEMENT)
throw new IllegalStateException("The current event is not START_ELEMENT\n but " + event);
handleStartDocument();
for(int i=0; i < inscopeNamespaces.length; i+=2) {
saxHandler.startPrefixMapping(inscopeNamespaces[i], inscopeNamespaces[i+1]);
}
OUTER:
do {
// These are all of the events listed in the javadoc for
// XMLEvent.
// The spec only really describes 11 of them.
switch (event) {
case XMLStreamConstants.START_ELEMENT :
depth++;
handleStartElement();
break;
case XMLStreamConstants.END_ELEMENT :
handleEndElement();
depth--;
if(depth==0 && eagerQuit)
break OUTER;
break;
case XMLStreamConstants.CHARACTERS :
handleCharacters();
break;
case XMLStreamConstants.ENTITY_REFERENCE :
handleEntityReference();
break;
case XMLStreamConstants.PROCESSING_INSTRUCTION :
handlePI();
break;
case XMLStreamConstants.COMMENT :
handleComment();
break;
case XMLStreamConstants.DTD :
handleDTD();
break;
case XMLStreamConstants.ATTRIBUTE :
handleAttribute();
break;
case XMLStreamConstants.NAMESPACE :
handleNamespace();
break;
case XMLStreamConstants.CDATA :
handleCDATA();
break;
case XMLStreamConstants.ENTITY_DECLARATION :
handleEntityDecl();
break;
case XMLStreamConstants.NOTATION_DECLARATION :
handleNotationDecl();
break;
case XMLStreamConstants.SPACE :
handleSpace();
break;
default :
throw new InternalError("processing event: " + event);
}
event=staxStreamReader.next();
} while (depth!=0);
for(int i=0; i < inscopeNamespaces.length; i+=2) {
saxHandler.endPrefixMapping(inscopeNamespaces[i]);
}
handleEndDocument();
} catch (SAXException e) {
throw new XMLStreamException2(e);
}
}
private void handleEndDocument() throws SAXException {
if(fragment)
return;
saxHandler.endDocument();
}
private void handleStartDocument() throws SAXException {
if(fragment)
return;
saxHandler.setDocumentLocator(new Locator() {
public int getColumnNumber() {
return staxStreamReader.getLocation().getColumnNumber();
}
public int getLineNumber() {
return staxStreamReader.getLocation().getLineNumber();
}
public String getPublicId() {
return staxStreamReader.getLocation().getPublicId();
}
public String getSystemId() {
return staxStreamReader.getLocation().getSystemId();
}
});
saxHandler.startDocument();
}
private void handlePI() throws XMLStreamException {
try {
saxHandler.processingInstruction(
staxStreamReader.getPITarget(),
staxStreamReader.getPIData());
} catch (SAXException e) {
throw new XMLStreamException2(e);
}
}
private void handleCharacters() throws XMLStreamException {
try {
saxHandler.characters(
staxStreamReader.getTextCharacters(),
staxStreamReader.getTextStart(),
staxStreamReader.getTextLength() );
} catch (SAXException e) {
throw new XMLStreamException2(e);
}
}
private void handleEndElement() throws XMLStreamException {
QName qName = staxStreamReader.getName();
try {
String pfix = qName.getPrefix();
String rawname = (pfix == null || pfix.length() == 0)
? qName.getLocalPart()
: pfix + ':' + qName.getLocalPart();
// fire endElement
saxHandler.endElement(
qName.getNamespaceURI(),
qName.getLocalPart(),
rawname);
// end namespace bindings
int nsCount = staxStreamReader.getNamespaceCount();
for (int i = nsCount - 1; i >= 0; i--) {
String prefix = staxStreamReader.getNamespacePrefix(i);
if (prefix == null) { // true for default namespace
prefix = "";
}
saxHandler.endPrefixMapping(prefix);
}
} catch (SAXException e) {
throw new XMLStreamException2(e);
}
}
private void handleStartElement() throws XMLStreamException {
try {
// start namespace bindings
int nsCount = staxStreamReader.getNamespaceCount();
for (int i = 0; i < nsCount; i++) {
saxHandler.startPrefixMapping(
fixNull(staxStreamReader.getNamespacePrefix(i)),
fixNull(staxStreamReader.getNamespaceURI(i)));
}
// fire startElement
QName qName = staxStreamReader.getName();
String prefix = qName.getPrefix();
String rawname;
if(prefix==null || prefix.length()==0)
rawname = qName.getLocalPart();
else
rawname = prefix + ':' + qName.getLocalPart();
Attributes attrs = getAttributes();
saxHandler.startElement(
qName.getNamespaceURI(),
qName.getLocalPart(),
rawname,
attrs);
} catch (SAXException e) {
throw new XMLStreamException2(e);
}
}
private static String fixNull(String s) {
if(s==null) return "";
else return s;
}
/**
* Get the attributes associated with the given START_ELEMENT or ATTRIBUTE
* StAXevent.
*
* @return the StAX attributes converted to an org.xml.sax.Attributes
*/
private Attributes getAttributes() {
AttributesImpl attrs = new AttributesImpl();
int eventType = staxStreamReader.getEventType();
if (eventType != XMLStreamConstants.ATTRIBUTE
&& eventType != XMLStreamConstants.START_ELEMENT) {
throw new InternalError(
"getAttributes() attempting to process: " + eventType);
}
// in SAX, namespace declarations are not part of attributes by default.
// (there's a property to control that, but as far as we are concerned
// we don't use it.) So don't add xmlns:* to attributes.
// gather non-namespace attrs
for (int i = 0; i < staxStreamReader.getAttributeCount(); i++) {
String uri = staxStreamReader.getAttributeNamespace(i);
if(uri==null) uri="";
String localName = staxStreamReader.getAttributeLocalName(i);
String prefix = staxStreamReader.getAttributePrefix(i);
String qName;
if(prefix==null || prefix.length()==0)
qName = localName;
else
qName = prefix + ':' + localName;
String type = staxStreamReader.getAttributeType(i);
String value = staxStreamReader.getAttributeValue(i);
attrs.addAttribute(uri, localName, qName, type, value);
}
return attrs;
}
private void handleNamespace() {
// no-op ???
// namespace events don't normally occur outside of a startElement
// or endElement
}
private void handleAttribute() {
// no-op ???
// attribute events don't normally occur outside of a startElement
// or endElement
}
private void handleDTD() {
// no-op ???
// it seems like we need to pass this info along, but how?
}
private void handleComment() {
// no-op ???
}
private void handleEntityReference() {
// no-op ???
}
private void handleSpace() {
// no-op ???
// this event is listed in the javadoc, but not in the spec.
}
private void handleNotationDecl() {
// no-op ???
// this event is listed in the javadoc, but not in the spec.
}
private void handleEntityDecl() {
// no-op ???
// this event is listed in the javadoc, but not in the spec.
}
private void handleCDATA() {
// no-op ???
// this event is listed in the javadoc, but not in the spec.
}
}