| /* |
| * 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.xml.internal.bind.v2.runtime.unmarshaller; |
| |
| import java.util.Iterator; |
| |
| import javax.xml.namespace.QName; |
| import javax.xml.stream.Location; |
| import javax.xml.stream.XMLEventReader; |
| import javax.xml.stream.XMLStreamConstants; |
| import javax.xml.stream.XMLStreamException; |
| import javax.xml.stream.events.Attribute; |
| import javax.xml.stream.events.Characters; |
| import javax.xml.stream.events.EndElement; |
| import javax.xml.stream.events.Namespace; |
| import javax.xml.stream.events.StartElement; |
| import javax.xml.stream.events.XMLEvent; |
| |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.helpers.AttributesImpl; |
| |
| /** |
| * This is a simple utility class that adapts StAX events from an |
| * {@link XMLEventReader} to unmarshaller events on a |
| * {@link XmlVisitor}, bridging between the two |
| * parser technologies. |
| * |
| * @author Ryan.Shoemaker@Sun.COM |
| * @version 1.0 |
| */ |
| final class StAXEventConnector extends StAXConnector { |
| |
| // StAX event source |
| private final XMLEventReader staxEventReader; |
| |
| /** Current event. */ |
| private XMLEvent event; |
| |
| /** |
| * Shared and reused {@link Attributes}. |
| */ |
| private final AttributesImpl attrs = new AttributesImpl(); |
| |
| /** |
| * SAX may fire consective characters event, but we don't allow it. |
| * so use this buffer to perform buffering. |
| */ |
| private final StringBuilder buffer = new StringBuilder(); |
| |
| private boolean seenText; |
| |
| /** |
| * 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 visitor |
| * sink |
| */ |
| public StAXEventConnector(XMLEventReader staxCore, XmlVisitor visitor) { |
| super(visitor); |
| staxEventReader = staxCore; |
| } |
| |
| public void bridge() throws XMLStreamException { |
| |
| try { |
| // remembers the nest level of elements to know when we are done. |
| int depth=0; |
| |
| event = staxEventReader.peek(); |
| |
| if( !event.isStartDocument() && !event.isStartElement() ) |
| throw new IllegalStateException(); |
| |
| // if the parser is on START_DOCUMENT, skip ahead to the first element |
| do { |
| event = staxEventReader.nextEvent(); |
| } while( !event.isStartElement() ); |
| |
| handleStartDocument(event.asStartElement().getNamespaceContext()); |
| |
| OUTER: |
| while(true) { |
| // These are all of the events listed in the javadoc for |
| // XMLEvent. |
| // The spec only really describes 11 of them. |
| switch (event.getEventType()) { |
| case XMLStreamConstants.START_ELEMENT : |
| handleStartElement(event.asStartElement()); |
| depth++; |
| break; |
| case XMLStreamConstants.END_ELEMENT : |
| depth--; |
| handleEndElement(event.asEndElement()); |
| if(depth==0) break OUTER; |
| break; |
| case XMLStreamConstants.CHARACTERS : |
| case XMLStreamConstants.CDATA : |
| case XMLStreamConstants.SPACE : |
| handleCharacters(event.asCharacters()); |
| break; |
| } |
| |
| |
| event=staxEventReader.nextEvent(); |
| } |
| |
| handleEndDocument(); |
| event = null; // avoid keeping a stale reference |
| } catch (SAXException e) { |
| throw new XMLStreamException(e); |
| } |
| } |
| |
| protected Location getCurrentLocation() { |
| return event.getLocation(); |
| } |
| |
| protected String getCurrentQName() { |
| QName qName; |
| if(event.isEndElement()) |
| qName = event.asEndElement().getName(); |
| else |
| qName = event.asStartElement().getName(); |
| return getQName(qName.getPrefix(), qName.getLocalPart()); |
| } |
| |
| |
| private void handleCharacters(Characters event) throws SAXException, XMLStreamException { |
| if(!predictor.expectText()) |
| return; // text isn't expected. simply skip |
| |
| seenText = true; |
| |
| // check the next event |
| XMLEvent next; |
| while(true) { |
| next = staxEventReader.peek(); |
| if(!isIgnorable(next)) |
| break; |
| staxEventReader.nextEvent(); |
| } |
| |
| if(isTag(next)) { |
| // this is by far the common case --- you have <foo>abc</foo> or <foo>abc<bar/>...</foo> |
| visitor.text(event.getData()); |
| return; |
| } |
| |
| // otherwise we have things like "abc<!-- test -->def". |
| // concatenate all text |
| buffer.append(event.getData()); |
| |
| while(true) { |
| while(true) { |
| next = staxEventReader.peek(); |
| if(!isIgnorable(next)) |
| break; |
| staxEventReader.nextEvent(); |
| } |
| |
| if(isTag(next)) { |
| // found all adjacent text |
| visitor.text(buffer); |
| buffer.setLength(0); |
| return; |
| } |
| |
| buffer.append(next.asCharacters().getData()); |
| staxEventReader.nextEvent(); // consume |
| } |
| } |
| |
| private boolean isTag(XMLEvent event) { |
| int eventType = event.getEventType(); |
| return eventType==XMLEvent.START_ELEMENT || eventType==XMLEvent.END_ELEMENT; |
| } |
| |
| private boolean isIgnorable(XMLEvent event) { |
| int eventType = event.getEventType(); |
| return eventType==XMLEvent.COMMENT || eventType==XMLEvent.PROCESSING_INSTRUCTION; |
| } |
| |
| private void handleEndElement(EndElement event) throws SAXException { |
| if(!seenText && predictor.expectText()) { |
| visitor.text(""); |
| } |
| |
| // fire endElement |
| QName qName = event.getName(); |
| tagName.uri = fixNull(qName.getNamespaceURI()); |
| tagName.local = qName.getLocalPart(); |
| visitor.endElement(tagName); |
| |
| // end namespace bindings |
| for( Iterator<Namespace> i = event.getNamespaces(); i.hasNext();) { |
| String prefix = fixNull(i.next().getPrefix()); // be defensive |
| visitor.endPrefixMapping(prefix); |
| } |
| |
| seenText = false; |
| } |
| |
| private void handleStartElement(StartElement event) throws SAXException { |
| // start namespace bindings |
| for (Iterator i = event.getNamespaces(); i.hasNext();) { |
| Namespace ns = (Namespace)i.next(); |
| visitor.startPrefixMapping( |
| fixNull(ns.getPrefix()), |
| fixNull(ns.getNamespaceURI())); |
| } |
| |
| // fire startElement |
| QName qName = event.getName(); |
| tagName.uri = fixNull(qName.getNamespaceURI()); |
| String localName = qName.getLocalPart(); |
| tagName.uri = fixNull(qName.getNamespaceURI()); |
| tagName.local = localName; |
| tagName.atts = getAttributes(event); |
| visitor.startElement(tagName); |
| |
| seenText = false; |
| } |
| |
| |
| |
| /** |
| * Get the attributes associated with the given START_ELEMENT StAXevent. |
| * |
| * @return the StAX attributes converted to an org.xml.sax.Attributes |
| */ |
| private Attributes getAttributes(StartElement event) { |
| attrs.clear(); |
| |
| // 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 (Iterator i = event.getAttributes(); i.hasNext();) { |
| Attribute staxAttr = (Attribute)i.next(); |
| |
| QName name = staxAttr.getName(); |
| String uri = fixNull(name.getNamespaceURI()); |
| String localName = name.getLocalPart(); |
| String prefix = name.getPrefix(); |
| String qName; |
| if (prefix == null || prefix.length() == 0) |
| qName = localName; |
| else |
| qName = prefix + ':' + localName; |
| String type = staxAttr.getDTDType(); |
| String value = staxAttr.getValue(); |
| |
| attrs.addAttribute(uri, localName, qName, type, value); |
| } |
| |
| return attrs; |
| } |
| } |