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

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