1 /*
2  * Copyright (c) 2005, 2016, 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 javax.xml.transform.stax;
27 
28 import javax.xml.stream.XMLEventReader;
29 import javax.xml.stream.XMLStreamConstants;
30 import javax.xml.stream.XMLStreamException;
31 import javax.xml.stream.XMLStreamReader;
32 import javax.xml.stream.events.XMLEvent;
33 import javax.xml.transform.Source;
34 
35 /**
36  * <p>Acts as a holder for an XML {@link Source} in the
37  * form of a StAX reader,i.e.
38  * {@link XMLStreamReader} or {@link XMLEventReader}.
39  * <code>StAXSource</code> can be used in all cases that accept
40  * a <code>Source</code>, e.g. {@link javax.xml.transform.Transformer},
41  * {@link javax.xml.validation.Validator} which accept
42  * <code>Source</code> as input.
43  *
44  * <p><code>StAXSource</code>s are consumed during processing
45  * and are not reusable.</p>
46  *
47  * @author Neeraj Bajaj
48  * @author Jeff Suttor
49  *
50  * @see <a href="http://jcp.org/en/jsr/detail?id=173">
51  *  JSR 173: Streaming API for XML</a>
52  * @see XMLStreamReader
53  * @see XMLEventReader
54  *
55  * @since 1.6
56  */
57 public class StAXSource implements Source {
58 
59     /** If {@link javax.xml.transform.TransformerFactory#getFeature(String name)}
60      * returns true when passed this value as an argument,
61      * the Transformer supports Source input of this type.
62      */
63     public static final String FEATURE =
64         "http://javax.xml.transform.stax.StAXSource/feature";
65 
66     /** <p><code>XMLEventReader</code> to be used for source input.</p> */
67     private XMLEventReader xmlEventReader = null;
68 
69     /** <p><code>XMLStreamReader</code> to be used for source input.</p> */
70     private XMLStreamReader xmlStreamReader = null;
71 
72     /** <p>System identifier of source input.</p> */
73     private String systemId = null;
74 
75     /**
76      * <p>Creates a new instance of a <code>StAXSource</code>
77      * by supplying an {@link XMLEventReader}.</p>
78      *
79      * <p><code>XMLEventReader</code> must be a
80      * non-<code>null</code> reference.</p>
81      *
82      * <p><code>XMLEventReader</code> must be in
83      * {@link XMLStreamConstants#START_DOCUMENT} or
84      * {@link XMLStreamConstants#START_ELEMENT} state.</p>
85      *
86      * @param xmlEventReader <code>XMLEventReader</code> used to create
87      *   this <code>StAXSource</code>.
88      *
89      * @throws XMLStreamException If <code>xmlEventReader</code> access
90      *   throws an <code>Exception</code>.
91      * @throws IllegalArgumentException If <code>xmlEventReader</code> ==
92      *   <code>null</code>.
93      * @throws IllegalStateException If <code>xmlEventReader</code>
94      *   is not in <code>XMLStreamConstants.START_DOCUMENT</code> or
95      *   <code>XMLStreamConstants.START_ELEMENT</code> state.
96      */
StAXSource(final XMLEventReader xmlEventReader)97     public StAXSource(final XMLEventReader xmlEventReader)
98         throws XMLStreamException {
99 
100         if (xmlEventReader == null) {
101             throw new IllegalArgumentException(
102                     "StAXSource(XMLEventReader) with XMLEventReader == null");
103         }
104 
105         // TODO: This is ugly ...
106         // there is no way to know the current position(event) of
107         // XMLEventReader.  peek() is the only way to know the next event.
108         // The next event on the input stream should be
109         // XMLStreamConstants.START_DOCUMENT or
110         // XMLStreamConstants.START_ELEMENT.
111         XMLEvent event = xmlEventReader.peek();
112         int eventType = event.getEventType();
113         if (eventType != XMLStreamConstants.START_DOCUMENT
114                 && eventType != XMLStreamConstants.START_ELEMENT) {
115             throw new IllegalStateException(
116                 "StAXSource(XMLEventReader) with XMLEventReader "
117                 + "not in XMLStreamConstants.START_DOCUMENT or "
118                 + "XMLStreamConstants.START_ELEMENT state");
119         }
120 
121         this.xmlEventReader = xmlEventReader;
122         systemId = event.getLocation().getSystemId();
123     }
124 
125     /**
126      * <p>Creates a new instance of a <code>StAXSource</code>
127      * by supplying an {@link XMLStreamReader}.</p>
128      *
129      * <p><code>XMLStreamReader</code> must be a
130      * non-<code>null</code> reference.</p>
131      *
132      * <p><code>XMLStreamReader</code> must be in
133      * {@link XMLStreamConstants#START_DOCUMENT} or
134      * {@link XMLStreamConstants#START_ELEMENT} state.</p>
135      *
136      * @param xmlStreamReader <code>XMLStreamReader</code> used to create
137      *   this <code>StAXSource</code>.
138      *
139      * @throws IllegalArgumentException If <code>xmlStreamReader</code> ==
140      *   <code>null</code>.
141      * @throws IllegalStateException If <code>xmlStreamReader</code>
142      *   is not in <code>XMLStreamConstants.START_DOCUMENT</code> or
143      *   <code>XMLStreamConstants.START_ELEMENT</code> state.
144      */
StAXSource(final XMLStreamReader xmlStreamReader)145     public StAXSource(final XMLStreamReader xmlStreamReader) {
146 
147         if (xmlStreamReader == null) {
148             throw new IllegalArgumentException(
149                     "StAXSource(XMLStreamReader) with XMLStreamReader == null");
150         }
151 
152         int eventType = xmlStreamReader.getEventType();
153         if (eventType != XMLStreamConstants.START_DOCUMENT
154                 && eventType != XMLStreamConstants.START_ELEMENT) {
155             throw new IllegalStateException(
156                     "StAXSource(XMLStreamReader) with XMLStreamReader"
157                     + "not in XMLStreamConstants.START_DOCUMENT or "
158                     + "XMLStreamConstants.START_ELEMENT state");
159         }
160 
161         this.xmlStreamReader = xmlStreamReader;
162         systemId = xmlStreamReader.getLocation().getSystemId();
163     }
164 
165     /**
166      * <p>Get the <code>XMLEventReader</code> used by this
167      * <code>StAXSource</code>.</p>
168      *
169      * <p><code>XMLEventReader</code> will be <code>null</code>.
170      * if this <code>StAXSource</code> was created with a
171      * <code>XMLStreamReader</code>.</p>
172      *
173      * @return <code>XMLEventReader</code> used by this
174      *   <code>StAXSource</code>.
175      */
getXMLEventReader()176     public XMLEventReader getXMLEventReader() {
177 
178         return xmlEventReader;
179     }
180 
181     /**
182      * <p>Get the <code>XMLStreamReader</code> used by this
183      * <code>StAXSource</code>.</p>
184      *
185      * <p><code>XMLStreamReader</code> will be <code>null</code>
186      * if this <code>StAXSource</code> was created with a
187      * <code>XMLEventReader</code>.</p>
188      *
189      * @return <code>XMLStreamReader</code> used by this
190      *   <code>StAXSource</code>.
191      */
getXMLStreamReader()192     public XMLStreamReader getXMLStreamReader() {
193 
194         return xmlStreamReader;
195     }
196 
197     /**
198      * <p>In the context of a <code>StAXSource</code>, it is not appropriate
199      * to explicitly set the system identifier.
200      * The <code>XMLStreamReader</code> or <code>XMLEventReader</code>
201      * used to construct this <code>StAXSource</code> determines the
202      * system identifier of the XML source.</p>
203      *
204      * <p>An {@link UnsupportedOperationException} is <strong>always</strong>
205      * thrown by this method.</p>
206      *
207      * @param systemId Ignored.
208      *
209      * @throws UnsupportedOperationException Is <strong>always</strong>
210      *   thrown by this method.
211      */
212     @Override
setSystemId(final String systemId)213     public void setSystemId(final String systemId) {
214 
215         throw new UnsupportedOperationException(
216                 "StAXSource#setSystemId(systemId) cannot set the "
217                 + "system identifier for a StAXSource");
218     }
219 
220     /**
221      * <p>Get the system identifier used by this
222      * <code>StAXSource</code>.</p>
223      *
224      * <p>The <code>XMLStreamReader</code> or <code>XMLEventReader</code>
225      * used to construct this <code>StAXSource</code> is queried to determine
226      * the system identifier of the XML source.</p>
227      *
228      * <p>The system identifier may be <code>null</code> or
229      * an empty <code>""</code> <code>String</code>.</p>
230      *
231      * @return System identifier used by this <code>StAXSource</code>.
232      */
233     @Override
getSystemId()234     public String getSystemId() {
235 
236         return systemId;
237     }
238 
239     /**
240      * Indicates whether the {@code StAXSource} object is empty. Since a
241      * {@code StAXSource} object can never be empty, this method always returns
242      * false.
243      *
244      * @return unconditionally false
245      */
246     @Override
isEmpty()247     public boolean isEmpty() {
248         return false;
249     }
250 }
251