Tue, 06 Mar 2012 16:09:35 -0800
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 }