com.sun.istack.XMLStreamReaderToContentHandler Maven / Gradle / Ivy
The newest version!
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2014 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.istack;
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 [email protected]
* @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.clone();
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.
}
}