Skip to content

Package: Base64Data$Base64DataSource

Base64Data$Base64DataSource

nameinstructionbranchcomplexitylinemethod
Base64Data.Base64DataSource(Base64Data)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getContentType()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getInputStream()
M: 11 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getName()
M: 2 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getOutputStream()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

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