aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.xml.internal.bind.v2.runtime.unmarshaller; aoqi@0: aoqi@0: import java.lang.reflect.Constructor; aoqi@0: aoqi@0: import javax.xml.stream.Location; aoqi@0: import javax.xml.stream.XMLStreamConstants; aoqi@0: import javax.xml.stream.XMLStreamException; aoqi@0: import javax.xml.stream.XMLStreamReader; aoqi@0: aoqi@0: import com.sun.xml.internal.bind.WhiteSpaceProcessor; aoqi@0: aoqi@0: import org.xml.sax.Attributes; aoqi@0: import org.xml.sax.SAXException; aoqi@0: aoqi@0: /** aoqi@0: * Reads XML from StAX {@link XMLStreamReader} and aoqi@0: * feeds events to {@link XmlVisitor}. aoqi@0: *

aoqi@0: * TODO: aoqi@0: * Finding the optimized FI implementations is a bit hacky and not very aoqi@0: * extensible. Can we use the service provider mechanism in general for aoqi@0: * concrete implementations of StAXConnector. aoqi@0: * aoqi@0: * @author Ryan.Shoemaker@Sun.COM aoqi@0: * @author Kohsuke Kawaguchi aoqi@0: * @version JAXB 2.0 aoqi@0: */ aoqi@0: class StAXStreamConnector extends StAXConnector { aoqi@0: aoqi@0: /** aoqi@0: * Creates a {@link StAXConnector} from {@link XMLStreamReader}. aoqi@0: * aoqi@0: * This method checks if the parser is FI parser and acts accordingly. aoqi@0: */ aoqi@0: public static StAXConnector create(XMLStreamReader reader, XmlVisitor visitor) { aoqi@0: // try optimized codepath aoqi@0: final Class readerClass = reader.getClass(); aoqi@0: if (FI_STAX_READER_CLASS != null && FI_STAX_READER_CLASS.isAssignableFrom(readerClass) && FI_CONNECTOR_CTOR!=null) { aoqi@0: try { aoqi@0: return FI_CONNECTOR_CTOR.newInstance(reader,visitor); aoqi@0: } catch (Exception t) { aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // Quick hack until SJSXP fixes 6270116 aoqi@0: boolean isZephyr = readerClass.getName().equals("com.sun.xml.internal.stream.XMLReaderImpl"); aoqi@0: if (getBoolProp(reader,"org.codehaus.stax2.internNames") && aoqi@0: getBoolProp(reader,"org.codehaus.stax2.internNsUris")) aoqi@0: ; // no need for interning aoqi@0: else aoqi@0: if (isZephyr) aoqi@0: ; // no need for interning aoqi@0: else aoqi@0: if (checkImplementaionNameOfSjsxp(reader)) aoqi@0: ; // no need for interning. aoqi@0: else aoqi@0: visitor = new InterningXmlVisitor(visitor); aoqi@0: aoqi@0: if (STAX_EX_READER_CLASS!=null && STAX_EX_READER_CLASS.isAssignableFrom(readerClass)) { aoqi@0: try { aoqi@0: return STAX_EX_CONNECTOR_CTOR.newInstance(reader,visitor); aoqi@0: } catch (Exception t) { aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return new StAXStreamConnector(reader,visitor); aoqi@0: } aoqi@0: aoqi@0: private static boolean checkImplementaionNameOfSjsxp(XMLStreamReader reader) { aoqi@0: try { aoqi@0: Object name = reader.getProperty("http://java.sun.com/xml/stream/properties/implementation-name"); aoqi@0: return name!=null && name.equals("sjsxp"); aoqi@0: } catch (Exception e) { aoqi@0: // be defensive against broken StAX parsers since javadoc is not clear aoqi@0: // about when an error happens aoqi@0: return false; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private static boolean getBoolProp(XMLStreamReader r, String n) { aoqi@0: try { aoqi@0: Object o = r.getProperty(n); aoqi@0: if(o instanceof Boolean) return (Boolean)o; aoqi@0: return false; aoqi@0: } catch (Exception e) { aoqi@0: // be defensive against broken StAX parsers since javadoc is not clear aoqi@0: // about when an error happens aoqi@0: return false; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: aoqi@0: // StAX event source aoqi@0: private final XMLStreamReader staxStreamReader; aoqi@0: aoqi@0: /** aoqi@0: * SAX may fire consecutive characters event, but we don't allow it. aoqi@0: * so use this buffer to perform buffering. aoqi@0: */ aoqi@0: protected final StringBuilder buffer = new StringBuilder(); aoqi@0: aoqi@0: /** aoqi@0: * Set to true if the text() event is reported, and therefore aoqi@0: * the following text() event should be suppressed. aoqi@0: */ aoqi@0: protected boolean textReported = false; aoqi@0: aoqi@0: protected StAXStreamConnector(XMLStreamReader staxStreamReader, XmlVisitor visitor) { aoqi@0: super(visitor); aoqi@0: this.staxStreamReader = staxStreamReader; aoqi@0: } aoqi@0: aoqi@0: public void bridge() throws XMLStreamException { aoqi@0: aoqi@0: try { aoqi@0: // remembers the nest level of elements to know when we are done. aoqi@0: int depth=0; aoqi@0: aoqi@0: // if the parser is at the start tag, proceed to the first element aoqi@0: int event = staxStreamReader.getEventType(); aoqi@0: if(event == XMLStreamConstants.START_DOCUMENT) { aoqi@0: // nextTag doesn't correctly handle DTDs aoqi@0: while( !staxStreamReader.isStartElement() ) aoqi@0: event = staxStreamReader.next(); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: if( event!=XMLStreamConstants.START_ELEMENT) aoqi@0: throw new IllegalStateException("The current event is not START_ELEMENT\n but " + event); aoqi@0: aoqi@0: handleStartDocument(staxStreamReader.getNamespaceContext()); aoqi@0: aoqi@0: OUTER: aoqi@0: while(true) { aoqi@0: // These are all of the events listed in the javadoc for aoqi@0: // XMLEvent. aoqi@0: // The spec only really describes 11 of them. aoqi@0: switch (event) { aoqi@0: case XMLStreamConstants.START_ELEMENT : aoqi@0: handleStartElement(); aoqi@0: depth++; aoqi@0: break; aoqi@0: case XMLStreamConstants.END_ELEMENT : aoqi@0: depth--; aoqi@0: handleEndElement(); aoqi@0: if(depth==0) break OUTER; aoqi@0: break; aoqi@0: case XMLStreamConstants.CHARACTERS : aoqi@0: case XMLStreamConstants.CDATA : aoqi@0: case XMLStreamConstants.SPACE : aoqi@0: handleCharacters(); aoqi@0: break; aoqi@0: // otherwise simply ignore aoqi@0: } aoqi@0: aoqi@0: event=staxStreamReader.next(); aoqi@0: } aoqi@0: aoqi@0: staxStreamReader.next(); // move beyond the end tag. aoqi@0: aoqi@0: handleEndDocument(); aoqi@0: } catch (SAXException e) { aoqi@0: throw new XMLStreamException(e); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: protected Location getCurrentLocation() { aoqi@0: return staxStreamReader.getLocation(); aoqi@0: } aoqi@0: aoqi@0: protected String getCurrentQName() { aoqi@0: return getQName(staxStreamReader.getPrefix(),staxStreamReader.getLocalName()); aoqi@0: } aoqi@0: aoqi@0: private void handleEndElement() throws SAXException { aoqi@0: processText(false); aoqi@0: aoqi@0: // fire endElement aoqi@0: tagName.uri = fixNull(staxStreamReader.getNamespaceURI()); aoqi@0: tagName.local = staxStreamReader.getLocalName(); aoqi@0: visitor.endElement(tagName); aoqi@0: aoqi@0: // end namespace bindings aoqi@0: int nsCount = staxStreamReader.getNamespaceCount(); aoqi@0: for (int i = nsCount - 1; i >= 0; i--) { aoqi@0: visitor.endPrefixMapping(fixNull(staxStreamReader.getNamespacePrefix(i))); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private void handleStartElement() throws SAXException { aoqi@0: processText(true); aoqi@0: aoqi@0: // start namespace bindings aoqi@0: int nsCount = staxStreamReader.getNamespaceCount(); aoqi@0: for (int i = 0; i < nsCount; i++) { aoqi@0: visitor.startPrefixMapping( aoqi@0: fixNull(staxStreamReader.getNamespacePrefix(i)), aoqi@0: fixNull(staxStreamReader.getNamespaceURI(i))); aoqi@0: } aoqi@0: aoqi@0: // fire startElement aoqi@0: tagName.uri = fixNull(staxStreamReader.getNamespaceURI()); aoqi@0: tagName.local = staxStreamReader.getLocalName(); aoqi@0: tagName.atts = attributes; aoqi@0: aoqi@0: visitor.startElement(tagName); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Proxy of {@link Attributes} that read from {@link XMLStreamReader}. aoqi@0: */ aoqi@0: private final Attributes attributes = new Attributes() { aoqi@0: public int getLength() { aoqi@0: return staxStreamReader.getAttributeCount(); aoqi@0: } aoqi@0: aoqi@0: public String getURI(int index) { aoqi@0: String uri = staxStreamReader.getAttributeNamespace(index); aoqi@0: if(uri==null) return ""; aoqi@0: return uri; aoqi@0: } aoqi@0: aoqi@0: public String getLocalName(int index) { aoqi@0: return staxStreamReader.getAttributeLocalName(index); aoqi@0: } aoqi@0: aoqi@0: public String getQName(int index) { aoqi@0: String prefix = staxStreamReader.getAttributePrefix(index); aoqi@0: if(prefix==null || prefix.length()==0) aoqi@0: return getLocalName(index); aoqi@0: else aoqi@0: return prefix + ':' + getLocalName(index); aoqi@0: } aoqi@0: aoqi@0: public String getType(int index) { aoqi@0: return staxStreamReader.getAttributeType(index); aoqi@0: } aoqi@0: aoqi@0: public String getValue(int index) { aoqi@0: return staxStreamReader.getAttributeValue(index); aoqi@0: } aoqi@0: aoqi@0: public int getIndex(String uri, String localName) { aoqi@0: for( int i=getLength()-1; i>=0; i-- ) aoqi@0: if( localName.equals(getLocalName(i)) && uri.equals(getURI(i))) aoqi@0: return i; aoqi@0: return -1; aoqi@0: } aoqi@0: aoqi@0: // this method sholdn't be used that often (if at all) aoqi@0: // so it's OK to be slow. aoqi@0: public int getIndex(String qName) { aoqi@0: for( int i=getLength()-1; i>=0; i-- ) { aoqi@0: if(qName.equals(getQName(i))) aoqi@0: return i; aoqi@0: } aoqi@0: return -1; aoqi@0: } aoqi@0: aoqi@0: public String getType(String uri, String localName) { aoqi@0: int index = getIndex(uri,localName); aoqi@0: if(index<0) return null; aoqi@0: return getType(index); aoqi@0: } aoqi@0: aoqi@0: public String getType(String qName) { aoqi@0: int index = getIndex(qName); aoqi@0: if(index<0) return null; aoqi@0: return getType(index); aoqi@0: } aoqi@0: aoqi@0: public String getValue(String uri, String localName) { aoqi@0: int index = getIndex(uri,localName); aoqi@0: if(index<0) return null; aoqi@0: return getValue(index); aoqi@0: } aoqi@0: aoqi@0: public String getValue(String qName) { aoqi@0: int index = getIndex(qName); aoqi@0: if(index<0) return null; aoqi@0: return getValue(index); aoqi@0: } aoqi@0: }; aoqi@0: aoqi@0: protected void handleCharacters() throws XMLStreamException, SAXException { aoqi@0: if( predictor.expectText() ) aoqi@0: buffer.append( aoqi@0: staxStreamReader.getTextCharacters(), aoqi@0: staxStreamReader.getTextStart(), aoqi@0: staxStreamReader.getTextLength() ); aoqi@0: } aoqi@0: aoqi@0: private void processText( boolean ignorable ) throws SAXException { aoqi@0: if( predictor.expectText() && (!ignorable || !WhiteSpaceProcessor.isWhiteSpace(buffer))) { aoqi@0: if(textReported) { aoqi@0: textReported = false; aoqi@0: } else { aoqi@0: visitor.text(buffer); aoqi@0: } aoqi@0: } aoqi@0: buffer.setLength(0); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Reference to FI's StAXReader class, if FI can be loaded. aoqi@0: */ aoqi@0: private static final Class FI_STAX_READER_CLASS = initFIStAXReaderClass(); aoqi@0: private static final Constructor FI_CONNECTOR_CTOR = initFastInfosetConnectorClass(); aoqi@0: aoqi@0: private static Class initFIStAXReaderClass() { aoqi@0: try { aoqi@0: Class fisr = Class.forName("com.sun.xml.internal.org.jvnet.fastinfoset.stax.FastInfosetStreamReader"); aoqi@0: Class sdp = Class.forName("com.sun.xml.internal.fastinfoset.stax.StAXDocumentParser"); aoqi@0: // Check if StAXDocumentParser implements FastInfosetStreamReader aoqi@0: if (fisr.isAssignableFrom(sdp)) aoqi@0: return sdp; aoqi@0: else aoqi@0: return null; aoqi@0: } catch (Throwable e) { aoqi@0: return null; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private static Constructor initFastInfosetConnectorClass() { aoqi@0: try { aoqi@0: if (FI_STAX_READER_CLASS == null) aoqi@0: return null; aoqi@0: aoqi@0: Class c = Class.forName( aoqi@0: "com.sun.xml.internal.bind.v2.runtime.unmarshaller.FastInfosetConnector"); aoqi@0: return c.getConstructor(FI_STAX_READER_CLASS,XmlVisitor.class); aoqi@0: } catch (Throwable e) { aoqi@0: return null; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // aoqi@0: // reference to StAXEx classes aoqi@0: // aoqi@0: private static final Class STAX_EX_READER_CLASS = initStAXExReader(); aoqi@0: private static final Constructor STAX_EX_CONNECTOR_CTOR = initStAXExConnector(); aoqi@0: aoqi@0: private static Class initStAXExReader() { aoqi@0: try { aoqi@0: return Class.forName("com.sun.xml.internal.org.jvnet.staxex.XMLStreamReaderEx"); aoqi@0: } catch (Throwable e) { aoqi@0: return null; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private static Constructor initStAXExConnector() { aoqi@0: try { aoqi@0: Class c = Class.forName("com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXExConnector"); aoqi@0: return c.getConstructor(STAX_EX_READER_CLASS,XmlVisitor.class); aoqi@0: } catch (Throwable e) { aoqi@0: return null; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: }