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

Fri, 07 Aug 2009 11:27:00 -0700

author
asaha
date
Fri, 07 Aug 2009 11:27:00 -0700
changeset 78
860b95cc8d1d
parent 46
a88ad84027a0
permissions
-rw-r--r--

6813167: 6u14 JAX-WS audit mutable static bugs
6803688: Integrate latest JAX-WS (2.1.6) in to JDK 6u14
Reviewed-by: darcy, ramap

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

mercurial