1 /* 2 * Copyright (c) 2005, 2012, 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.stream.buffer.stax; 27 28 import com.sun.xml.internal.stream.buffer.AbstractProcessor; 29 import com.sun.xml.internal.stream.buffer.XMLStreamBuffer; 30 31 import java.io.IOException; 32 import java.util.Collections; 33 import java.util.HashSet; 34 import java.util.Map; 35 import java.util.Set; 36 37 import com.sun.xml.internal.org.jvnet.staxex.Base64Data; 38 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamWriterEx; 39 40 import javax.xml.stream.XMLStreamException; 41 import javax.xml.stream.XMLStreamWriter; 42 43 44 /** 45 * A processor of a {@link XMLStreamBuffer} that writes the XML infoset to a 46 * {@link XMLStreamWriter}. 47 * 48 * @author Paul.Sandoz@Sun.Com 49 * @author K.Venugopal@sun.com 50 */ 51 public class StreamWriterBufferProcessor extends AbstractProcessor { 52 53 StreamWriterBufferProcessor()54 public StreamWriterBufferProcessor() { 55 } 56 57 /** 58 * @deprecated 59 * Use {@link #StreamWriterBufferProcessor(XMLStreamBuffer, boolean)} 60 */ StreamWriterBufferProcessor(XMLStreamBuffer buffer)61 public StreamWriterBufferProcessor(XMLStreamBuffer buffer) { 62 setXMLStreamBuffer(buffer,buffer.isFragment()); 63 } 64 65 /** 66 * @param produceFragmentEvent 67 * True to generate fragment SAX events without start/endDocument. 68 * False to generate a full document SAX events. 69 */ StreamWriterBufferProcessor(XMLStreamBuffer buffer,boolean produceFragmentEvent)70 public StreamWriterBufferProcessor(XMLStreamBuffer buffer,boolean produceFragmentEvent) { 71 setXMLStreamBuffer(buffer,produceFragmentEvent); 72 } 73 process(XMLStreamBuffer buffer, XMLStreamWriter writer)74 public final void process(XMLStreamBuffer buffer, XMLStreamWriter writer) throws XMLStreamException { 75 setXMLStreamBuffer(buffer,buffer.isFragment()); 76 process(writer); 77 } 78 process(XMLStreamWriter writer)79 public void process(XMLStreamWriter writer) throws XMLStreamException { 80 if(_fragmentMode){ 81 writeFragment(writer); 82 }else{ 83 write(writer); 84 } 85 } 86 87 /** 88 * @deprecated 89 * Use {@link #setXMLStreamBuffer(XMLStreamBuffer, boolean)} 90 */ setXMLStreamBuffer(XMLStreamBuffer buffer)91 public void setXMLStreamBuffer(XMLStreamBuffer buffer) { 92 setBuffer(buffer); 93 } 94 95 /** 96 * @param produceFragmentEvent 97 * True to generate fragment SAX events without start/endDocument. 98 * False to generate a full document SAX events. 99 */ setXMLStreamBuffer(XMLStreamBuffer buffer, boolean produceFragmentEvent)100 public void setXMLStreamBuffer(XMLStreamBuffer buffer, boolean produceFragmentEvent) { 101 setBuffer(buffer,produceFragmentEvent); 102 } 103 104 /** 105 * Writes a full XML infoset event to the given writer, 106 * including start/end document. 107 * Any inscope namespaces present will be written as namespace 108 * delcarations on each top-level element. 109 */ write(XMLStreamWriter writer)110 public void write(XMLStreamWriter writer) throws XMLStreamException{ 111 112 if(!_fragmentMode) { 113 if(_treeCount>1) 114 throw new IllegalStateException("forest cannot be written as a full infoset"); 115 writer.writeStartDocument(); 116 } 117 118 while(true) { 119 int item = getEIIState(peekStructure()); 120 writer.flush(); 121 122 switch(item) { 123 case STATE_DOCUMENT: 124 readStructure(); //skip 125 break; 126 case STATE_ELEMENT_U_LN_QN: 127 case STATE_ELEMENT_P_U_LN: 128 case STATE_ELEMENT_U_LN: 129 case STATE_ELEMENT_LN: 130 writeFragment(writer); 131 break; 132 case STATE_COMMENT_AS_CHAR_ARRAY_SMALL: { 133 readStructure(); 134 final int length = readStructure(); 135 final int start = readContentCharactersBuffer(length); 136 final String comment = new String(_contentCharactersBuffer, start, length); 137 writer.writeComment(comment); 138 break; 139 } 140 case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM: { 141 readStructure(); 142 final int length = readStructure16(); 143 final int start = readContentCharactersBuffer(length); 144 final String comment = new String(_contentCharactersBuffer, start, length); 145 writer.writeComment(comment); 146 break; 147 } 148 case STATE_COMMENT_AS_CHAR_ARRAY_COPY: { 149 readStructure(); 150 final char[] ch = readContentCharactersCopy(); 151 writer.writeComment(new String(ch)); 152 break; 153 } 154 case STATE_PROCESSING_INSTRUCTION: 155 readStructure(); 156 writer.writeProcessingInstruction(readStructureString(), readStructureString()); 157 break; 158 case STATE_END: // done 159 readStructure(); 160 writer.writeEndDocument(); 161 return; 162 default: 163 throw new XMLStreamException("Invalid State "+item); 164 } 165 } 166 167 } 168 169 /** 170 * Writes the buffer as a fragment, meaning 171 * the writer will not receive start/endDocument events. 172 * Any inscope namespaces present will be written as namespace 173 * delcarations on each top-level element. 174 * <p> 175 * If {@link XMLStreamBuffer} has a forest, this method will write all the forests. 176 */ writeFragment(XMLStreamWriter writer)177 public void writeFragment(XMLStreamWriter writer) throws XMLStreamException { 178 if (writer instanceof XMLStreamWriterEx) { 179 writeFragmentEx((XMLStreamWriterEx)writer); 180 } else { 181 writeFragmentNoEx(writer); 182 } 183 } 184 writeFragmentEx(XMLStreamWriterEx writer)185 public void writeFragmentEx(XMLStreamWriterEx writer) throws XMLStreamException { 186 int depth = 0; // used to determine when we are done with a tree. 187 188 int item = getEIIState(peekStructure()); 189 if(item==STATE_DOCUMENT) 190 readStructure(); // skip STATE_DOCUMENT 191 192 do { 193 194 item = readEiiState(); 195 196 switch(item) { 197 case STATE_DOCUMENT: 198 throw new AssertionError(); 199 case STATE_ELEMENT_U_LN_QN: { 200 depth ++; 201 final String uri = readStructureString(); 202 final String localName = readStructureString(); 203 final String prefix = getPrefixFromQName(readStructureString()); 204 writer.writeStartElement(prefix,localName,uri); 205 writeAttributes(writer, isInscope(depth)); 206 break; 207 } 208 case STATE_ELEMENT_P_U_LN: { 209 depth ++; 210 final String prefix = readStructureString(); 211 final String uri = readStructureString(); 212 final String localName = readStructureString(); 213 writer.writeStartElement(prefix,localName,uri); 214 writeAttributes(writer, isInscope(depth)); 215 break; 216 } 217 case STATE_ELEMENT_U_LN: { 218 depth ++; 219 final String uri = readStructureString(); 220 final String localName = readStructureString(); 221 writer.writeStartElement("",localName,uri); 222 writeAttributes(writer, isInscope(depth)); 223 break; 224 } 225 case STATE_ELEMENT_LN: { 226 depth ++; 227 final String localName = readStructureString(); 228 writer.writeStartElement(localName); 229 writeAttributes(writer, isInscope(depth)); 230 break; 231 } 232 case STATE_TEXT_AS_CHAR_ARRAY_SMALL: { 233 final int length = readStructure(); 234 final int start = readContentCharactersBuffer(length); 235 writer.writeCharacters(_contentCharactersBuffer,start,length); 236 break; 237 } 238 case STATE_TEXT_AS_CHAR_ARRAY_MEDIUM: { 239 final int length = readStructure16(); 240 final int start = readContentCharactersBuffer(length); 241 writer.writeCharacters(_contentCharactersBuffer,start,length); 242 break; 243 } 244 case STATE_TEXT_AS_CHAR_ARRAY_COPY: { 245 char[] c = readContentCharactersCopy(); 246 writer.writeCharacters(c,0,c.length); 247 break; 248 } 249 case STATE_TEXT_AS_STRING: { 250 final String s = readContentString(); 251 writer.writeCharacters(s); 252 break; 253 } 254 case STATE_TEXT_AS_OBJECT: { 255 final CharSequence c = (CharSequence)readContentObject(); 256 writer.writePCDATA(c); 257 break; 258 } 259 case STATE_COMMENT_AS_CHAR_ARRAY_SMALL: { 260 final int length = readStructure(); 261 final int start = readContentCharactersBuffer(length); 262 final String comment = new String(_contentCharactersBuffer, start, length); 263 writer.writeComment(comment); 264 break; 265 } 266 case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM: { 267 final int length = readStructure16(); 268 final int start = readContentCharactersBuffer(length); 269 final String comment = new String(_contentCharactersBuffer, start, length); 270 writer.writeComment(comment); 271 break; 272 } 273 case STATE_COMMENT_AS_CHAR_ARRAY_COPY: { 274 final char[] ch = readContentCharactersCopy(); 275 writer.writeComment(new String(ch)); 276 break; 277 } 278 case STATE_PROCESSING_INSTRUCTION: 279 writer.writeProcessingInstruction(readStructureString(), readStructureString()); 280 break; 281 case STATE_END: 282 writer.writeEndElement(); 283 depth --; 284 if(depth==0) 285 _treeCount--; 286 break; 287 default: 288 throw new XMLStreamException("Invalid State "+item); 289 } 290 } while(depth>0 || _treeCount>0); 291 292 } 293 writeFragmentNoEx(XMLStreamWriter writer)294 public void writeFragmentNoEx(XMLStreamWriter writer) throws XMLStreamException { 295 int depth = 0; 296 297 int item = getEIIState(peekStructure()); 298 if(item==STATE_DOCUMENT) 299 readStructure(); // skip STATE_DOCUMENT 300 301 do { 302 item = readEiiState(); 303 304 switch(item) { 305 case STATE_DOCUMENT: 306 throw new AssertionError(); 307 case STATE_ELEMENT_U_LN_QN: { 308 depth ++; 309 final String uri = readStructureString(); 310 final String localName = readStructureString(); 311 final String prefix = getPrefixFromQName(readStructureString()); 312 writer.writeStartElement(prefix,localName,uri); 313 writeAttributes(writer, isInscope(depth)); 314 break; 315 } 316 case STATE_ELEMENT_P_U_LN: { 317 depth ++; 318 final String prefix = readStructureString(); 319 final String uri = readStructureString(); 320 final String localName = readStructureString(); 321 writer.writeStartElement(prefix,localName,uri); 322 writeAttributes(writer, isInscope(depth)); 323 break; 324 } 325 case STATE_ELEMENT_U_LN: { 326 depth ++; 327 final String uri = readStructureString(); 328 final String localName = readStructureString(); 329 writer.writeStartElement("",localName,uri); 330 writeAttributes(writer, isInscope(depth)); 331 break; 332 } 333 case STATE_ELEMENT_LN: { 334 depth ++; 335 final String localName = readStructureString(); 336 writer.writeStartElement(localName); 337 writeAttributes(writer, isInscope(depth)); 338 break; 339 } 340 case STATE_TEXT_AS_CHAR_ARRAY_SMALL: { 341 final int length = readStructure(); 342 final int start = readContentCharactersBuffer(length); 343 writer.writeCharacters(_contentCharactersBuffer,start,length); 344 break; 345 } 346 case STATE_TEXT_AS_CHAR_ARRAY_MEDIUM: { 347 final int length = readStructure16(); 348 final int start = readContentCharactersBuffer(length); 349 writer.writeCharacters(_contentCharactersBuffer,start,length); 350 break; 351 } 352 case STATE_TEXT_AS_CHAR_ARRAY_COPY: { 353 char[] c = readContentCharactersCopy(); 354 writer.writeCharacters(c,0,c.length); 355 break; 356 } 357 case STATE_TEXT_AS_STRING: { 358 final String s = readContentString(); 359 writer.writeCharacters(s); 360 break; 361 } 362 case STATE_TEXT_AS_OBJECT: { 363 final CharSequence c = (CharSequence)readContentObject(); 364 if (c instanceof Base64Data) { 365 try { 366 Base64Data bd = (Base64Data)c; 367 bd.writeTo(writer); 368 } catch (IOException e) { 369 throw new XMLStreamException(e); 370 } 371 } else { 372 writer.writeCharacters(c.toString()); 373 } 374 break; 375 } 376 case STATE_COMMENT_AS_CHAR_ARRAY_SMALL: { 377 final int length = readStructure(); 378 final int start = readContentCharactersBuffer(length); 379 final String comment = new String(_contentCharactersBuffer, start, length); 380 writer.writeComment(comment); 381 break; 382 } 383 case STATE_COMMENT_AS_CHAR_ARRAY_MEDIUM: { 384 final int length = readStructure16(); 385 final int start = readContentCharactersBuffer(length); 386 final String comment = new String(_contentCharactersBuffer, start, length); 387 writer.writeComment(comment); 388 break; 389 } 390 case STATE_COMMENT_AS_CHAR_ARRAY_COPY: { 391 final char[] ch = readContentCharactersCopy(); 392 writer.writeComment(new String(ch)); 393 break; 394 } 395 case STATE_PROCESSING_INSTRUCTION: 396 writer.writeProcessingInstruction(readStructureString(), readStructureString()); 397 break; 398 case STATE_END: 399 writer.writeEndElement(); 400 depth --; 401 if(depth==0) 402 _treeCount--; 403 break; 404 default: 405 throw new XMLStreamException("Invalid State "+item); 406 } 407 } while(depth > 0 || _treeCount>0); 408 409 } 410 isInscope(int depth)411 private boolean isInscope(int depth) { 412 return _buffer.getInscopeNamespaces().size() > 0 && depth ==1; 413 } 414 415 /* 416 * @param inscope: true means write inscope namespaces 417 */ writeAttributes(XMLStreamWriter writer, boolean inscope)418 private void writeAttributes(XMLStreamWriter writer, boolean inscope) throws XMLStreamException { 419 // prefixSet to collect prefixes that are written before writing inscope namespaces 420 Set<String> prefixSet = inscope ? new HashSet<String>() : Collections.<String>emptySet(); 421 int item = peekStructure(); 422 if ((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE) { 423 // Skip the namespace declarations on the element 424 // they will have been added already 425 item = writeNamespaceAttributes(item, writer, inscope, prefixSet); 426 } 427 if (inscope) { 428 writeInscopeNamespaces(writer, prefixSet); 429 } 430 if ((item & TYPE_MASK) == T_ATTRIBUTE) { 431 writeAttributes(item, writer); 432 } 433 } 434 fixNull(String s)435 private static String fixNull(String s) { 436 if (s == null) return ""; 437 else return s; 438 } 439 440 /* 441 * @param prefixSet: already written prefixes 442 */ writeInscopeNamespaces(XMLStreamWriter writer, Set<String> prefixSet)443 private void writeInscopeNamespaces(XMLStreamWriter writer, Set<String> prefixSet) throws XMLStreamException { 444 for (Map.Entry<String, String> e : _buffer.getInscopeNamespaces().entrySet()) { 445 String key = fixNull(e.getKey()); 446 // If the prefix is already written, do not write the prefix 447 if (!prefixSet.contains(key)) { 448 writer.writeNamespace(key, e.getValue()); 449 } 450 } 451 } 452 writeNamespaceAttributes(int item, XMLStreamWriter writer, boolean collectPrefixes, Set<String> prefixSet)453 private int writeNamespaceAttributes(int item, XMLStreamWriter writer, boolean collectPrefixes, Set<String> prefixSet) throws XMLStreamException { 454 do { 455 switch(getNIIState(item)){ 456 case STATE_NAMESPACE_ATTRIBUTE: 457 // Undeclaration of default namespace 458 writer.writeDefaultNamespace(""); 459 if (collectPrefixes) { 460 prefixSet.add(""); 461 } 462 break; 463 case STATE_NAMESPACE_ATTRIBUTE_P: 464 // Undeclaration of namespace 465 // Declaration with prefix 466 String prefix = readStructureString(); 467 writer.writeNamespace(prefix, ""); 468 if (collectPrefixes) { 469 prefixSet.add(prefix); 470 } 471 break; 472 case STATE_NAMESPACE_ATTRIBUTE_P_U: 473 // Declaration with prefix 474 prefix = readStructureString(); 475 writer.writeNamespace(prefix, readStructureString()); 476 if (collectPrefixes) { 477 prefixSet.add(prefix); 478 } 479 break; 480 case STATE_NAMESPACE_ATTRIBUTE_U: 481 // Default declaration 482 writer.writeDefaultNamespace(readStructureString()); 483 if (collectPrefixes) { 484 prefixSet.add(""); 485 } 486 break; 487 } 488 readStructure(); 489 490 item = peekStructure(); 491 } while((item & TYPE_MASK) == T_NAMESPACE_ATTRIBUTE); 492 493 return item; 494 } 495 writeAttributes(int item, XMLStreamWriter writer)496 private void writeAttributes(int item, XMLStreamWriter writer) throws XMLStreamException { 497 do { 498 switch(getAIIState(item)) { 499 case STATE_ATTRIBUTE_U_LN_QN: { 500 final String uri = readStructureString(); 501 final String localName = readStructureString(); 502 final String prefix = getPrefixFromQName(readStructureString()); 503 writer.writeAttribute(prefix,uri,localName,readContentString()); 504 break; 505 } 506 case STATE_ATTRIBUTE_P_U_LN: 507 writer.writeAttribute(readStructureString(), readStructureString(), 508 readStructureString(), readContentString()); 509 break; 510 case STATE_ATTRIBUTE_U_LN: 511 writer.writeAttribute(readStructureString(), readStructureString(), readContentString()); 512 break; 513 case STATE_ATTRIBUTE_LN: 514 writer.writeAttribute(readStructureString(), readContentString()); 515 break; 516 } 517 // Ignore the attribute type 518 readStructureString(); 519 520 readStructure(); 521 522 item = peekStructure(); 523 } while((item & TYPE_MASK) == T_ATTRIBUTE); 524 } 525 } 526