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

Tue, 09 Apr 2013 14:51:13 +0100

author
alanb
date
Tue, 09 Apr 2013 14:51:13 +0100
changeset 368
0989ad8c0860
parent 286
f50545b5e2f1
child 408
b0610cd08440
permissions
-rw-r--r--

8010393: Update JAX-WS RI to 2.2.9-b12941
Reviewed-by: alanb, erikj
Contributed-by: miroslav.kos@oracle.com, martin.grebac@oracle.com

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

mercurial