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

Sat, 01 Dec 2007 00:00:00 +0000

author
duke
date
Sat, 01 Dec 2007 00:00:00 +0000
changeset 1
0961a4a21176
child 42
99fc62f032a7
child 45
31822b475baa
permissions
-rw-r--r--

Initial load

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

mercurial