src/share/jaxws_classes/com/sun/xml/internal/org/jvnet/staxex/Base64Data.java

Tue, 06 Mar 2012 16:09:35 -0800

author
ohair
date
Tue, 06 Mar 2012 16:09:35 -0800
changeset 286
f50545b5e2f1
child 368
0989ad8c0860
permissions
-rw-r--r--

7150322: Stop using drop source bundles in jaxws
Reviewed-by: darcy, ohrstrom

     1 /*
     2  * Copyright (c) 1997, 2010, 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.org.jvnet.staxex;
    28 import javax.activation.DataHandler;
    29 import javax.activation.DataSource;
    30 import java.io.ByteArrayInputStream;
    31 import java.io.ByteArrayOutputStream;
    32 import java.io.File;
    33 import java.io.FileOutputStream;
    34 import java.io.IOException;
    35 import java.io.InputStream;
    36 import java.io.OutputStream;
    37 import javax.xml.stream.XMLStreamException;
    38 import javax.xml.stream.XMLStreamWriter;
    40 // for testing method
    41 //import com.sun.xml.internal.stream.writers.XMLStreamWriterImpl;
    42 //import java.io.FileNotFoundException;
    43 //import java.io.StringWriter;
    44 //import javax.activation.FileDataSource;
    46 /**
    47  * Binary data represented as base64-encoded string
    48  * in XML.
    49  *
    50  * <p>
    51  * Used in conjunction with {@link XMLStreamReaderEx}
    52  * and {@link XMLStreamWriterEx}.
    53  *
    54  * @author Kohsuke Kawaguchi, Martin Grebac
    55  */
    56 public class Base64Data implements CharSequence, Cloneable {
    58     // either dataHandler or (data,dataLen,mimeType?) must be present
    59     // (note that having both is allowed)
    61     private DataHandler dataHandler;
    63     private byte[] data;
    64     /**
    65      * Length of the valid data in {@link #data}.
    66      */
    67     private int dataLen;
    68     /**
    69      * True if {@link #data} can be cloned by reference
    70      * if Base64Data instance is cloned.
    71      */
    72     private boolean dataCloneByRef;
    73     /**
    74      * Optional MIME type of {@link #data}.
    75      *
    76      * Unused when {@link #dataHandler} is set.
    77      * Use {@link DataHandler#getContentType()} in that case.
    78      */
    79     private String mimeType;
    81     /**
    82      * Default constructor
    83      */
    84     public Base64Data() {
    85     }
    87     /**
    88      * Clone constructor
    89      * @param that needs to be cloned
    90      */
    91     public Base64Data(Base64Data that) {
    92         that.get();
    93         if (that.dataCloneByRef) {
    94             this.data = that.data;
    95         } else {
    96             this.data = new byte[that.dataLen];
    97             System.arraycopy(that.data, 0, this.data, 0, that.dataLen);
    98         }
   100         this.dataCloneByRef = true;
   101         this.dataLen = that.dataLen;
   102         this.dataHandler = null;
   103         this.mimeType = that.mimeType;
   104     }
   106     /**
   107      * Fills in the data object by a portion of the byte[].
   108      *
   109      * @param data actual data
   110      * @param len
   111      *      data[0] to data[len-1] are treated as the data.
   112      * @param mimeType MIME type
   113      * @param cloneByRef
   114      *      true if data[] can be cloned by reference
   115      */
   116     public void set(byte[] data, int len, String mimeType, boolean cloneByRef) {
   117         this.data = data;
   118         this.dataLen = len;
   119         this.dataCloneByRef = cloneByRef;
   120         this.dataHandler = null;
   121         this.mimeType = mimeType;
   122     }
   124     /**
   125      * Fills in the data object by a portion of the byte[].
   126      *
   127      * @param data actual data bytes
   128      * @param len
   129      *      data[0] to data[len-1] are treated as the data.
   130      * @param mimeType MIME type
   131      */
   132     public void set(byte[] data, int len, String mimeType) {
   133         set(data,len,mimeType,false);
   134     }
   136     /**
   137      * Fills in the data object by the byte[] of the exact length.
   138      *
   139      * @param data
   140      *      this buffer may be owned directly by the unmarshaleld JAXB object.
   141      * @param mimeType MIME type
   142      */
   143     public void set(byte[] data,String mimeType) {
   144         set(data,data.length,mimeType,false);
   145     }
   147     /**
   148      * Fills in the data object by a {@link DataHandler}.
   149      *
   150      * @param data DataHandler for the data
   151      */
   152     public void set(DataHandler data) {
   153         assert data!=null;
   154         this.dataHandler = data;
   155         this.data = null;
   156     }
   158     /**
   159      * Gets the raw data. If the returned DataHandler is {@link StreamingDataHandler},
   160      * callees may need to downcast to take advantage of its capabilities.
   161      *
   162      * @see StreamingDataHandler
   163      * @return DataHandler for the data
   164      */
   165     public DataHandler getDataHandler() {
   166         if(dataHandler==null){
   167             dataHandler = new Base64StreamingDataHandler(new Base64DataSource());
   168         } else if (!(dataHandler instanceof StreamingDataHandler)) {
   169             dataHandler = new FilterDataHandler(dataHandler);
   170         }
   171         return dataHandler;
   172     }
   174     private final class Base64DataSource implements DataSource {
   175         public String getContentType() {
   176             return getMimeType();
   177         }
   179         public InputStream getInputStream() {
   180             return new ByteArrayInputStream(data,0,dataLen);
   181         }
   183         public String getName() {
   184             return null;
   185         }
   187         public OutputStream getOutputStream() {
   188             throw new UnsupportedOperationException();
   189         }
   191     }
   193     private final class Base64StreamingDataHandler extends StreamingDataHandler {
   195         Base64StreamingDataHandler(DataSource source) {
   196             super(source);
   197         }
   199         public InputStream readOnce() throws IOException {
   200             return getDataSource().getInputStream();
   201         }
   203         public void moveTo(File dst) throws IOException {
   204             FileOutputStream fout = new FileOutputStream(dst);
   205             try {
   206                 fout.write(data, 0, dataLen);
   207             } finally {
   208                 fout.close();
   209             }
   210         }
   212         public void close() throws IOException {
   213             // nothing to do
   214         }
   215     }
   217     private static final class FilterDataHandler extends StreamingDataHandler {
   219         FilterDataHandler(DataHandler dh) {
   220             super(dh.getDataSource());
   221         }
   223         public InputStream readOnce() throws IOException {
   224             return getDataSource().getInputStream();
   225         }
   227         public void moveTo(File dst) throws IOException {
   228             byte[] buf = new byte[8192];
   229             InputStream in = null;
   230             OutputStream out = null;
   231             try {
   232                 in = getDataSource().getInputStream();
   233                 out = new FileOutputStream(dst);
   234                 while (true) {
   235                     int amountRead = in.read(buf);
   236                     if (amountRead == -1) {
   237                         break;
   238                     }
   239                     out.write(buf, 0, amountRead);
   240                 }
   241             } finally {
   242                 if (in != null) {
   243                     try {
   244                         in.close();
   245                     } catch(IOException ioe) {
   246                         // nothing to do
   247                     }
   248                 }
   249                 if (out != null) {
   250                     try {
   251                         out.close();
   252                     } catch(IOException ioe) {
   253                         // nothing to do
   254                     }
   255                 }
   256             }
   257         }
   259         public void close() throws IOException {
   260             // nothing to do
   261         }
   262     }
   264     /**
   265      * Gets the byte[] of the exact length.
   266      *
   267      * @return byte[] for data
   268      */
   269     public byte[] getExact() {
   270         get();
   271         if(dataLen!=data.length) {
   272             byte[] buf = new byte[dataLen];
   273             System.arraycopy(data,0,buf,0,dataLen);
   274             data = buf;
   275         }
   276         return data;
   277     }
   279     /**
   280      * Gets the data as an {@link InputStream}.
   281      *
   282      * @return data as InputStream
   283      * @throws IOException if i/o error occurs
   284      */
   285     public InputStream getInputStream() throws IOException {
   286         if(dataHandler!=null)
   287             return dataHandler.getInputStream();
   288         else
   289             return new ByteArrayInputStream(data,0,dataLen);
   290     }
   292     /**
   293      * Returns false if this object only has {@link DataHandler} and therefore
   294      * {@link #get()} operation is likely going to be expensive.
   295      *
   296      * @return false if it has only DataHandler
   297      */
   298     public boolean hasData() {
   299         return data!=null;
   300     }
   302     /**
   303      * Gets the raw data. The size of the byte array maybe larger than the actual length.
   304      *
   305      * @return data as byte[], the array may be larger
   306      */
   307     public byte[] get() {
   308         if(data==null) {
   309             try {
   310                 ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx(1024);
   311                 InputStream is = dataHandler.getDataSource().getInputStream();
   312                 baos.readFrom(is);
   313                 is.close();
   314                 data = baos.getBuffer();
   315                 dataLen = baos.size();
   316                 dataCloneByRef = true;
   317             } catch (IOException e) {
   318                 // TODO: report the error to the unmarshaller
   319                 dataLen = 0;    // recover by assuming length-0 data
   320             }
   321         }
   322         return data;
   323     }
   325     /**
   326      * Gets the length of the binary data counted in bytes.
   327      *
   328      * Note that if this object encapsulates {@link DataHandler},
   329      * this method would have to read the whole thing into {@code byte[]}
   330      * just to count the length, because {@link DataHandler}
   331      * doesn't easily expose the length.
   332      *
   333      * @return no of bytes
   334      */
   335     public int getDataLen() {
   336         get();
   337         return dataLen;
   338     }
   340     public String getMimeType() {
   341         if(mimeType==null)
   342             return "application/octet-stream";
   343         return mimeType;
   344     }
   346     /**
   347      * Gets the number of characters needed to represent
   348      * this binary data in the base64 encoding.
   349      */
   350     public int length() {
   351         // for each 3 bytes you use 4 chars
   352         // if the remainder is 1 or 2 there will be 4 more
   353         get();  // fill in the buffer if necessary
   354         return ((dataLen+2)/3)*4;
   355     }
   357     /**
   358      * Encode this binary data in the base64 encoding
   359      * and returns the character at the specified position.
   360      */
   361     public char charAt(int index) {
   362         // we assume that the length() method is called before this method
   363         // (otherwise how would the caller know that the index is valid?)
   364         // so we assume that the byte[] is already populated
   366         int offset = index%4;
   367         int base = (index/4)*3;
   369         byte b1,b2;
   371         switch(offset) {
   372         case 0:
   373             return Base64Encoder.encode(data[base]>>2);
   374         case 1:
   375             if(base+1<dataLen)
   376                 b1 = data[base+1];
   377             else
   378                 b1 = 0;
   379             return Base64Encoder.encode(
   380                         ((data[base]&0x3)<<4) |
   381                         ((b1>>4)&0xF));
   382         case 2:
   383             if(base+1<dataLen) {
   384                 b1 = data[base+1];
   385                 if(base+2<dataLen)
   386                     b2 = data[base+2];
   387                 else
   388                     b2 = 0;
   390                 return Base64Encoder.encode(
   391                             ((b1&0xF)<<2)|
   392                             ((b2>>6)&0x3));
   393             } else
   394                 return '=';
   395         case 3:
   396             if(base+2<dataLen)
   397                 return Base64Encoder.encode(data[base+2]&0x3F);
   398             else
   399                 return '=';
   400         }
   402         throw new IllegalStateException();
   403     }
   405     /**
   406      * Internally this is only used to split a text to a list,
   407      * which doesn't happen that much for base64.
   408      * So this method should be smaller than faster.
   409      */
   410     public CharSequence subSequence(int start, int end) {
   411         StringBuilder buf = new StringBuilder();
   412         get();  // fill in the buffer if we haven't done so
   413         for( int i=start; i<end; i++ )
   414             buf.append(charAt(i));
   415         return buf;
   416     }
   418     /**
   419      * Returns the base64 encoded string of this data.
   420      */
   421     @Override
   422     public String toString() {
   423         get();  // fill in the buffer
   424         return Base64Encoder.print(data, 0, dataLen);
   425     }
   427     public void writeTo(char[] buf, int start) {
   428         get();
   429         Base64Encoder.print(data, 0, dataLen, buf, start);
   430     }
   432     public void writeTo(XMLStreamWriter output) throws IOException, XMLStreamException {
   433         if (data==null) {
   434             try {
   435                 InputStream is = dataHandler.getDataSource().getInputStream();
   436                 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); // dev-null stream
   437                 Base64EncoderStream encWriter = new Base64EncoderStream(output, outStream);
   438                 int b = -1;
   439                 while ((b = is.read()) != -1) {
   440                     encWriter.write(b);
   441                 }
   442                 outStream.close();
   443                 encWriter.close();
   444             } catch (IOException e) {
   445                 dataLen = 0;    // recover by assuming length-0 data
   446                 throw e;
   447             }
   448         } else {
   449             // the data is already in memory and not streamed
   450             String s = Base64Encoder.print(data, 0, dataLen);
   451             output.writeCharacters(s);
   452         }
   453     }
   455     @Override
   456     public Base64Data clone() {
   457         return new Base64Data(this);
   458     }
   460 //    public static void main(String[] args) throws FileNotFoundException, IOException, XMLStreamException {
   461 //        Base64Data data = new Base64Data();
   462 //
   463 //        data.set(new DataHandler(new FileDataSource(new File("/home/snajper/Desktop/a.txt"))));
   464 //
   465 //        StringWriter sw = new StringWriter();
   466 //        XMLStreamWriterImpl wi = new XMLStreamWriterImpl(sw, null);
   467 //
   468 //        data.writeTo(wi);
   469 //        wi.flush();sw.flush();
   470 //        System.out.println("SW: " + sw.toString());
   471 //
   472 //    }
   474 }

mercurial