1 /*
   2  * Copyright (c) 2005, 2010, 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                         storeContentObject(((Base64Data)c).clone());
 219                     } else {
 220                         storeContentCharacters(T_TEXT_AS_CHAR_ARRAY,
 221                                 reader.getTextCharacters(), reader.getTextStart(),
 222                                 reader.getTextLength());
 223                     }
 224                     break;
 225                 }
 226                 case XMLStreamReader.COMMENT:
 227                     storeComment(reader);
 228                     break;
 229                 case XMLStreamReader.PROCESSING_INSTRUCTION:
 230                     storeProcessingInstruction(reader);
 231                     break;
 232             }
 233         }
 234 
 235         /*
 236          * Move to next item after the end of the element
 237          * that has been stored
 238          */
 239         _eventType = reader.next();
 240     }
 241 
 242     private void storeElementAndChildrenNoEx(XMLStreamReader reader) throws XMLStreamException {
 243         int depth = 1;
 244         if (_storeInScopeNamespacesOnElementFragment) {
 245             storeElementWithInScopeNamespaces(reader);
 246         } else {
 247             storeElement(reader);
 248         }
 249 
 250         while(depth > 0) {
 251             _eventType = reader.next();
 252             switch (_eventType) {
 253                 case XMLStreamReader.START_ELEMENT:
 254                     depth++;
 255                     storeElement(reader);
 256                     break;
 257                 case XMLStreamReader.END_ELEMENT:
 258                     depth--;
 259                     storeStructure(T_END);
 260                     break;
 261                 case XMLStreamReader.NAMESPACE:
 262                     storeNamespaceAttributes(reader);
 263                     break;
 264                 case XMLStreamReader.ATTRIBUTE:
 265                     storeAttributes(reader);
 266                     break;
 267                 case XMLStreamReader.SPACE:
 268                 case XMLStreamReader.CHARACTERS:
 269                 case XMLStreamReader.CDATA: {
 270                     storeContentCharacters(T_TEXT_AS_CHAR_ARRAY,
 271                             reader.getTextCharacters(), reader.getTextStart(),
 272                             reader.getTextLength());
 273                     break;
 274                 }
 275                 case XMLStreamReader.COMMENT:
 276                     storeComment(reader);
 277                     break;
 278                 case XMLStreamReader.PROCESSING_INSTRUCTION:
 279                     storeProcessingInstruction(reader);
 280                     break;
 281             }
 282         }
 283 
 284         /*
 285          * Move to next item after the end of the element
 286          * that has been stored
 287          */
 288         _eventType = reader.next();
 289     }
 290 
 291     private void storeElementWithInScopeNamespaces(XMLStreamReader reader) {
 292         storeQualifiedName(T_ELEMENT_LN,
 293                 reader.getPrefix(), reader.getNamespaceURI(), reader.getLocalName());
 294 
 295         if (reader.getNamespaceCount() > 0) {
 296             storeNamespaceAttributes(reader);
 297         }
 298 
 299         if (reader.getAttributeCount() > 0) {
 300             storeAttributes(reader);
 301         }
 302     }
 303 
 304     private void storeElement(XMLStreamReader reader) {
 305         storeQualifiedName(T_ELEMENT_LN,
 306                 reader.getPrefix(), reader.getNamespaceURI(), reader.getLocalName());
 307 
 308         if (reader.getNamespaceCount() > 0) {
 309             storeNamespaceAttributes(reader);
 310         }
 311 
 312         if (reader.getAttributeCount() > 0) {
 313             storeAttributes(reader);
 314         }
 315     }
 316 
 317     /**
 318      * A low level method a create a structure element explicitly. This is useful when xsb is
 319      * created from a fragment's XMLStreamReader and inscope namespaces can be passed using
 320      * this method. Note that there is no way to enumerate namespaces from XMLStreamReader.
 321      *
 322      * For e.g: Say the SOAP message is as follows
 323      *
 324      *  <S:Envelope xmlns:n1=".."><S:Body><ns2:A> ...
 325      *
 326      * when xsb is to be created using a reader that is at <ns2:A> tag, the inscope
 327      * namespace like 'n1' can be passed using this method.
 328      *
 329      * WARNING: Instead of using this, try other methods(if you don't know what you are
 330      * doing).
 331      *
 332      * @param ns an array of the even length of the form { prefix0, uri0, prefix1, uri1, ... }.
 333      */
 334     public void storeElement(String nsURI, String localName, String prefix, String[] ns) {
 335         storeQualifiedName(T_ELEMENT_LN, prefix, nsURI, localName);
 336         storeNamespaceAttributes(ns);
 337     }
 338 
 339     /**
 340      * A low level method a create a structure element explicitly. This is
 341      * required to support {@link #storeElement} method.
 342      *
 343      * WARNING: Instead of using this, try other methods(if you don't know what
 344      * you are doing).
 345      */
 346     public void storeEndElement() {
 347         storeStructure(T_END);
 348     }
 349 
 350     private void storeNamespaceAttributes(XMLStreamReader reader) {
 351         int count = reader.getNamespaceCount();
 352         for (int i = 0; i < count; i++) {
 353             storeNamespaceAttribute(reader.getNamespacePrefix(i), reader.getNamespaceURI(i));
 354         }
 355     }
 356 
 357     /**
 358      * @param ns an array of the even length of the form { prefix0, uri0, prefix1, uri1, ... }.
 359      */
 360     private void storeNamespaceAttributes(String[] ns) {
 361         for (int i = 0; i < ns.length; i=i+2) {
 362             storeNamespaceAttribute(ns[i], ns[i+1]);
 363         }
 364     }
 365 
 366     private void storeAttributes(XMLStreamReader reader) {
 367         int count = reader.getAttributeCount();
 368         for (int i = 0; i < count; i++) {
 369             storeAttribute(reader.getAttributePrefix(i), reader.getAttributeNamespace(i), reader.getAttributeLocalName(i),
 370                     reader.getAttributeType(i), reader.getAttributeValue(i));
 371         }
 372     }
 373 
 374     private void storeComment(XMLStreamReader reader) {
 375         storeContentCharacters(T_COMMENT_AS_CHAR_ARRAY,
 376                 reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
 377     }
 378 
 379     private void storeProcessingInstruction(XMLStreamReader reader) {
 380         storeProcessingInstruction(reader.getPITarget(), reader.getPIData());
 381     }
 382 }