diff -r 000000000000 -r 373ffda63c9a src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/unmarshaller/Base64Data.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/unmarshaller/Base64Data.java Wed Apr 27 01:27:09 2016 +0800 @@ -0,0 +1,307 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.xml.internal.bind.v2.runtime.unmarshaller; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.activation.DataHandler; +import javax.activation.DataSource; +import javax.xml.stream.XMLStreamException; + +import javax.xml.stream.XMLStreamWriter; + +import com.sun.xml.internal.bind.DatatypeConverterImpl; +import com.sun.xml.internal.bind.v2.runtime.XMLSerializer; +import com.sun.xml.internal.bind.v2.runtime.output.Pcdata; +import com.sun.xml.internal.bind.v2.runtime.output.UTF8XmlOutput; +import com.sun.xml.internal.bind.v2.util.ByteArrayOutputStreamEx; +import com.sun.istack.internal.Nullable; + +/** + * Fed to unmarshaller when the 'text' data is actually + * a virtual image of base64 encoding of the binary data + * transferred on the wire. + * + * Used for the MTOM support. + * + * This object is mutable and the owner of this object can + * reuse it with new data. + * + * Also used by the marshaller to write out the binary data + * that could be possibly attached. + * + * @see XmlVisitor#text(CharSequence) + * @see XMLSerializer#text(Pcdata,String) + * + * @author Kohsuke Kawaguchi, Martin Grebac + */ +public final class Base64Data extends Pcdata { + + // either dataHandler or (data,dataLen,mimeType?) must be present + private DataHandler dataHandler; + private byte[] data; + /** + * Length of the valid data in {@link #data}. + */ + private int dataLen; + /** + * Optional MIME type of {@link #data}. + * + * Unused when {@link #dataHandler} is set. + * Use {@link DataHandler#getContentType()} in that case. + */ + private @Nullable + String mimeType; + + /** + * Fills in the data object by a portion of the byte[]. + * + * @param len + * data[0] to data[len-1] are treated as the data. + */ + public void set(byte[] data, int len, @Nullable String mimeType) { + this.data = data; + this.dataLen = len; + this.dataHandler = null; + this.mimeType = mimeType; + } + + /** + * Fills in the data object by the byte[] of the exact length. + * + * @param data + * this buffer may be owned directly by the unmarshaleld JAXB object. + */ + public void set(byte[] data, @Nullable String mimeType) { + set(data, data.length, mimeType); + } + + /** + * Fills in the data object by a {@link DataHandler}. + */ + public void set(DataHandler data) { + assert data != null; + this.dataHandler = data; + this.data = null; + } + + /** + * Gets the raw data. + */ + public DataHandler getDataHandler() { + if (dataHandler == null) { + dataHandler = new DataHandler(new DataSource() { + + public String getContentType() { + return getMimeType(); + } + + public InputStream getInputStream() { + return new ByteArrayInputStream(data, 0, dataLen); + } + + public String getName() { + return null; + } + + public OutputStream getOutputStream() { + throw new UnsupportedOperationException(); + } + }); + } + + return dataHandler; + } + + /** + * Gets the byte[] of the exact length. + */ + public byte[] getExact() { + get(); + if (dataLen != data.length) { + byte[] buf = new byte[dataLen]; + System.arraycopy(data, 0, buf, 0, dataLen); + data = buf; + } + return data; + } + + /** + * Gets the data as an {@link InputStream}. + */ + public InputStream getInputStream() throws IOException { + if (dataHandler != null) { + return dataHandler.getInputStream(); + } else { + return new ByteArrayInputStream(data, 0, dataLen); + } + } + + /** + * Returns false if this object only has {@link DataHandler} and therefore + * {@link #get()} operation is likely going to be expensive. + */ + public boolean hasData() { + return data != null; + } + + /** + * Gets the raw data. The size of the byte array maybe larger than the actual length. + */ + public byte[] get() { + if (data == null) { + try { + ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx(1024); + InputStream is = dataHandler.getDataSource().getInputStream(); + baos.readFrom(is); + is.close(); + data = baos.getBuffer(); + dataLen = baos.size(); + } catch (IOException e) { + // TODO: report the error to the unmarshaller + dataLen = 0; // recover by assuming length-0 data + } + } + return data; + } + + public int getDataLen() { + return dataLen; + } + + public String getMimeType() { + if (mimeType == null) { + return "application/octet-stream"; + } + return mimeType; + } + + /** + * Gets the number of characters needed to represent + * this binary data in the base64 encoding. + */ + public int length() { + // for each 3 bytes you use 4 chars + // if the remainder is 1 or 2 there will be 4 more + get(); // fill in the buffer if necessary + return ((dataLen + 2) / 3) * 4; + } + + /** + * Encode this binary data in the base64 encoding + * and returns the character at the specified position. + */ + public char charAt(int index) { + // we assume that the length() method is called before this method + // (otherwise how would the caller know that the index is valid?) + // so we assume that the byte[] is already populated + + int offset = index % 4; + int base = (index / 4) * 3; + + byte b1, b2; + + switch (offset) { + case 0: + return DatatypeConverterImpl.encode(data[base] >> 2); + case 1: + if (base + 1 < dataLen) { + b1 = data[base + 1]; + } else { + b1 = 0; + } + return DatatypeConverterImpl.encode( + ((data[base] & 0x3) << 4) + | ((b1 >> 4) & 0xF)); + case 2: + if (base + 1 < dataLen) { + b1 = data[base + 1]; + if (base + 2 < dataLen) { + b2 = data[base + 2]; + } else { + b2 = 0; + } + + return DatatypeConverterImpl.encode( + ((b1 & 0xF) << 2) + | ((b2 >> 6) & 0x3)); + } else { + return '='; + } + case 3: + if (base + 2 < dataLen) { + return DatatypeConverterImpl.encode(data[base + 2] & 0x3F); + } else { + return '='; + } + } + + throw new IllegalStateException(); + } + + /** + * Internally this is only used to split a text to a list, + * which doesn't happen that much for base64. + * So this method should be smaller than faster. + */ + public CharSequence subSequence(int start, int end) { + StringBuilder buf = new StringBuilder(); + get(); // fill in the buffer if we haven't done so + for (int i = start; i < end; i++) { + buf.append(charAt(i)); + } + return buf; + } + + /** + * Returns the base64 encoded string of this data. + */ + public String toString() { + get(); // fill in the buffer + return DatatypeConverterImpl._printBase64Binary(data, 0, dataLen); + } + + @Override + public void writeTo(char[] buf, int start) { + get(); + DatatypeConverterImpl._printBase64Binary(data, 0, dataLen, buf, start); + } + + public void writeTo(UTF8XmlOutput output) throws IOException { + // TODO: this is inefficient if the data source is note byte[] but DataHandler + get(); + output.text(data, dataLen); + } + + public void writeTo(XMLStreamWriter output) throws IOException, XMLStreamException { + get(); + DatatypeConverterImpl._printBase64Binary(data, 0, dataLen, output); + } + +}