1 /* 2 * Copyright (c) 1997, 2013, 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.istack.internal; 27 28 import org.xml.sax.ContentHandler; 29 import org.xml.sax.SAXException; 30 import org.xml.sax.Locator; 31 import org.xml.sax.Attributes; 32 import org.xml.sax.helpers.AttributesImpl; 33 34 import javax.xml.stream.XMLStreamReader; 35 import javax.xml.stream.XMLStreamException; 36 import javax.xml.stream.XMLStreamConstants; 37 import javax.xml.namespace.QName; 38 39 /** 40 * This is a simple utility class that adapts StAX events from an 41 * {@link XMLStreamReader} to SAX events on a 42 * {@link ContentHandler}, bridging between the two 43 * parser technologies. 44 * 45 * @author Ryan.Shoemaker@Sun.COM 46 * @version 1.0 47 */ 48 public class XMLStreamReaderToContentHandler { 49 50 // StAX event source 51 private final XMLStreamReader staxStreamReader; 52 53 // SAX event sink 54 private final ContentHandler saxHandler; 55 56 // if true, when the conversion is completed, leave the cursor to the last 57 // event that was fired (such as end element) 58 private final boolean eagerQuit; 59 60 /** 61 * If true, not start/endDocument event. 62 */ 63 private final boolean fragment; 64 65 // array of the even length of the form { prefix0, uri0, prefix1, uri1, ... } 66 private final String[] inscopeNamespaces; 67 68 /** 69 * @see #XMLStreamReaderToContentHandler(XMLStreamReader, ContentHandler, boolean, boolean, String[]) 70 */ 71 public XMLStreamReaderToContentHandler(XMLStreamReader staxCore, ContentHandler saxCore, boolean eagerQuit, boolean fragment) { 72 this(staxCore, saxCore, eagerQuit, fragment, new String[0]); 73 } 74 75 /** 76 * Construct a new StAX to SAX adapter that will convert a StAX event 77 * stream into a SAX event stream. 78 * 79 * @param staxCore 80 * StAX event source 81 * @param saxCore 82 * SAXevent sink 83 * @param eagerQuit 84 * @param fragment 85 * @param inscopeNamespaces 86 * array of the even length of the form { prefix0, uri0, prefix1, uri1, ... } 87 */ 88 public XMLStreamReaderToContentHandler(XMLStreamReader staxCore, ContentHandler saxCore, 89 boolean eagerQuit, boolean fragment, String[] inscopeNamespaces) { 90 this.staxStreamReader = staxCore; 91 this.saxHandler = saxCore; 92 this.eagerQuit = eagerQuit; 93 this.fragment = fragment; 94 this.inscopeNamespaces = inscopeNamespaces; 95 assert inscopeNamespaces.length%2 == 0; 96 } 97 98 99 /* 100 * @see StAXReaderToContentHandler#bridge() 101 */ 102 public void bridge() throws XMLStreamException { 103 104 try { 105 // remembers the nest level of elements to know when we are done. 106 int depth=0; 107 108 // if the parser is at the start tag, proceed to the first element 109 int event = staxStreamReader.getEventType(); 110 if(event == XMLStreamConstants.START_DOCUMENT) { 111 // nextTag doesn't correctly handle DTDs 112 while( !staxStreamReader.isStartElement() ) 113 event = staxStreamReader.next(); 114 } 115 116 117 if( event!=XMLStreamConstants.START_ELEMENT) 118 throw new IllegalStateException("The current event is not START_ELEMENT\n but " + event); 119 120 handleStartDocument(); 121 122 for(int i=0; i < inscopeNamespaces.length; i+=2) { 123 saxHandler.startPrefixMapping(inscopeNamespaces[i], inscopeNamespaces[i+1]); 124 } 125 126 OUTER: 127 do { 128 // These are all of the events listed in the javadoc for 129 // XMLEvent. 130 // The spec only really describes 11 of them. 131 switch (event) { 132 case XMLStreamConstants.START_ELEMENT : 133 depth++; 134 handleStartElement(); 135 break; 136 case XMLStreamConstants.END_ELEMENT : 137 handleEndElement(); 138 depth--; 139 if(depth==0 && eagerQuit) 140 break OUTER; 141 break; 142 case XMLStreamConstants.CHARACTERS : 143 handleCharacters(); 144 break; 145 case XMLStreamConstants.ENTITY_REFERENCE : 146 handleEntityReference(); 147 break; 148 case XMLStreamConstants.PROCESSING_INSTRUCTION : 149 handlePI(); 150 break; 151 case XMLStreamConstants.COMMENT : 152 handleComment(); 153 break; 154 case XMLStreamConstants.DTD : 155 handleDTD(); 156 break; 157 case XMLStreamConstants.ATTRIBUTE : 158 handleAttribute(); 159 break; 160 case XMLStreamConstants.NAMESPACE : 161 handleNamespace(); 162 break; 163 case XMLStreamConstants.CDATA : 164 handleCDATA(); 165 break; 166 case XMLStreamConstants.ENTITY_DECLARATION : 167 handleEntityDecl(); 168 break; 169 case XMLStreamConstants.NOTATION_DECLARATION : 170 handleNotationDecl(); 171 break; 172 case XMLStreamConstants.SPACE : 173 handleSpace(); 174 break; 175 default : 176 throw new InternalError("processing event: " + event); 177 } 178 179 event=staxStreamReader.next(); 180 } while (depth!=0); 181 182 for(int i=0; i < inscopeNamespaces.length; i+=2) { 183 saxHandler.endPrefixMapping(inscopeNamespaces[i]); 184 } 185 186 handleEndDocument(); 187 } catch (SAXException e) { 188 throw new XMLStreamException2(e); 189 } 190 } 191 192 private void handleEndDocument() throws SAXException { 193 if(fragment) 194 return; 195 196 saxHandler.endDocument(); 197 } 198 199 private void handleStartDocument() throws SAXException { 200 if(fragment) 201 return; 202 203 saxHandler.setDocumentLocator(new Locator() { 204 public int getColumnNumber() { 205 return staxStreamReader.getLocation().getColumnNumber(); 206 } 207 public int getLineNumber() { 208 return staxStreamReader.getLocation().getLineNumber(); 209 } 210 public String getPublicId() { 211 return staxStreamReader.getLocation().getPublicId(); 212 } 213 public String getSystemId() { 214 return staxStreamReader.getLocation().getSystemId(); 215 } 216 }); 217 saxHandler.startDocument(); 218 } 219 220 private void handlePI() throws XMLStreamException { 221 try { 222 saxHandler.processingInstruction( 223 staxStreamReader.getPITarget(), 224 staxStreamReader.getPIData()); 225 } catch (SAXException e) { 226 throw new XMLStreamException2(e); 227 } 228 } 229 230 private void handleCharacters() throws XMLStreamException { 231 try { 232 saxHandler.characters( 233 staxStreamReader.getTextCharacters(), 234 staxStreamReader.getTextStart(), 235 staxStreamReader.getTextLength() ); 236 } catch (SAXException e) { 237 throw new XMLStreamException2(e); 238 } 239 } 240 241 private void handleEndElement() throws XMLStreamException { 242 QName qName = staxStreamReader.getName(); 243 244 try { 245 String pfix = qName.getPrefix(); 246 String rawname = (pfix == null || pfix.length() == 0) 247 ? qName.getLocalPart() 248 : pfix + ':' + qName.getLocalPart(); 249 // fire endElement 250 saxHandler.endElement( 251 qName.getNamespaceURI(), 252 qName.getLocalPart(), 253 rawname); 254 255 // end namespace bindings 256 int nsCount = staxStreamReader.getNamespaceCount(); 257 for (int i = nsCount - 1; i >= 0; i--) { 258 String prefix = staxStreamReader.getNamespacePrefix(i); 259 if (prefix == null) { // true for default namespace 260 prefix = ""; 261 } 262 saxHandler.endPrefixMapping(prefix); 263 } 264 } catch (SAXException e) { 265 throw new XMLStreamException2(e); 266 } 267 } 268 269 private void handleStartElement() throws XMLStreamException { 270 271 try { 272 // start namespace bindings 273 int nsCount = staxStreamReader.getNamespaceCount(); 274 for (int i = 0; i < nsCount; i++) { 275 saxHandler.startPrefixMapping( 276 fixNull(staxStreamReader.getNamespacePrefix(i)), 277 fixNull(staxStreamReader.getNamespaceURI(i))); 278 } 279 280 // fire startElement 281 QName qName = staxStreamReader.getName(); 282 String prefix = qName.getPrefix(); 283 String rawname; 284 if(prefix==null || prefix.length()==0) 285 rawname = qName.getLocalPart(); 286 else 287 rawname = prefix + ':' + qName.getLocalPart(); 288 Attributes attrs = getAttributes(); 289 saxHandler.startElement( 290 qName.getNamespaceURI(), 291 qName.getLocalPart(), 292 rawname, 293 attrs); 294 } catch (SAXException e) { 295 throw new XMLStreamException2(e); 296 } 297 } 298 299 private static String fixNull(String s) { 300 if(s==null) return ""; 301 else return s; 302 } 303 304 /** 305 * Get the attributes associated with the given START_ELEMENT or ATTRIBUTE 306 * StAXevent. 307 * 308 * @return the StAX attributes converted to an org.xml.sax.Attributes 309 */ 310 private Attributes getAttributes() { 311 AttributesImpl attrs = new AttributesImpl(); 312 313 int eventType = staxStreamReader.getEventType(); 314 if (eventType != XMLStreamConstants.ATTRIBUTE 315 && eventType != XMLStreamConstants.START_ELEMENT) { 316 throw new InternalError( 317 "getAttributes() attempting to process: " + eventType); 318 } 319 320 // in SAX, namespace declarations are not part of attributes by default. 321 // (there's a property to control that, but as far as we are concerned 322 // we don't use it.) So don't add xmlns:* to attributes. 323 324 // gather non-namespace attrs 325 for (int i = 0; i < staxStreamReader.getAttributeCount(); i++) { 326 String uri = staxStreamReader.getAttributeNamespace(i); 327 if(uri==null) uri=""; 328 String localName = staxStreamReader.getAttributeLocalName(i); 329 String prefix = staxStreamReader.getAttributePrefix(i); 330 String qName; 331 if(prefix==null || prefix.length()==0) 332 qName = localName; 333 else 334 qName = prefix + ':' + localName; 335 String type = staxStreamReader.getAttributeType(i); 336 String value = staxStreamReader.getAttributeValue(i); 337 338 attrs.addAttribute(uri, localName, qName, type, value); 339 } 340 341 return attrs; 342 } 343 344 private void handleNamespace() { 345 // no-op ??? 346 // namespace events don't normally occur outside of a startElement 347 // or endElement 348 } 349 350 private void handleAttribute() { 351 // no-op ??? 352 // attribute events don't normally occur outside of a startElement 353 // or endElement 354 } 355 356 private void handleDTD() { 357 // no-op ??? 358 // it seems like we need to pass this info along, but how? 359 } 360 361 private void handleComment() { 362 // no-op ??? 363 } 364 365 private void handleEntityReference() { 366 // no-op ??? 367 } 368 369 private void handleSpace() { 370 // no-op ??? 371 // this event is listed in the javadoc, but not in the spec. 372 } 373 374 private void handleNotationDecl() { 375 // no-op ??? 376 // this event is listed in the javadoc, but not in the spec. 377 } 378 379 private void handleEntityDecl() { 380 // no-op ??? 381 // this event is listed in the javadoc, but not in the spec. 382 } 383 384 private void handleCDATA() { 385 // no-op ??? 386 // this event is listed in the javadoc, but not in the spec. 387 } 388 }