src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/output/UTF8XmlOutput.java

Sun, 18 Jun 2017 23:18:45 +0100

author
aefimov
date
Sun, 18 Jun 2017 23:18:45 +0100
changeset 1491
d5c5a205d7fb
parent 0
373ffda63c9a
permissions
-rw-r--r--

8172297: In java 8, the marshalling with JAX-WS does not escape carriage return
Reviewed-by: lancea

     1 /*
     2  * Copyright (c) 1997, 2011, 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  */
    26 package com.sun.xml.internal.bind.v2.runtime.output;
    28 import java.io.IOException;
    29 import java.io.OutputStream;
    31 import java.io.StringWriter;
    32 import javax.xml.stream.XMLStreamException;
    34 import com.sun.xml.internal.bind.DatatypeConverterImpl;
    35 import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler;
    36 import com.sun.xml.internal.bind.v2.runtime.Name;
    37 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
    38 import com.sun.xml.internal.bind.v2.runtime.MarshallerImpl;
    40 import org.xml.sax.SAXException;
    42 /**
    43  * {@link XmlOutput} implementation specialized for UTF-8.
    44  *
    45  * @author Kohsuke Kawaguchi
    46  * @author Paul Sandoz
    47  */
    48 public class UTF8XmlOutput extends XmlOutputAbstractImpl {
    49     protected final OutputStream out;
    51     /** prefixes encoded. */
    52     private Encoded[] prefixes = new Encoded[8];
    54     /**
    55      * Of the {@link #prefixes}, number of filled entries.
    56      * This is almost the same as {@link NamespaceContextImpl#count()},
    57      * except that it allows us to handle contextual in-scope namespace bindings correctly.
    58      */
    59     private int prefixCount;
    61     /** local names encoded in UTF-8. All entries are pre-filled. */
    62     private final Encoded[] localNames;
    64     /** Temporary buffer used to encode text. */
    65     /*
    66      * TODO
    67      * The textBuffer could write directly to the _octetBuffer
    68      * when encoding a string if Encoder is modified.
    69      * This will avoid an additional memory copy.
    70      */
    71     private final Encoded textBuffer = new Encoded();
    73     /** Buffer of octets for writing. */
    74     // TODO: Obtain buffer size from property on the JAXB context
    75     protected final byte[] octetBuffer = new byte[1024];
    77     /** Index in buffer to write to. */
    78     protected int octetBufferIndex;
    80     /**
    81      * Set to true to indicate that we need to write '>'
    82      * to close a start tag. Deferring the write of this char
    83      * allows us to write "/>" for empty elements.
    84      */
    85     protected boolean closeStartTagPending = false;
    87     /**
    88      * @see MarshallerImpl#header
    89      */
    90     private String header;
    92     private CharacterEscapeHandler escapeHandler = null;
    94     /**
    95      *
    96      * @param localNames
    97      *      local names encoded in UTF-8.
    98      */
    99     public UTF8XmlOutput(OutputStream out, Encoded[] localNames, CharacterEscapeHandler escapeHandler) {
   100         this.out = out;
   101         this.localNames = localNames;
   102         for( int i=0; i<prefixes.length; i++ )
   103             prefixes[i] = new Encoded();
   104         this.escapeHandler = escapeHandler;
   105     }
   107     public void setHeader(String header) {
   108         this.header = header;
   109     }
   111     @Override
   112     public void startDocument(XMLSerializer serializer, boolean fragment, int[] nsUriIndex2prefixIndex, NamespaceContextImpl nsContext) throws IOException, SAXException, XMLStreamException {
   113         super.startDocument(serializer, fragment,nsUriIndex2prefixIndex,nsContext);
   115         octetBufferIndex = 0;
   116         if(!fragment) {
   117             write(XML_DECL);
   118         }
   119         if(header!=null) {
   120             textBuffer.set(header);
   121             textBuffer.write(this);
   122         }
   123     }
   125     @Override
   126     public void endDocument(boolean fragment) throws IOException, SAXException, XMLStreamException {
   127         flushBuffer();
   128         super.endDocument(fragment);
   129     }
   131     /**
   132      * Writes '>' to close the start tag, if necessary.
   133      */
   134     protected final void closeStartTag() throws IOException {
   135         if(closeStartTagPending) {
   136             write('>');
   137             closeStartTagPending = false;
   138         }
   139     }
   141     public void beginStartTag(int prefix, String localName) throws IOException {
   142         closeStartTag();
   143         int base= pushNsDecls();
   144         write('<');
   145         writeName(prefix,localName);
   146         writeNsDecls(base);
   147     }
   149     @Override
   150     public void beginStartTag(Name name) throws IOException {
   151         closeStartTag();
   152         int base = pushNsDecls();
   153         write('<');
   154         writeName(name);
   155         writeNsDecls(base);
   156     }
   158     private int pushNsDecls() {
   159         int total = nsContext.count();
   160         NamespaceContextImpl.Element ns = nsContext.getCurrent();
   162         if(total > prefixes.length) {
   163             // reallocate
   164             int m = Math.max(total,prefixes.length*2);
   165             Encoded[] buf = new Encoded[m];
   166             System.arraycopy(prefixes,0,buf,0,prefixes.length);
   167             for( int i=prefixes.length; i<buf.length; i++ )
   168                 buf[i] = new Encoded();
   169             prefixes = buf;
   170         }
   172         int base = Math.min(prefixCount,ns.getBase());
   173         int size = nsContext.count();
   174         for( int i=base; i<size; i++ ) {
   175             String p = nsContext.getPrefix(i);
   177             Encoded e = prefixes[i];
   179             if(p.length()==0) {
   180                 e.buf = EMPTY_BYTE_ARRAY;
   181                 e.len = 0;
   182             } else {
   183                 e.set(p);
   184                 e.append(':');
   185             }
   186         }
   187         prefixCount = size;
   188         return base;
   189     }
   191     protected void writeNsDecls(int base) throws IOException {
   192         NamespaceContextImpl.Element ns = nsContext.getCurrent();
   193         int size = nsContext.count();
   195         for( int i=ns.getBase(); i<size; i++ )
   196             writeNsDecl(i);
   197     }
   199     /**
   200      * Writes a single namespace declaration for the specified prefix.
   201      */
   202     protected final void writeNsDecl(int prefixIndex) throws IOException {
   203         String p = nsContext.getPrefix(prefixIndex);
   205         if(p.length()==0) {
   206             if(nsContext.getCurrent().isRootElement()
   207             && nsContext.getNamespaceURI(prefixIndex).length()==0)
   208                 return;     // no point in declaring xmlns="" on the root element
   209             write(XMLNS_EQUALS);
   210         } else {
   211             Encoded e = prefixes[prefixIndex];
   212             write(XMLNS_COLON);
   213             write(e.buf,0,e.len-1); // skip the trailing ':'
   214             write(EQUALS);
   215         }
   216         doText(nsContext.getNamespaceURI(prefixIndex),true);
   217         write('\"');
   218     }
   220     private void writePrefix(int prefix) throws IOException {
   221         prefixes[prefix].write(this);
   222     }
   224     private void writeName(Name name) throws IOException {
   225         writePrefix(nsUriIndex2prefixIndex[name.nsUriIndex]);
   226         localNames[name.localNameIndex].write(this);
   227     }
   229     private void writeName(int prefix, String localName) throws IOException {
   230         writePrefix(prefix);
   231         textBuffer.set(localName);
   232         textBuffer.write(this);
   233     }
   235     @Override
   236     public void attribute(Name name, String value) throws IOException {
   237         write(' ');
   238         if(name.nsUriIndex==-1) {
   239             localNames[name.localNameIndex].write(this);
   240         } else
   241             writeName(name);
   242         write(EQUALS);
   243         doText(value,true);
   244         write('\"');
   245     }
   247     public void attribute(int prefix, String localName, String value) throws IOException {
   248         write(' ');
   249         if(prefix==-1) {
   250             textBuffer.set(localName);
   251             textBuffer.write(this);
   252         } else
   253             writeName(prefix,localName);
   254         write(EQUALS);
   255         doText(value,true);
   256         write('\"');
   257     }
   259     public void endStartTag() throws IOException {
   260         closeStartTagPending = true;
   261     }
   263     @Override
   264     public void endTag(Name name) throws IOException {
   265         if(closeStartTagPending) {
   266             write(EMPTY_TAG);
   267             closeStartTagPending = false;
   268         } else {
   269             write(CLOSE_TAG);
   270             writeName(name);
   271             write('>');
   272         }
   273     }
   275     public void endTag(int prefix, String localName) throws IOException {
   276         if(closeStartTagPending) {
   277             write(EMPTY_TAG);
   278             closeStartTagPending = false;
   279         } else {
   280             write(CLOSE_TAG);
   281             writeName(prefix,localName);
   282             write('>');
   283         }
   284     }
   286     public void text(String value, boolean needSP) throws IOException {
   287         closeStartTag();
   288         if(needSP)
   289             write(' ');
   290         doText(value,false);
   291     }
   293     public void text(Pcdata value, boolean needSP) throws IOException {
   294         closeStartTag();
   295         if(needSP)
   296             write(' ');
   297         value.writeTo(this);
   298     }
   300     private void doText(String value,boolean isAttribute) throws IOException {
   301         if (escapeHandler != null) {
   302             StringWriter sw = new StringWriter();
   303             escapeHandler.escape(value.toCharArray(), 0, value.length(), isAttribute, sw);
   304             textBuffer.set(sw.toString());
   305         } else {
   306             textBuffer.setEscape(value, isAttribute);
   307         }
   308         textBuffer.write(this);
   309     }
   311     public final void text(int value) throws IOException {
   312         closeStartTag();
   313         /*
   314          * TODO
   315          * Change to use the octet buffer directly
   316          */
   318         // max is -2147483648 and 11 digits
   319         boolean minus = (value<0);
   320         textBuffer.ensureSize(11);
   321         byte[] buf = textBuffer.buf;
   322         int idx = 11;
   324         do {
   325             int r = value%10;
   326             if(r<0) r = -r;
   327             buf[--idx] = (byte)('0'|r);    // really measn 0x30+r but 0<=r<10, so bit-OR would do.
   328             value /= 10;
   329         } while(value!=0);
   331         if(minus)   buf[--idx] = (byte)'-';
   333         write(buf,idx,11-idx);
   334     }
   336     /**
   337      * Writes the given byte[] as base64 encoded binary to the output.
   338      *
   339      * <p>
   340      * Being defined on this class allows this method to access the buffer directly,
   341      * which translates to a better performance.
   342      */
   343     public void text(byte[] data, int dataLen) throws IOException {
   344         closeStartTag();
   346         int start = 0;
   348         while(dataLen>0) {
   349             // how many bytes (in data) can we write without overflowing the buffer?
   350             int batchSize = Math.min(((octetBuffer.length-octetBufferIndex)/4)*3,dataLen);
   352             // write the batch
   353             octetBufferIndex = DatatypeConverterImpl._printBase64Binary(data,start,batchSize,octetBuffer,octetBufferIndex);
   355             if(batchSize<dataLen)
   356                 flushBuffer();
   358             start += batchSize;
   359             dataLen -= batchSize;
   361         }
   362     }
   364 //
   365 //
   366 // series of the write method that places bytes to the output
   367 // (by doing some buffering internal to this class)
   368 //
   370     /**
   371      * Writes one byte directly into the buffer.
   372      *
   373      * <p>
   374      * This method can be used somewhat like the {@code text} method,
   375      * but it doesn't perform character escaping.
   376      */
   377     public final void write(int i) throws IOException {
   378         if (octetBufferIndex < octetBuffer.length) {
   379             octetBuffer[octetBufferIndex++] = (byte)i;
   380         } else {
   381             out.write(octetBuffer);
   382             octetBufferIndex = 1;
   383             octetBuffer[0] = (byte)i;
   384         }
   385     }
   387     protected final void write(byte[] b) throws IOException {
   388         write(b, 0,  b.length);
   389     }
   391     protected final void write(byte[] b, int start, int length) throws IOException {
   392         if ((octetBufferIndex + length) < octetBuffer.length) {
   393             System.arraycopy(b, start, octetBuffer, octetBufferIndex, length);
   394             octetBufferIndex += length;
   395         } else {
   396             out.write(octetBuffer, 0, octetBufferIndex);
   397             out.write(b, start, length);
   398             octetBufferIndex = 0;
   399         }
   400     }
   402     protected final void flushBuffer() throws IOException {
   403         out.write(octetBuffer, 0, octetBufferIndex);
   404         octetBufferIndex = 0;
   405     }
   407     static byte[] toBytes(String s) {
   408         byte[] buf = new byte[s.length()];
   409         for( int i=s.length()-1; i>=0; i-- )
   410             buf[i] = (byte)s.charAt(i);
   411         return buf;
   412     }
   414     // per instance copy to prevent an attack where malicious OutputStream
   415     // rewrites the byte array.
   416     private final byte[] XMLNS_EQUALS = _XMLNS_EQUALS.clone();
   417     private final byte[] XMLNS_COLON = _XMLNS_COLON.clone();
   418     private final byte[] EQUALS = _EQUALS.clone();
   419     private final byte[] CLOSE_TAG = _CLOSE_TAG.clone();
   420     private final byte[] EMPTY_TAG = _EMPTY_TAG.clone();
   421     private final byte[] XML_DECL = _XML_DECL.clone();
   423     // masters
   424     private static final byte[] _XMLNS_EQUALS = toBytes(" xmlns=\"");
   425     private static final byte[] _XMLNS_COLON = toBytes(" xmlns:");
   426     private static final byte[] _EQUALS = toBytes("=\"");
   427     private static final byte[] _CLOSE_TAG = toBytes("</");
   428     private static final byte[] _EMPTY_TAG = toBytes("/>");
   429     private static final byte[] _XML_DECL = toBytes("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
   431     // no need to copy
   432     private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
   433 }

mercurial