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

Mon, 20 Apr 2009 15:14:39 -0700

author
tbell
date
Mon, 20 Apr 2009 15:14:39 -0700
changeset 45
31822b475baa
parent 1
0961a4a21176
child 46
a88ad84027a0
permissions
-rw-r--r--

6831313: update jaxws in OpenJDK7 to 2.1 plus bug fixes from OpenJDK 6
6672868: Package javax.xml.ws.wsaddressing not included in make/docs/CORE_PKGS.gmk
Reviewed-by: darcy

     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  */
    25 package com.sun.xml.internal.bind.v2.runtime.output;
    27 import java.io.IOException;
    28 import java.io.OutputStream;
    30 import javax.xml.stream.XMLStreamException;
    32 import com.sun.xml.internal.bind.DatatypeConverterImpl;
    33 import com.sun.xml.internal.bind.v2.runtime.Name;
    34 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
    36 import org.xml.sax.SAXException;
    38 /**
    39  * {@link XmlOutput} implementation specialized for UTF-8.
    40  *
    41  * @author Kohsuke Kawaguchi
    42  * @author Paul Sandoz
    43  */
    44 public class UTF8XmlOutput extends XmlOutputAbstractImpl {
    45     protected final OutputStream out;
    47     /** prefixes encoded. */
    48     private Encoded[] prefixes = new Encoded[8];
    50     /**
    51      * Of the {@link #prefixes}, number of filled entries.
    52      * This is almost the same as {@link NamespaceContextImpl#count()},
    53      * except that it allows us to handle contextual in-scope namespace bindings correctly.
    54      */
    55     private int prefixCount;
    57     /** local names encoded in UTF-8. All entries are pre-filled. */
    58     private final Encoded[] localNames;
    60     /** Temporary buffer used to encode text. */
    61     /*
    62      * TODO
    63      * The textBuffer could write directly to the _octetBuffer
    64      * when encoding a string if Encoder is modified.
    65      * This will avoid an additional memory copy.
    66      */
    67     private final Encoded textBuffer = new Encoded();
    69     /** Buffer of octets for writing. */
    70     // TODO: Obtain buffer size from property on the JAXB context
    71     protected final byte[] octetBuffer = new byte[1024];
    73     /** Index in buffer to write to. */
    74     protected int octetBufferIndex;
    76     /**
    77      * Set to true to indicate that we need to write '>'
    78      * to close a start tag. Deferring the write of this char
    79      * allows us to write "/>" for empty elements.
    80      */
    81     protected boolean closeStartTagPending = false;
    83     /**
    84      *
    85      * @param localNames
    86      *      local names encoded in UTF-8.
    87      */
    88     public UTF8XmlOutput(OutputStream out, Encoded[] localNames) {
    89         this.out = out;
    90         this.localNames = localNames;
    91         for( int i=0; i<prefixes.length; i++ )
    92             prefixes[i] = new Encoded();
    93     }
    95     @Override
    96     public void startDocument(XMLSerializer serializer, boolean fragment, int[] nsUriIndex2prefixIndex, NamespaceContextImpl nsContext) throws IOException, SAXException, XMLStreamException {
    97         super.startDocument(serializer, fragment,nsUriIndex2prefixIndex,nsContext);
    99         octetBufferIndex = 0;
   100         if(!fragment) {
   101             write(XML_DECL);
   102         }
   103     }
   105     public void endDocument(boolean fragment) throws IOException, SAXException, XMLStreamException {
   106         flushBuffer();
   107         super.endDocument(fragment);
   108     }
   110     /**
   111      * Writes '>' to close the start tag, if necessary.
   112      */
   113     protected final void closeStartTag() throws IOException {
   114         if(closeStartTagPending) {
   115             write('>');
   116             closeStartTagPending = false;
   117         }
   118     }
   120     public void beginStartTag(int prefix, String localName) throws IOException {
   121         closeStartTag();
   122         int base= pushNsDecls();
   123         write('<');
   124         writeName(prefix,localName);
   125         writeNsDecls(base);
   126     }
   128     public void beginStartTag(Name name) throws IOException {
   129         closeStartTag();
   130         int base = pushNsDecls();
   131         write('<');
   132         writeName(name);
   133         writeNsDecls(base);
   134     }
   136     private int pushNsDecls() {
   137         int total = nsContext.count();
   138         NamespaceContextImpl.Element ns = nsContext.getCurrent();
   140         if(total > prefixes.length) {
   141             // reallocate
   142             int m = Math.max(total,prefixes.length*2);
   143             Encoded[] buf = new Encoded[m];
   144             System.arraycopy(prefixes,0,buf,0,prefixes.length);
   145             for( int i=prefixes.length; i<buf.length; i++ )
   146                 buf[i] = new Encoded();
   147             prefixes = buf;
   148         }
   150         int base = Math.min(prefixCount,ns.getBase());
   151         int size = nsContext.count();
   152         for( int i=base; i<size; i++ ) {
   153             String p = nsContext.getPrefix(i);
   155             Encoded e = prefixes[i];
   157             if(p.length()==0) {
   158                 e.buf = EMPTY_BYTE_ARRAY;
   159                 e.len = 0;
   160             } else {
   161                 e.set(p);
   162                 e.append(':');
   163             }
   164         }
   165         prefixCount = size;
   166         return base;
   167     }
   169     protected void writeNsDecls(int base) throws IOException {
   170         NamespaceContextImpl.Element ns = nsContext.getCurrent();
   171         int size = nsContext.count();
   173         for( int i=ns.getBase(); i<size; i++ )
   174             writeNsDecl(i);
   175     }
   177     /**
   178      * Writes a single namespace declaration for the specified prefix.
   179      */
   180     protected final void writeNsDecl(int prefixIndex) throws IOException {
   181         String p = nsContext.getPrefix(prefixIndex);
   183         if(p.length()==0) {
   184             if(nsContext.getCurrent().isRootElement()
   185             && nsContext.getNamespaceURI(prefixIndex).length()==0)
   186                 return;     // no point in declaring xmlns="" on the root element
   187             write(XMLNS_EQUALS);
   188         } else {
   189             Encoded e = prefixes[prefixIndex];
   190             write(XMLNS_COLON);
   191             write(e.buf,0,e.len-1); // skip the trailing ':'
   192             write(EQUALS);
   193         }
   194         doText(nsContext.getNamespaceURI(prefixIndex),true);
   195         write('\"');
   196     }
   198     private void writePrefix(int prefix) throws IOException {
   199         prefixes[prefix].write(this);
   200     }
   202     private void writeName(Name name) throws IOException {
   203         writePrefix(nsUriIndex2prefixIndex[name.nsUriIndex]);
   204         localNames[name.localNameIndex].write(this);
   205     }
   207     private void writeName(int prefix, String localName) throws IOException {
   208         writePrefix(prefix);
   209         textBuffer.set(localName);
   210         textBuffer.write(this);
   211     }
   213     @Override
   214     public void attribute(Name name, String value) throws IOException {
   215         write(' ');
   216         if(name.nsUriIndex==-1) {
   217             localNames[name.localNameIndex].write(this);
   218         } else
   219             writeName(name);
   220         write(EQUALS);
   221         doText(value,true);
   222         write('\"');
   223     }
   225     public void attribute(int prefix, String localName, String value) throws IOException {
   226         write(' ');
   227         if(prefix==-1) {
   228             textBuffer.set(localName);
   229             textBuffer.write(this);
   230         } else
   231             writeName(prefix,localName);
   232         write(EQUALS);
   233         doText(value,true);
   234         write('\"');
   235     }
   237     public void endStartTag() throws IOException {
   238         closeStartTagPending = true;
   239     }
   241     @Override
   242     public void endTag(Name name) throws IOException {
   243         if(closeStartTagPending) {
   244             write(EMPTY_TAG);
   245             closeStartTagPending = false;
   246         } else {
   247             write(CLOSE_TAG);
   248             writeName(name);
   249             write('>');
   250         }
   251     }
   253     public void endTag(int prefix, String localName) throws IOException {
   254         if(closeStartTagPending) {
   255             write(EMPTY_TAG);
   256             closeStartTagPending = false;
   257         } else {
   258             write(CLOSE_TAG);
   259             writeName(prefix,localName);
   260             write('>');
   261         }
   262     }
   264     public void text(String value, boolean needSP) throws IOException {
   265         closeStartTag();
   266         if(needSP)
   267             write(' ');
   268         doText(value,false);
   269     }
   271     public void text(Pcdata value, boolean needSP) throws IOException {
   272         closeStartTag();
   273         if(needSP)
   274             write(' ');
   275         value.writeTo(this);
   276     }
   278     private void doText(String value,boolean isAttribute) throws IOException {
   279         textBuffer.setEscape(value,isAttribute);
   280         textBuffer.write(this);
   281     }
   283     public final void text(int value) throws IOException {
   284         closeStartTag();
   285         /*
   286          * TODO
   287          * Change to use the octet buffer directly
   288          */
   290         // max is -2147483648 and 11 digits
   291         boolean minus = (value<0);
   292         textBuffer.ensureSize(11);
   293         byte[] buf = textBuffer.buf;
   294         int idx = 11;
   296         do {
   297             int r = value%10;
   298             if(r<0) r = -r;
   299             buf[--idx] = (byte)('0'|r);    // really measn 0x30+r but 0<=r<10, so bit-OR would do.
   300             value /= 10;
   301         } while(value!=0);
   303         if(minus)   buf[--idx] = (byte)'-';
   305         write(buf,idx,11-idx);
   306     }
   308     /**
   309      * Writes the given byte[] as base64 encoded binary to the output.
   310      *
   311      * <p>
   312      * Being defined on this class allows this method to access the buffer directly,
   313      * which translates to a better performance.
   314      */
   315     public void text(byte[] data, int dataLen) throws IOException {
   316         closeStartTag();
   318         int start = 0;
   320         while(dataLen>0) {
   321             // how many bytes (in data) can we write without overflowing the buffer?
   322             int batchSize = Math.min(((octetBuffer.length-octetBufferIndex)/4)*3,dataLen);
   324             // write the batch
   325             octetBufferIndex = DatatypeConverterImpl._printBase64Binary(data,start,batchSize,octetBuffer,octetBufferIndex);
   327             if(batchSize<dataLen)
   328                 flushBuffer();
   330             start += batchSize;
   331             dataLen -= batchSize;
   333         }
   334     }
   336 //
   337 //
   338 // series of the write method that places bytes to the output
   339 // (by doing some buffering internal to this class)
   340 //
   342     /**
   343      * Writes one byte directly into the buffer.
   344      *
   345      * <p>
   346      * This method can be used somewhat like the {@code text} method,
   347      * but it doesn't perform character escaping.
   348      */
   349     public final void write(int i) throws IOException {
   350         if (octetBufferIndex < octetBuffer.length) {
   351             octetBuffer[octetBufferIndex++] = (byte)i;
   352         } else {
   353             out.write(octetBuffer);
   354             octetBufferIndex = 1;
   355             octetBuffer[0] = (byte)i;
   356         }
   357     }
   359     protected final void write(byte[] b) throws IOException {
   360         write(b, 0,  b.length);
   361     }
   363     protected final void write(byte[] b, int start, int length) throws IOException {
   364         if ((octetBufferIndex + length) < octetBuffer.length) {
   365             System.arraycopy(b, start, octetBuffer, octetBufferIndex, length);
   366             octetBufferIndex += length;
   367         } else {
   368             out.write(octetBuffer, 0, octetBufferIndex);
   369             out.write(b, start, length);
   370             octetBufferIndex = 0;
   371         }
   372     }
   374     protected final void flushBuffer() throws IOException {
   375         out.write(octetBuffer, 0, octetBufferIndex);
   376         octetBufferIndex = 0;
   377     }
   379     static byte[] toBytes(String s) {
   380         byte[] buf = new byte[s.length()];
   381         for( int i=s.length()-1; i>=0; i-- )
   382             buf[i] = (byte)s.charAt(i);
   383         return buf;
   384     }
   386     private static final byte[] XMLNS_EQUALS = toBytes(" xmlns=\"");
   387     private static final byte[] XMLNS_COLON = toBytes(" xmlns:");
   388     private static final byte[] EQUALS = toBytes("=\"");
   389     private static final byte[] CLOSE_TAG = toBytes("</");
   390     private static final byte[] EMPTY_TAG = toBytes("/>");
   391     private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
   392     private static final byte[] XML_DECL = toBytes("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
   393 }

mercurial