389 lines
13 KiB
Java
389 lines
13 KiB
Java
/*
|
|
* 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.
|
|
}
|
|
}
|