1 /*
   2  * Copyright (c) 1997, 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.ws.util.xml;
  27 
  28 import java.io.IOException;
  29 
  30 import javax.xml.bind.attachment.AttachmentMarshaller;
  31 import javax.xml.stream.XMLStreamConstants;
  32 import javax.xml.stream.XMLStreamException;
  33 import javax.xml.stream.XMLStreamReader;
  34 import javax.xml.stream.XMLStreamWriter;
  35 import javax.xml.XMLConstants;
  36 
  37 import com.sun.xml.internal.ws.streaming.MtomStreamWriter;
  38 import com.sun.xml.internal.org.jvnet.staxex.Base64Data;
  39 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamReaderEx;
  40 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamWriterEx;
  41 
  42 /**
  43  * Reads a sub-tree from {@link XMLStreamReader} and writes to {@link XMLStreamWriter}
  44  * as-is.
  45  *
  46  * <p>
  47  * This class can be sub-classed to implement a simple transformation logic.
  48  *
  49  * @author Kohsuke Kawaguchi
  50  * @author Ryan Shoemaker
  51  */
  52 public class XMLStreamReaderToXMLStreamWriter {
  53 
  54     private static final int BUF_SIZE = 4096;
  55 
  56     protected XMLStreamReader in;
  57     protected XMLStreamWriter out;
  58 
  59     private char[] buf;
  60 
  61     boolean optimizeBase64Data = false;
  62 
  63     AttachmentMarshaller mtomAttachmentMarshaller;
  64 
  65     /**
  66      * Reads one subtree and writes it out.
  67      *
  68      * <p>
  69      * The {@link XMLStreamWriter} never receives a start/end document event.
  70      * Those need to be written separately by the caller.
  71      */
  72     public void bridge(XMLStreamReader in, XMLStreamWriter out) throws XMLStreamException {
  73         assert in!=null && out!=null;
  74         this.in = in;
  75         this.out = out;
  76 
  77         optimizeBase64Data = (in instanceof XMLStreamReaderEx);
  78 
  79         if (out instanceof XMLStreamWriterEx && out instanceof MtomStreamWriter) {
  80             mtomAttachmentMarshaller = ((MtomStreamWriter) out).getAttachmentMarshaller();
  81         }
  82         // remembers the nest level of elements to know when we are done.
  83         int depth=0;
  84 
  85         buf = new char[BUF_SIZE];
  86 
  87         // if the parser is at the start tag, proceed to the first element
  88         int event = in.getEventType();
  89         if(event == XMLStreamConstants.START_DOCUMENT) {
  90             // nextTag doesn't correctly handle DTDs
  91             while( !in.isStartElement() ) {
  92                 event = in.next();
  93                 if (event == XMLStreamConstants.COMMENT)
  94                     handleComment();
  95             }
  96         }
  97 
  98 
  99         if( event!=XMLStreamConstants.START_ELEMENT)
 100             throw new IllegalStateException("The current event is not START_ELEMENT\n but " + event);
 101 
 102         do {
 103             // These are all of the events listed in the javadoc for
 104             // XMLEvent.
 105             // The spec only really describes 11 of them.
 106             switch (event) {
 107                 case XMLStreamConstants.START_ELEMENT :
 108                     depth++;
 109                     handleStartElement();
 110                     break;
 111                 case XMLStreamConstants.END_ELEMENT :
 112                     handleEndElement();
 113                     depth--;
 114                     if(depth==0)
 115                         return;
 116                     break;
 117                 case XMLStreamConstants.CHARACTERS :
 118                     handleCharacters();
 119                     break;
 120                 case XMLStreamConstants.ENTITY_REFERENCE :
 121                     handleEntityReference();
 122                     break;
 123                 case XMLStreamConstants.PROCESSING_INSTRUCTION :
 124                     handlePI();
 125                     break;
 126                 case XMLStreamConstants.COMMENT :
 127                     handleComment();
 128                     break;
 129                 case XMLStreamConstants.DTD :
 130                     handleDTD();
 131                     break;
 132                 case XMLStreamConstants.CDATA :
 133                     handleCDATA();
 134                     break;
 135                 case XMLStreamConstants.SPACE :
 136                     handleSpace();
 137                     break;
 138                 case XMLStreamConstants.END_DOCUMENT:
 139                     throw new XMLStreamException("Malformed XML at depth="+depth+", Reached EOF. Event="+event);
 140                 default :
 141                     throw new XMLStreamException("Cannot process event: " + event);
 142             }
 143 
 144             event=in.next();
 145         } while (depth!=0);
 146     }
 147 
 148     protected void handlePI() throws XMLStreamException {
 149         out.writeProcessingInstruction(
 150             in.getPITarget(),
 151             in.getPIData());
 152     }
 153 
 154 
 155     protected void handleCharacters() throws XMLStreamException {
 156 
 157         CharSequence c = null;
 158 
 159         if (optimizeBase64Data) {
 160             c = ((XMLStreamReaderEx)in).getPCDATA();
 161         }
 162 
 163         if ((c != null) && (c instanceof Base64Data)) {
 164             if (mtomAttachmentMarshaller != null) {
 165                 Base64Data b64d = (Base64Data) c;
 166                 ((XMLStreamWriterEx)out).writeBinary(b64d.getDataHandler());
 167             } else {
 168                 try {
 169                     ((Base64Data)c).writeTo(out);
 170                 } catch (IOException e) {
 171                     throw new XMLStreamException(e);
 172                 }
 173             }
 174         } else {
 175             for (int start=0,read=buf.length; read == buf.length; start+=buf.length) {
 176                 read = in.getTextCharacters(start, buf, 0, buf.length);
 177                 out.writeCharacters(buf, 0, read);
 178             }
 179         }
 180     }
 181 
 182     protected void handleEndElement() throws XMLStreamException {
 183         out.writeEndElement();
 184     }
 185 
 186     protected void handleStartElement() throws XMLStreamException {
 187         String nsUri = in.getNamespaceURI();
 188         if(nsUri==null)
 189             out.writeStartElement(in.getLocalName());
 190         else
 191             out.writeStartElement(
 192                 fixNull(in.getPrefix()),
 193                 in.getLocalName(),
 194                 nsUri
 195             );
 196 
 197         // start namespace bindings
 198         int nsCount = in.getNamespaceCount();
 199         for (int i = 0; i < nsCount; i++) {
 200             out.writeNamespace(
 201                 in.getNamespacePrefix(i),
 202                 fixNull(in.getNamespaceURI(i)));    // zephyr doesn't like null, I don't know what is correct, so just fix null to "" for now
 203         }
 204 
 205         // write attributes
 206         int attCount = in.getAttributeCount();
 207         for (int i = 0; i < attCount; i++) {
 208             handleAttribute(i);
 209         }
 210     }
 211 
 212     /**
 213      * Writes out the {@code i}-th attribute of the current element.
 214      *
 215      * <p>
 216      * Used from {@link #handleStartElement()}.
 217      */
 218     protected void handleAttribute(int i) throws XMLStreamException {
 219         String nsUri = in.getAttributeNamespace(i);
 220         String prefix = in.getAttributePrefix(i);
 221          if (fixNull(nsUri).equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
 222              //Its a namespace decl, ignore as it is already written.
 223              return;
 224          }
 225 
 226         if(nsUri==null || prefix == null || prefix.equals("")) {
 227             out.writeAttribute(
 228                 in.getAttributeLocalName(i),
 229                 in.getAttributeValue(i)
 230             );
 231         } else {
 232             out.writeAttribute(
 233                 prefix,
 234                 nsUri,
 235                 in.getAttributeLocalName(i),
 236                 in.getAttributeValue(i)
 237             );
 238         }
 239     }
 240 
 241     protected void handleDTD() throws XMLStreamException {
 242         out.writeDTD(in.getText());
 243     }
 244 
 245     protected void handleComment() throws XMLStreamException {
 246         out.writeComment(in.getText());
 247     }
 248 
 249     protected void handleEntityReference() throws XMLStreamException {
 250         out.writeEntityRef(in.getText());
 251     }
 252 
 253     protected void handleSpace() throws XMLStreamException {
 254         handleCharacters();
 255     }
 256 
 257     protected void handleCDATA() throws XMLStreamException {
 258         out.writeCData(in.getText());
 259     }
 260 
 261     private static String fixNull(String s) {
 262         if(s==null)     return "";
 263         else            return s;
 264     }
 265 }