/* * Copyright 2013 Guidewire Software, Inc. */ package gw.xml; import gw.lang.PublishInGosu; import gw.util.StreamUtil; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; /** * A provider of binary data, for XSD base64Binary datatypes. The backing content can be read or assigned as * an input stream or a byte array. If the backing content is an input stream, and the data is read as * an input stream, the backing content becomes invalid for additional reads. For example, if an MTOM web * service is invoked, the backing content will be an input stream for any XOP-encoded base64Binary data. * This data can be read in its input stream form, but only once, to avoid the need to read the data into * memory or store it to disk. If the content is known to be small in size, the Bytes property can be used * instead, at which point this BinaryData will read the entire input stream into memory, after which * it can be read multiple times. The current multi-read state of this binary data can be inspected * using the MultiRead property. */ @PublishInGosu public final class BinaryData { private byte[] _bytes = {}; private InputStream _inputStream; /** * Creates a new binary data, backed by a zero-length byte array. */ public BinaryData() { } /** * Creates a new binary data, backed by the specified input stream. * @param inputStream the input stream */ public BinaryData(InputStream inputStream) { setInputStream(inputStream); } /** * Creates a new binary data, backed by the specified byte array. * * @param bytes the backing bytes */ public BinaryData(byte[] bytes) { setBytes(bytes); } /** * Returns the backing content as a byte array. If the backing content was previously an input stream, * invoking this property will cause the entire content of the input stream to be read into memory, and * the backing content will thereafter be the resulting byte array. * @return the backing content as a byte array * @throws IOException if an I/O error occurs * @throws XmlException If the backing data is invalid */ public byte[] getBytes() throws IOException { if ( _bytes == null ) { _bytes = StreamUtil.getContent( getInputStream() ); } return _bytes; } /** * Returns the backing content as an input stream. If the backing content is an input stream, invoking * this property will cause the backing content to become invalid. * @return the backing content as an input stream * @throws XmlException If the backing data is invalid */ public InputStream getInputStream() { if ( _inputStream == null ) { if ( _bytes == null ) { throw new XmlException( "Binary data has already been read" ); } return new ByteArrayInputStream( _bytes ); } InputStream ret = _inputStream; _inputStream = null; // can only be called once if backed by an actual input stream return ret; } /** * Sets the backing content as a byte array. If the backing content was previously invalid, setting * this property will cause it to become valid. * @param bytes the new backing byte array */ public void setBytes( byte[] bytes ) { if ( bytes == null ) { throw new IllegalArgumentException( "bytes cannot be null" ); } _bytes = bytes; _inputStream = null; } /** * Sets the backing content as an input stream. If the backing content was previously invalid, setting * this property will cause it to become valid. * @param inputStream the new backing input stream */ public void setInputStream( InputStream inputStream ) { if ( inputStream == null ) { throw new IllegalArgumentException( "inputStream cannot be null" ); } _inputStream = inputStream; _bytes = null; } /** * Returns true if the backing content can be accessed multiple times via the InputStream property. This is * only true if the backing content is currently a byte array. Keep in mind that accessing the Bytes property * on a previously non-multi-read provider will cause the entire contents of the backing input stream to * be read into memory and stored, causing the provider to become a multi-read provider. * @return true if the backing content can be accessed multiple times via the InputStream property */ public boolean isMultiRead() { return _inputStream == null; } @Override public boolean equals( Object o ) { if ( this == o ) return true; if ( o == null || getClass() != o.getClass() ) return false; BinaryData that = (BinaryData) o; if ( ! isMultiRead() || ! that.isMultiRead() ) { return false; } return Arrays.equals( _bytes, that._bytes ); } @Override public int hashCode() { return _bytes != null ? Arrays.hashCode( _bytes ) : 0; } }