1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/org/jvnet/staxex/Base64Data.java Tue Mar 06 16:09:35 2012 -0800 1.3 @@ -0,0 +1,474 @@ 1.4 +/* 1.5 + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +package com.sun.xml.internal.org.jvnet.staxex; 1.30 + 1.31 +import javax.activation.DataHandler; 1.32 +import javax.activation.DataSource; 1.33 +import java.io.ByteArrayInputStream; 1.34 +import java.io.ByteArrayOutputStream; 1.35 +import java.io.File; 1.36 +import java.io.FileOutputStream; 1.37 +import java.io.IOException; 1.38 +import java.io.InputStream; 1.39 +import java.io.OutputStream; 1.40 +import javax.xml.stream.XMLStreamException; 1.41 +import javax.xml.stream.XMLStreamWriter; 1.42 + 1.43 +// for testing method 1.44 +//import com.sun.xml.internal.stream.writers.XMLStreamWriterImpl; 1.45 +//import java.io.FileNotFoundException; 1.46 +//import java.io.StringWriter; 1.47 +//import javax.activation.FileDataSource; 1.48 + 1.49 +/** 1.50 + * Binary data represented as base64-encoded string 1.51 + * in XML. 1.52 + * 1.53 + * <p> 1.54 + * Used in conjunction with {@link XMLStreamReaderEx} 1.55 + * and {@link XMLStreamWriterEx}. 1.56 + * 1.57 + * @author Kohsuke Kawaguchi, Martin Grebac 1.58 + */ 1.59 +public class Base64Data implements CharSequence, Cloneable { 1.60 + 1.61 + // either dataHandler or (data,dataLen,mimeType?) must be present 1.62 + // (note that having both is allowed) 1.63 + 1.64 + private DataHandler dataHandler; 1.65 + 1.66 + private byte[] data; 1.67 + /** 1.68 + * Length of the valid data in {@link #data}. 1.69 + */ 1.70 + private int dataLen; 1.71 + /** 1.72 + * True if {@link #data} can be cloned by reference 1.73 + * if Base64Data instance is cloned. 1.74 + */ 1.75 + private boolean dataCloneByRef; 1.76 + /** 1.77 + * Optional MIME type of {@link #data}. 1.78 + * 1.79 + * Unused when {@link #dataHandler} is set. 1.80 + * Use {@link DataHandler#getContentType()} in that case. 1.81 + */ 1.82 + private String mimeType; 1.83 + 1.84 + /** 1.85 + * Default constructor 1.86 + */ 1.87 + public Base64Data() { 1.88 + } 1.89 + 1.90 + /** 1.91 + * Clone constructor 1.92 + * @param that needs to be cloned 1.93 + */ 1.94 + public Base64Data(Base64Data that) { 1.95 + that.get(); 1.96 + if (that.dataCloneByRef) { 1.97 + this.data = that.data; 1.98 + } else { 1.99 + this.data = new byte[that.dataLen]; 1.100 + System.arraycopy(that.data, 0, this.data, 0, that.dataLen); 1.101 + } 1.102 + 1.103 + this.dataCloneByRef = true; 1.104 + this.dataLen = that.dataLen; 1.105 + this.dataHandler = null; 1.106 + this.mimeType = that.mimeType; 1.107 + } 1.108 + 1.109 + /** 1.110 + * Fills in the data object by a portion of the byte[]. 1.111 + * 1.112 + * @param data actual data 1.113 + * @param len 1.114 + * data[0] to data[len-1] are treated as the data. 1.115 + * @param mimeType MIME type 1.116 + * @param cloneByRef 1.117 + * true if data[] can be cloned by reference 1.118 + */ 1.119 + public void set(byte[] data, int len, String mimeType, boolean cloneByRef) { 1.120 + this.data = data; 1.121 + this.dataLen = len; 1.122 + this.dataCloneByRef = cloneByRef; 1.123 + this.dataHandler = null; 1.124 + this.mimeType = mimeType; 1.125 + } 1.126 + 1.127 + /** 1.128 + * Fills in the data object by a portion of the byte[]. 1.129 + * 1.130 + * @param data actual data bytes 1.131 + * @param len 1.132 + * data[0] to data[len-1] are treated as the data. 1.133 + * @param mimeType MIME type 1.134 + */ 1.135 + public void set(byte[] data, int len, String mimeType) { 1.136 + set(data,len,mimeType,false); 1.137 + } 1.138 + 1.139 + /** 1.140 + * Fills in the data object by the byte[] of the exact length. 1.141 + * 1.142 + * @param data 1.143 + * this buffer may be owned directly by the unmarshaleld JAXB object. 1.144 + * @param mimeType MIME type 1.145 + */ 1.146 + public void set(byte[] data,String mimeType) { 1.147 + set(data,data.length,mimeType,false); 1.148 + } 1.149 + 1.150 + /** 1.151 + * Fills in the data object by a {@link DataHandler}. 1.152 + * 1.153 + * @param data DataHandler for the data 1.154 + */ 1.155 + public void set(DataHandler data) { 1.156 + assert data!=null; 1.157 + this.dataHandler = data; 1.158 + this.data = null; 1.159 + } 1.160 + 1.161 + /** 1.162 + * Gets the raw data. If the returned DataHandler is {@link StreamingDataHandler}, 1.163 + * callees may need to downcast to take advantage of its capabilities. 1.164 + * 1.165 + * @see StreamingDataHandler 1.166 + * @return DataHandler for the data 1.167 + */ 1.168 + public DataHandler getDataHandler() { 1.169 + if(dataHandler==null){ 1.170 + dataHandler = new Base64StreamingDataHandler(new Base64DataSource()); 1.171 + } else if (!(dataHandler instanceof StreamingDataHandler)) { 1.172 + dataHandler = new FilterDataHandler(dataHandler); 1.173 + } 1.174 + return dataHandler; 1.175 + } 1.176 + 1.177 + private final class Base64DataSource implements DataSource { 1.178 + public String getContentType() { 1.179 + return getMimeType(); 1.180 + } 1.181 + 1.182 + public InputStream getInputStream() { 1.183 + return new ByteArrayInputStream(data,0,dataLen); 1.184 + } 1.185 + 1.186 + public String getName() { 1.187 + return null; 1.188 + } 1.189 + 1.190 + public OutputStream getOutputStream() { 1.191 + throw new UnsupportedOperationException(); 1.192 + } 1.193 + 1.194 + } 1.195 + 1.196 + private final class Base64StreamingDataHandler extends StreamingDataHandler { 1.197 + 1.198 + Base64StreamingDataHandler(DataSource source) { 1.199 + super(source); 1.200 + } 1.201 + 1.202 + public InputStream readOnce() throws IOException { 1.203 + return getDataSource().getInputStream(); 1.204 + } 1.205 + 1.206 + public void moveTo(File dst) throws IOException { 1.207 + FileOutputStream fout = new FileOutputStream(dst); 1.208 + try { 1.209 + fout.write(data, 0, dataLen); 1.210 + } finally { 1.211 + fout.close(); 1.212 + } 1.213 + } 1.214 + 1.215 + public void close() throws IOException { 1.216 + // nothing to do 1.217 + } 1.218 + } 1.219 + 1.220 + private static final class FilterDataHandler extends StreamingDataHandler { 1.221 + 1.222 + FilterDataHandler(DataHandler dh) { 1.223 + super(dh.getDataSource()); 1.224 + } 1.225 + 1.226 + public InputStream readOnce() throws IOException { 1.227 + return getDataSource().getInputStream(); 1.228 + } 1.229 + 1.230 + public void moveTo(File dst) throws IOException { 1.231 + byte[] buf = new byte[8192]; 1.232 + InputStream in = null; 1.233 + OutputStream out = null; 1.234 + try { 1.235 + in = getDataSource().getInputStream(); 1.236 + out = new FileOutputStream(dst); 1.237 + while (true) { 1.238 + int amountRead = in.read(buf); 1.239 + if (amountRead == -1) { 1.240 + break; 1.241 + } 1.242 + out.write(buf, 0, amountRead); 1.243 + } 1.244 + } finally { 1.245 + if (in != null) { 1.246 + try { 1.247 + in.close(); 1.248 + } catch(IOException ioe) { 1.249 + // nothing to do 1.250 + } 1.251 + } 1.252 + if (out != null) { 1.253 + try { 1.254 + out.close(); 1.255 + } catch(IOException ioe) { 1.256 + // nothing to do 1.257 + } 1.258 + } 1.259 + } 1.260 + } 1.261 + 1.262 + public void close() throws IOException { 1.263 + // nothing to do 1.264 + } 1.265 + } 1.266 + 1.267 + /** 1.268 + * Gets the byte[] of the exact length. 1.269 + * 1.270 + * @return byte[] for data 1.271 + */ 1.272 + public byte[] getExact() { 1.273 + get(); 1.274 + if(dataLen!=data.length) { 1.275 + byte[] buf = new byte[dataLen]; 1.276 + System.arraycopy(data,0,buf,0,dataLen); 1.277 + data = buf; 1.278 + } 1.279 + return data; 1.280 + } 1.281 + 1.282 + /** 1.283 + * Gets the data as an {@link InputStream}. 1.284 + * 1.285 + * @return data as InputStream 1.286 + * @throws IOException if i/o error occurs 1.287 + */ 1.288 + public InputStream getInputStream() throws IOException { 1.289 + if(dataHandler!=null) 1.290 + return dataHandler.getInputStream(); 1.291 + else 1.292 + return new ByteArrayInputStream(data,0,dataLen); 1.293 + } 1.294 + 1.295 + /** 1.296 + * Returns false if this object only has {@link DataHandler} and therefore 1.297 + * {@link #get()} operation is likely going to be expensive. 1.298 + * 1.299 + * @return false if it has only DataHandler 1.300 + */ 1.301 + public boolean hasData() { 1.302 + return data!=null; 1.303 + } 1.304 + 1.305 + /** 1.306 + * Gets the raw data. The size of the byte array maybe larger than the actual length. 1.307 + * 1.308 + * @return data as byte[], the array may be larger 1.309 + */ 1.310 + public byte[] get() { 1.311 + if(data==null) { 1.312 + try { 1.313 + ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx(1024); 1.314 + InputStream is = dataHandler.getDataSource().getInputStream(); 1.315 + baos.readFrom(is); 1.316 + is.close(); 1.317 + data = baos.getBuffer(); 1.318 + dataLen = baos.size(); 1.319 + dataCloneByRef = true; 1.320 + } catch (IOException e) { 1.321 + // TODO: report the error to the unmarshaller 1.322 + dataLen = 0; // recover by assuming length-0 data 1.323 + } 1.324 + } 1.325 + return data; 1.326 + } 1.327 + 1.328 + /** 1.329 + * Gets the length of the binary data counted in bytes. 1.330 + * 1.331 + * Note that if this object encapsulates {@link DataHandler}, 1.332 + * this method would have to read the whole thing into {@code byte[]} 1.333 + * just to count the length, because {@link DataHandler} 1.334 + * doesn't easily expose the length. 1.335 + * 1.336 + * @return no of bytes 1.337 + */ 1.338 + public int getDataLen() { 1.339 + get(); 1.340 + return dataLen; 1.341 + } 1.342 + 1.343 + public String getMimeType() { 1.344 + if(mimeType==null) 1.345 + return "application/octet-stream"; 1.346 + return mimeType; 1.347 + } 1.348 + 1.349 + /** 1.350 + * Gets the number of characters needed to represent 1.351 + * this binary data in the base64 encoding. 1.352 + */ 1.353 + public int length() { 1.354 + // for each 3 bytes you use 4 chars 1.355 + // if the remainder is 1 or 2 there will be 4 more 1.356 + get(); // fill in the buffer if necessary 1.357 + return ((dataLen+2)/3)*4; 1.358 + } 1.359 + 1.360 + /** 1.361 + * Encode this binary data in the base64 encoding 1.362 + * and returns the character at the specified position. 1.363 + */ 1.364 + public char charAt(int index) { 1.365 + // we assume that the length() method is called before this method 1.366 + // (otherwise how would the caller know that the index is valid?) 1.367 + // so we assume that the byte[] is already populated 1.368 + 1.369 + int offset = index%4; 1.370 + int base = (index/4)*3; 1.371 + 1.372 + byte b1,b2; 1.373 + 1.374 + switch(offset) { 1.375 + case 0: 1.376 + return Base64Encoder.encode(data[base]>>2); 1.377 + case 1: 1.378 + if(base+1<dataLen) 1.379 + b1 = data[base+1]; 1.380 + else 1.381 + b1 = 0; 1.382 + return Base64Encoder.encode( 1.383 + ((data[base]&0x3)<<4) | 1.384 + ((b1>>4)&0xF)); 1.385 + case 2: 1.386 + if(base+1<dataLen) { 1.387 + b1 = data[base+1]; 1.388 + if(base+2<dataLen) 1.389 + b2 = data[base+2]; 1.390 + else 1.391 + b2 = 0; 1.392 + 1.393 + return Base64Encoder.encode( 1.394 + ((b1&0xF)<<2)| 1.395 + ((b2>>6)&0x3)); 1.396 + } else 1.397 + return '='; 1.398 + case 3: 1.399 + if(base+2<dataLen) 1.400 + return Base64Encoder.encode(data[base+2]&0x3F); 1.401 + else 1.402 + return '='; 1.403 + } 1.404 + 1.405 + throw new IllegalStateException(); 1.406 + } 1.407 + 1.408 + /** 1.409 + * Internally this is only used to split a text to a list, 1.410 + * which doesn't happen that much for base64. 1.411 + * So this method should be smaller than faster. 1.412 + */ 1.413 + public CharSequence subSequence(int start, int end) { 1.414 + StringBuilder buf = new StringBuilder(); 1.415 + get(); // fill in the buffer if we haven't done so 1.416 + for( int i=start; i<end; i++ ) 1.417 + buf.append(charAt(i)); 1.418 + return buf; 1.419 + } 1.420 + 1.421 + /** 1.422 + * Returns the base64 encoded string of this data. 1.423 + */ 1.424 + @Override 1.425 + public String toString() { 1.426 + get(); // fill in the buffer 1.427 + return Base64Encoder.print(data, 0, dataLen); 1.428 + } 1.429 + 1.430 + public void writeTo(char[] buf, int start) { 1.431 + get(); 1.432 + Base64Encoder.print(data, 0, dataLen, buf, start); 1.433 + } 1.434 + 1.435 + public void writeTo(XMLStreamWriter output) throws IOException, XMLStreamException { 1.436 + if (data==null) { 1.437 + try { 1.438 + InputStream is = dataHandler.getDataSource().getInputStream(); 1.439 + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); // dev-null stream 1.440 + Base64EncoderStream encWriter = new Base64EncoderStream(output, outStream); 1.441 + int b = -1; 1.442 + while ((b = is.read()) != -1) { 1.443 + encWriter.write(b); 1.444 + } 1.445 + outStream.close(); 1.446 + encWriter.close(); 1.447 + } catch (IOException e) { 1.448 + dataLen = 0; // recover by assuming length-0 data 1.449 + throw e; 1.450 + } 1.451 + } else { 1.452 + // the data is already in memory and not streamed 1.453 + String s = Base64Encoder.print(data, 0, dataLen); 1.454 + output.writeCharacters(s); 1.455 + } 1.456 + } 1.457 + 1.458 + @Override 1.459 + public Base64Data clone() { 1.460 + return new Base64Data(this); 1.461 + } 1.462 + 1.463 +// public static void main(String[] args) throws FileNotFoundException, IOException, XMLStreamException { 1.464 +// Base64Data data = new Base64Data(); 1.465 +// 1.466 +// data.set(new DataHandler(new FileDataSource(new File("/home/snajper/Desktop/a.txt")))); 1.467 +// 1.468 +// StringWriter sw = new StringWriter(); 1.469 +// XMLStreamWriterImpl wi = new XMLStreamWriterImpl(sw, null); 1.470 +// 1.471 +// data.writeTo(wi); 1.472 +// wi.flush();sw.flush(); 1.473 +// System.out.println("SW: " + sw.toString()); 1.474 +// 1.475 +// } 1.476 + 1.477 +}