1 /* 2 * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.bind.v2.runtime.unmarshaller; 27 28 import java.util.Iterator; 29 30 import javax.xml.namespace.QName; 31 import javax.xml.stream.Location; 32 import javax.xml.stream.XMLEventReader; 33 import javax.xml.stream.XMLStreamConstants; 34 import javax.xml.stream.XMLStreamException; 35 import javax.xml.stream.events.Attribute; 36 import javax.xml.stream.events.Characters; 37 import javax.xml.stream.events.EndElement; 38 import javax.xml.stream.events.Namespace; 39 import javax.xml.stream.events.StartElement; 40 import javax.xml.stream.events.XMLEvent; 41 42 import org.xml.sax.Attributes; 43 import org.xml.sax.SAXException; 44 import org.xml.sax.helpers.AttributesImpl; 45 46 /** 47 * This is a simple utility class that adapts StAX events from an 48 * {@link XMLEventReader} to unmarshaller events on a 49 * {@link XmlVisitor}, bridging between the two 50 * parser technologies. 51 * 52 * @author Ryan.Shoemaker@Sun.COM 53 * @version 1.0 54 */ 55 final class StAXEventConnector extends StAXConnector { 56 57 // StAX event source 58 private final XMLEventReader staxEventReader; 59 60 /** Current event. */ 61 private XMLEvent event; 62 63 /** 64 * Shared and reused {@link Attributes}. 65 */ 66 private final AttributesImpl attrs = new AttributesImpl(); 67 68 /** 69 * SAX may fire consective characters event, but we don't allow it. 70 * so use this buffer to perform buffering. 71 */ 72 private final StringBuilder buffer = new StringBuilder(); 73 74 private boolean seenText; 75 76 /** 77 * Construct a new StAX to SAX adapter that will convert a StAX event 78 * stream into a SAX event stream. 79 * 80 * @param staxCore 81 * StAX event source 82 * @param visitor 83 * sink 84 */ StAXEventConnector(XMLEventReader staxCore, XmlVisitor visitor)85 public StAXEventConnector(XMLEventReader staxCore, XmlVisitor visitor) { 86 super(visitor); 87 staxEventReader = staxCore; 88 } 89 bridge()90 public void bridge() throws XMLStreamException { 91 92 try { 93 // remembers the nest level of elements to know when we are done. 94 int depth=0; 95 96 event = staxEventReader.peek(); 97 98 if( !event.isStartDocument() && !event.isStartElement() ) 99 throw new IllegalStateException(); 100 101 // if the parser is on START_DOCUMENT, skip ahead to the first element 102 do { 103 event = staxEventReader.nextEvent(); 104 } while( !event.isStartElement() ); 105 106 handleStartDocument(event.asStartElement().getNamespaceContext()); 107 108 OUTER: 109 while(true) { 110 // These are all of the events listed in the javadoc for 111 // XMLEvent. 112 // The spec only really describes 11 of them. 113 switch (event.getEventType()) { 114 case XMLStreamConstants.START_ELEMENT : 115 handleStartElement(event.asStartElement()); 116 depth++; 117 break; 118 case XMLStreamConstants.END_ELEMENT : 119 depth--; 120 handleEndElement(event.asEndElement()); 121 if(depth==0) break OUTER; 122 break; 123 case XMLStreamConstants.CHARACTERS : 124 case XMLStreamConstants.CDATA : 125 case XMLStreamConstants.SPACE : 126 handleCharacters(event.asCharacters()); 127 break; 128 } 129 130 131 event=staxEventReader.nextEvent(); 132 } 133 134 handleEndDocument(); 135 event = null; // avoid keeping a stale reference 136 } catch (SAXException e) { 137 throw new XMLStreamException(e); 138 } 139 } 140 getCurrentLocation()141 protected Location getCurrentLocation() { 142 return event.getLocation(); 143 } 144 getCurrentQName()145 protected String getCurrentQName() { 146 QName qName; 147 if(event.isEndElement()) 148 qName = event.asEndElement().getName(); 149 else 150 qName = event.asStartElement().getName(); 151 return getQName(qName.getPrefix(), qName.getLocalPart()); 152 } 153 154 handleCharacters(Characters event)155 private void handleCharacters(Characters event) throws SAXException, XMLStreamException { 156 if(!predictor.expectText()) 157 return; // text isn't expected. simply skip 158 159 seenText = true; 160 161 // check the next event 162 XMLEvent next; 163 while(true) { 164 next = staxEventReader.peek(); 165 if(!isIgnorable(next)) 166 break; 167 staxEventReader.nextEvent(); 168 } 169 170 if(isTag(next)) { 171 // this is by far the common case --- you have <foo>abc</foo> or <foo>abc<bar/>...</foo> 172 visitor.text(event.getData()); 173 return; 174 } 175 176 // otherwise we have things like "abc<!-- test -->def". 177 // concatenate all text 178 buffer.append(event.getData()); 179 180 while(true) { 181 while(true) { 182 next = staxEventReader.peek(); 183 if(!isIgnorable(next)) 184 break; 185 staxEventReader.nextEvent(); 186 } 187 188 if(isTag(next)) { 189 // found all adjacent text 190 visitor.text(buffer); 191 buffer.setLength(0); 192 return; 193 } 194 195 buffer.append(next.asCharacters().getData()); 196 staxEventReader.nextEvent(); // consume 197 } 198 } 199 isTag(XMLEvent event)200 private boolean isTag(XMLEvent event) { 201 int eventType = event.getEventType(); 202 return eventType==XMLEvent.START_ELEMENT || eventType==XMLEvent.END_ELEMENT; 203 } 204 isIgnorable(XMLEvent event)205 private boolean isIgnorable(XMLEvent event) { 206 int eventType = event.getEventType(); 207 return eventType==XMLEvent.COMMENT || eventType==XMLEvent.PROCESSING_INSTRUCTION; 208 } 209 handleEndElement(EndElement event)210 private void handleEndElement(EndElement event) throws SAXException { 211 if(!seenText && predictor.expectText()) { 212 visitor.text(""); 213 } 214 215 // fire endElement 216 QName qName = event.getName(); 217 tagName.uri = fixNull(qName.getNamespaceURI()); 218 tagName.local = qName.getLocalPart(); 219 visitor.endElement(tagName); 220 221 // end namespace bindings 222 for( Iterator<Namespace> i = event.getNamespaces(); i.hasNext();) { 223 String prefix = fixNull(i.next().getPrefix()); // be defensive 224 visitor.endPrefixMapping(prefix); 225 } 226 227 seenText = false; 228 } 229 handleStartElement(StartElement event)230 private void handleStartElement(StartElement event) throws SAXException { 231 // start namespace bindings 232 for (Iterator i = event.getNamespaces(); i.hasNext();) { 233 Namespace ns = (Namespace)i.next(); 234 visitor.startPrefixMapping( 235 fixNull(ns.getPrefix()), 236 fixNull(ns.getNamespaceURI())); 237 } 238 239 // fire startElement 240 QName qName = event.getName(); 241 tagName.uri = fixNull(qName.getNamespaceURI()); 242 String localName = qName.getLocalPart(); 243 tagName.uri = fixNull(qName.getNamespaceURI()); 244 tagName.local = localName; 245 tagName.atts = getAttributes(event); 246 visitor.startElement(tagName); 247 248 seenText = false; 249 } 250 251 252 253 /** 254 * Get the attributes associated with the given START_ELEMENT StAXevent. 255 * 256 * @return the StAX attributes converted to an org.xml.sax.Attributes 257 */ getAttributes(StartElement event)258 private Attributes getAttributes(StartElement event) { 259 attrs.clear(); 260 261 // in SAX, namespace declarations are not part of attributes by default. 262 // (there's a property to control that, but as far as we are concerned 263 // we don't use it.) So don't add xmlns:* to attributes. 264 265 // gather non-namespace attrs 266 for (Iterator i = event.getAttributes(); i.hasNext();) { 267 Attribute staxAttr = (Attribute)i.next(); 268 269 QName name = staxAttr.getName(); 270 String uri = fixNull(name.getNamespaceURI()); 271 String localName = name.getLocalPart(); 272 String prefix = name.getPrefix(); 273 String qName; 274 if (prefix == null || prefix.length() == 0) 275 qName = localName; 276 else 277 qName = prefix + ':' + localName; 278 String type = staxAttr.getDTDType(); 279 String value = staxAttr.getValue(); 280 281 attrs.addAttribute(uri, localName, qName, type, value); 282 } 283 284 return attrs; 285 } 286 } 287