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
26package com.sun.xml.internal.stream.buffer.stax;
27
28import com.sun.xml.internal.stream.buffer.MutableXMLStreamBuffer;
29import com.sun.xml.internal.org.jvnet.staxex.Base64Data;
30import com.sun.xml.internal.org.jvnet.staxex.XMLStreamReaderEx;
31
32import javax.xml.stream.XMLStreamException;
33import javax.xml.stream.XMLStreamReader;
34import java.util.HashMap;
35import 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 */
43public 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}
384