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 
  54     public StreamWriterBufferProcessor() {
  55     }
  56 
  57     /**
  58      * @deprecated
  59      *      Use {@link #StreamWriterBufferProcessor(XMLStreamBuffer, boolean)}
  60      */
  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      */
  70     public StreamWriterBufferProcessor(XMLStreamBuffer buffer,boolean produceFragmentEvent) {
  71         setXMLStreamBuffer(buffer,produceFragmentEvent);
  72     }
  73 
  74     public final void process(XMLStreamBuffer buffer, XMLStreamWriter writer) throws XMLStreamException {
  75         setXMLStreamBuffer(buffer,buffer.isFragment());
  76         process(writer);
  77     }
  78 
  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      */
  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      */
 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      */
 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      */
 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 
 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 
 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 
 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      */
 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 
 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      */
 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 
 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 
 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 }