View Javadoc
1   /*
2    * Copyright (c) 2005, 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.xml.internal.stream.buffer.stax;
27  
28  import com.sun.xml.internal.stream.buffer.MutableXMLStreamBuffer;
29  import com.sun.xml.internal.org.jvnet.staxex.Base64Data;
30  import com.sun.xml.internal.org.jvnet.staxex.XMLStreamReaderEx;
31  
32  import javax.xml.stream.XMLStreamException;
33  import javax.xml.stream.XMLStreamReader;
34  import java.util.HashMap;
35  import java.util.Map;
36  
37  /**
38   * Create a buffer using an {@link XMLStreamReader}.
39   * <p>
40   * TODO: Implement the marking the stream on the element when an ID
41   * attribute on the element is defined
42   */
43  public class StreamReaderBufferCreator extends StreamBufferCreator {
44      private int _eventType;
45      private boolean _storeInScopeNamespacesOnElementFragment;
46      private Map<String, Integer> _inScopePrefixes;
47  
48      /**
49       * Create a stream reader buffer creator.
50       * <p>
51       * A stream buffer will be created for storing the infoset
52       * from a stream reader.
53       */
54      public StreamReaderBufferCreator() {
55      }
56  
57      /**
58       * Create a stream reader buffer creator using a mutable stream buffer.
59       * <p>
60       * @param buffer the mutable stream buffer.
61       */
62      public StreamReaderBufferCreator(MutableXMLStreamBuffer buffer) {
63          setBuffer(buffer);
64      }
65  
66      /**
67       * Create the buffer from a stream reader.
68       * <p>
69       * The stream reader must be positioned at the start of the document
70       * or the start of an element.
71       * <p>
72       * If the stream is positioned at the start of the document then the
73       * whole document is stored and after storing the stream will be positioned
74       * at the end of the document.
75       * <p>
76       * If the stream is positioned at the start of an element then the
77       * element and all its children will be stored and after storing the stream
78       * will be positioned at the next event after the end of the element.
79       * <p>
80       * @return the mutable stream buffer.
81       * @throws XMLStreamException if the stream reader is not positioned at
82       *         the start of the document or at an element.
83       */
84      public MutableXMLStreamBuffer create(XMLStreamReader reader) throws XMLStreamException {
85          if (_buffer == null) {
86              createBuffer();
87          }
88          store(reader);
89  
90          return getXMLStreamBuffer();
91      }
92  
93      /**
94       * Creates the buffer from a stream reader that is an element fragment.
95       * <p>
96       * The stream reader will be moved to the position of the next start of
97       * an element if the stream reader is not already positioned at the start
98       * of an element.
99       * <p>
100      * The element and all its children will be stored and after storing the stream
101      * will be positioned at the next event after the end of the element.
102      * <p>
103      * @param storeInScopeNamespaces true if in-scope namespaces of the element
104      *        fragment should be stored.
105      * @return the mutable stream buffer.
106      * @throws XMLStreamException if the stream reader cannot be positioned at
107      *         the start of an element.
108      */
109     public MutableXMLStreamBuffer createElementFragment(XMLStreamReader reader,
110             boolean storeInScopeNamespaces) throws XMLStreamException {
111         if (_buffer == null) {
112             createBuffer();
113         }
114 
115         if (!reader.hasNext()) {
116             return _buffer;
117         }
118 
119         _storeInScopeNamespacesOnElementFragment = storeInScopeNamespaces;
120 
121         _eventType = reader.getEventType();
122         if (_eventType != XMLStreamReader.START_ELEMENT) {
123             do {
124                 _eventType = reader.next();
125             } while(_eventType != XMLStreamReader.START_ELEMENT && _eventType != XMLStreamReader.END_DOCUMENT);
126         }
127 
128         if (storeInScopeNamespaces) {
129             _inScopePrefixes = new HashMap<String,Integer>();
130         }
131 
132         storeElementAndChildren(reader);
133 
134         return getXMLStreamBuffer();
135     }
136 
137     private void store(XMLStreamReader reader) throws XMLStreamException {
138         if (!reader.hasNext()) {
139             return;
140         }
141 
142         _eventType = reader.getEventType();
143         switch (_eventType) {
144             case XMLStreamReader.START_DOCUMENT:
145                 storeDocumentAndChildren(reader);
146                 break;
147             case XMLStreamReader.START_ELEMENT:
148                 storeElementAndChildren(reader);
149                 break;
150             default:
151                 throw new XMLStreamException("XMLStreamReader not positioned at a document or element");
152         }
153 
154         increaseTreeCount();
155     }
156 
157     private void storeDocumentAndChildren(XMLStreamReader reader) throws XMLStreamException {
158         storeStructure(T_DOCUMENT);
159 
160         _eventType = reader.next();
161         while (_eventType != XMLStreamReader.END_DOCUMENT) {
162             switch (_eventType) {
163                 case XMLStreamReader.START_ELEMENT:
164                     storeElementAndChildren(reader);
165                     continue;
166                 case XMLStreamReader.COMMENT:
167                     storeComment(reader);
168                     break;
169                 case XMLStreamReader.PROCESSING_INSTRUCTION:
170                     storeProcessingInstruction(reader);
171                     break;
172             }
173             _eventType = reader.next();
174         }
175 
176         storeStructure(T_END);
177     }
178 
179     private void storeElementAndChildren(XMLStreamReader reader) throws XMLStreamException {
180         if (reader instanceof XMLStreamReaderEx) {
181             storeElementAndChildrenEx((XMLStreamReaderEx)reader);
182         } else {
183             storeElementAndChildrenNoEx(reader);
184         }
185     }
186 
187     private void storeElementAndChildrenEx(XMLStreamReaderEx reader) throws XMLStreamException {
188         int depth = 1;
189         if (_storeInScopeNamespacesOnElementFragment) {
190             storeElementWithInScopeNamespaces(reader);
191         } else {
192             storeElement(reader);
193         }
194 
195         while(depth > 0) {
196             _eventType = reader.next();
197             switch (_eventType) {
198                 case XMLStreamReader.START_ELEMENT:
199                     depth++;
200                     storeElement(reader);
201                     break;
202                 case XMLStreamReader.END_ELEMENT:
203                     depth--;
204                     storeStructure(T_END);
205                     break;
206                 case XMLStreamReader.NAMESPACE:
207                     storeNamespaceAttributes(reader);
208                     break;
209                 case XMLStreamReader.ATTRIBUTE:
210                     storeAttributes(reader);
211                     break;
212                 case XMLStreamReader.SPACE:
213                 case XMLStreamReader.CHARACTERS:
214                 case XMLStreamReader.CDATA: {
215                     CharSequence c = reader.getPCDATA();
216                     if (c instanceof Base64Data) {
217                         storeStructure(T_TEXT_AS_OBJECT);
218                         //Instead of clone the Base64Data, the original Base64Data instance is used here to preserve the DataHandler
219                         storeContentObject(c);
220                     } else {
221                         storeContentCharacters(T_TEXT_AS_CHAR_ARRAY,
222                                 reader.getTextCharacters(), reader.getTextStart(),
223                                 reader.getTextLength());
224                     }
225                     break;
226                 }
227                 case XMLStreamReader.COMMENT:
228                     storeComment(reader);
229                     break;
230                 case XMLStreamReader.PROCESSING_INSTRUCTION:
231                     storeProcessingInstruction(reader);
232                     break;
233             }
234         }
235 
236         /*
237          * Move to next item after the end of the element
238          * that has been stored
239          */
240         _eventType = reader.next();
241     }
242 
243     private void storeElementAndChildrenNoEx(XMLStreamReader reader) throws XMLStreamException {
244         int depth = 1;
245         if (_storeInScopeNamespacesOnElementFragment) {
246             storeElementWithInScopeNamespaces(reader);
247         } else {
248             storeElement(reader);
249         }
250 
251         while(depth > 0) {
252             _eventType = reader.next();
253             switch (_eventType) {
254                 case XMLStreamReader.START_ELEMENT:
255                     depth++;
256                     storeElement(reader);
257                     break;
258                 case XMLStreamReader.END_ELEMENT:
259                     depth--;
260                     storeStructure(T_END);
261                     break;
262                 case XMLStreamReader.NAMESPACE:
263                     storeNamespaceAttributes(reader);
264                     break;
265                 case XMLStreamReader.ATTRIBUTE:
266                     storeAttributes(reader);
267                     break;
268                 case XMLStreamReader.SPACE:
269                 case XMLStreamReader.CHARACTERS:
270                 case XMLStreamReader.CDATA: {
271                     storeContentCharacters(T_TEXT_AS_CHAR_ARRAY,
272                             reader.getTextCharacters(), reader.getTextStart(),
273                             reader.getTextLength());
274                     break;
275                 }
276                 case XMLStreamReader.COMMENT:
277                     storeComment(reader);
278                     break;
279                 case XMLStreamReader.PROCESSING_INSTRUCTION:
280                     storeProcessingInstruction(reader);
281                     break;
282             }
283         }
284 
285         /*
286          * Move to next item after the end of the element
287          * that has been stored
288          */
289         _eventType = reader.next();
290     }
291 
292     private void storeElementWithInScopeNamespaces(XMLStreamReader reader) {
293         storeQualifiedName(T_ELEMENT_LN,
294                 reader.getPrefix(), reader.getNamespaceURI(), reader.getLocalName());
295 
296         if (reader.getNamespaceCount() > 0) {
297             storeNamespaceAttributes(reader);
298         }
299 
300         if (reader.getAttributeCount() > 0) {
301             storeAttributes(reader);
302         }
303     }
304 
305     private void storeElement(XMLStreamReader reader) {
306         storeQualifiedName(T_ELEMENT_LN,
307                 reader.getPrefix(), reader.getNamespaceURI(), reader.getLocalName());
308 
309         if (reader.getNamespaceCount() > 0) {
310             storeNamespaceAttributes(reader);
311         }
312 
313         if (reader.getAttributeCount() > 0) {
314             storeAttributes(reader);
315         }
316     }
317 
318     /**
319      * A low level method a create a structure element explicitly. This is useful when xsb is
320      * created from a fragment's XMLStreamReader and inscope namespaces can be passed using
321      * this method. Note that there is no way to enumerate namespaces from XMLStreamReader.
322      *
323      * For e.g: Say the SOAP message is as follows
324      *
325      *  <S:Envelope xmlns:n1=".."><S:Body><ns2:A> ...
326      *
327      * when xsb is to be created using a reader that is at <ns2:A> tag, the inscope
328      * namespace like 'n1' can be passed using this method.
329      *
330      * WARNING: Instead of using this, try other methods(if you don't know what you are
331      * doing).
332      *
333      * @param ns an array of the even length of the form { prefix0, uri0, prefix1, uri1, ... }.
334      */
335     public void storeElement(String nsURI, String localName, String prefix, String[] ns) {
336         storeQualifiedName(T_ELEMENT_LN, prefix, nsURI, localName);
337         storeNamespaceAttributes(ns);
338     }
339 
340     /**
341      * A low level method a create a structure element explicitly. This is
342      * required to support {@link #storeElement} method.
343      *
344      * WARNING: Instead of using this, try other methods(if you don't know what
345      * you are doing).
346      */
347     public void storeEndElement() {
348         storeStructure(T_END);
349     }
350 
351     private void storeNamespaceAttributes(XMLStreamReader reader) {
352         int count = reader.getNamespaceCount();
353         for (int i = 0; i < count; i++) {
354             storeNamespaceAttribute(reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
355         }
356     }
357 
358     /**
359      * @param ns an array of the even length of the form { prefix0, uri0, prefix1, uri1, ... }.
360      */
361     private void storeNamespaceAttributes(String[] ns) {
362         for (int i = 0; i < ns.length; i=i+2) {
363             storeNamespaceAttribute(ns[i], ns[i+1]);
364         }
365     }
366 
367     private void storeAttributes(XMLStreamReader reader) {
368         int count = reader.getAttributeCount();
369         for (int i = 0; i < count; i++) {
370             storeAttribute(reader.getAttributePrefix(i), reader.getAttributeNamespace(i), reader.getAttributeLocalName(i),
371                     reader.getAttributeType(i), reader.getAttributeValue(i));
372         }
373     }
374 
375     private void storeComment(XMLStreamReader reader) {
376         storeContentCharacters(T_COMMENT_AS_CHAR_ARRAY,
377                 reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
378     }
379 
380     private void storeProcessingInstruction(XMLStreamReader reader) {
381         storeProcessingInstruction(reader.getPITarget(), reader.getPIData());
382     }
383 }