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