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      */
  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      */
 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      */
 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      */
 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
 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
 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
 247     public boolean isEmpty() {
 248         return false;
 249     }
 250 }