![JAR search and dependency download from the Maven repository](/logo.png)
xdev.vt.XdevBlob Maven / Gradle / Ivy
/*
* XDEV Application Framework - XDEV Application Framework
* Copyright © 2003 XDEV Software (https://xdev.software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
package xdev.vt;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.sql.Blob;
import java.sql.SQLException;
import java.util.Arrays;
import javax.sql.rowset.serial.SerialBlob;
import javax.sql.rowset.serial.SerialException;
import xdev.db.DBException;
import xdev.io.ByteHolder;
import xdev.io.IOUtils;
/**
* The mapping in XDEV for the SQL BLOB type. A XdevBlob stores a Binary Large
* Object as a column value in a row of a {@link VirtualTable}.
*
* The XdevBlob holds either an reference to a {@link Blob} object or plain
* bytes.
*
* @author XDEV Software Corp.
* @see Blob
*
*/
public class XdevBlob implements ByteHolder, Serializable, Comparable
{
private static final long serialVersionUID = 7975847043132313545L;
private byte[] bytes;
private Blob blob;
/**
* Initializes a new instance of {@link XdevBlob}.
*/
public XdevBlob()
{
this(new byte[0]);
}
/**
* Initializes a new instance of {@link XdevBlob}.
*
* @param bytes
* a byte array initially to be set
*/
public XdevBlob(byte[] bytes)
{
super();
this.blob = null;
this.bytes = bytes;
}
/**
* Initializes a new instance of {@link XdevBlob}.
*
* @param blob
* a {@link Blob} initially to be set.
* @throws DBException
* if the specified blob could not be set.
*/
public XdevBlob(Blob blob) throws DBException
{
super();
this.bytes = null;
this.blob = blob;
}
void readFully() throws DBException
{
if(bytes == null && blob != null)
{
int length = length();
if(length >= 0)
{
bytes = new byte[length];
InputStream in = getBinaryStream();
try
{
int read;
int offset = 0;
while(offset < length && (read = in.read(bytes,offset,length - offset)) != -1)
{
offset += read;
}
}
catch(IOException ioe)
{
throw new DBException(null,ioe);
}
finally
{
IOUtils.closeSilent(in);
}
}
else
{
bytes = new byte[0];
}
blob = null;
}
}
/**
* Returns the number of bytes of this {@link XdevBlob}.
*
* @return the number of bytes or -1 if the length couldn't be retrieved
*/
public int length()
{
if(blob != null)
{
try
{
return (int)blob.length();
}
catch(SQLException e)
{
return -1;
}
}
else
{
return bytes.length;
}
}
/**
* Truncates the BLOB value that this {@link XdevBlob} object represents to
* be len bytes in length.
*
* @param length
* the length, in bytes, to which the BLOB value that this Blob
* object represents should be truncated
* @throws DBException
* if truncate was not successful.
*/
public void truncate(long length) throws DBException
{
if(blob != null)
{
try
{
blob.truncate(length);
}
catch(SQLException e)
{
throw new DBException(null,e);
}
}
else
{
if(length < bytes.length && length >= 0)
{
byte[] bytes = new byte[(int)length];
System.arraycopy(this.bytes,0,bytes,0,bytes.length);
this.bytes = bytes;
}
}
}
/**
* Retrieves the BLOB value designated by this Blob instance as a stream.
*
* @return an {@link InputStream} containing the BLOB data
* @throws DBException
* if there is an error accessing the BLOB value
*/
public InputStream getBinaryStream() throws DBException
{
if(blob != null)
{
try
{
return blob.getBinaryStream();
}
catch(SQLException e)
{
throw new DBException(null,e);
}
}
else
{
return new ByteArrayInputStream(bytes);
}
}
/**
* Returns an InputStream object that contains a partial Blob value,
* starting with the byte specified by pos, which is length bytes in length.
*
* @param position
* the offset to the first byte of the partial value to be
* retrieved
* @param length
* the length in bytes of the partial value to be retrieved
*
* @return {@link InputStream} through which the partial Blob value can be
* read.
*
* @throws DBException
* if there is an error accessing the BLOB value
*/
public InputStream getBinaryStream(int position, int length) throws DBException
{
if(blob != null)
{
try
{
return blob.getBinaryStream(position + 1,length);
}
catch(SQLException e)
{
throw new DBException(null,e);
}
}
else
{
return new ByteArrayInputStream(bytes,position,length);
}
}
/**
* Retrieves a stream that can be used to write to the BLOB value that this
* Blob object represents. The stream begins at position pos. The bytes
* written to the stream will overwrite the existing bytes in the Blob
* object starting at the position pos. If the end of the Blob value is
* reached while writing to the stream, then the length of the Blob value
* will be increased to accomodate the extra bytes.
*
* @param position
* the position in the BLOB value at which to start writing
* @return a {@link OutputStream} object to which data can be written
* @throws DBException
* if there is an error accessing the BLOB value
*/
public OutputStream setBinaryStream(final int position) throws DBException
{
if(blob != null)
{
try
{
return blob.setBinaryStream(position + 1);
}
catch(SQLException e)
{
throw new DBException(null,e);
}
}
else
{
return new ByteArrayOutputStream()
{
@Override
public void close() throws IOException
{
byte[] result = new byte[position + count];
System.arraycopy(bytes,0,result,0,position);
System.arraycopy(buf,0,result,position,count);
XdevBlob.this.bytes = result;
}
};
}
}
/**
* Returns the byte value at the specified index position.
*
* @param index
* the index to retrieve the byte from.
* @return a byte value
* @throws DBException
* if byte could not be read.
*/
public int getByteAt(int index) throws DBException
{
if(blob != null)
{
try
{
return blob.getBytes(index + 1,1)[0];
}
catch(SQLException e)
{
throw new DBException(null,e);
}
}
else
{
return bytes[index];
}
}
/**
* Retrieves all or part of the BLOB value that this Blob object represents,
* as an array of bytes. This byte array contains up to length consecutive
* bytes starting at position pos.
*
* @param position
* the ordinal position of the first byte in the BLOB value to be
* extracted
* @param length
* the number of consecutive bytes to be copied; the value for
* length must be 0 or greater
* @return a byte array containing up to length consecutive bytes from the
* BLOB value designated by this Blob object, starting with the byte
* at position pos
* @throws DBException
* if there is an error accessing the BLOB value
*/
public byte[] getBytes(int position, int length) throws DBException
{
if(blob != null)
{
try
{
return blob.getBytes(position + 1,length);
}
catch(SQLException e)
{
throw new DBException(null,e);
}
}
else
{
return Arrays.copyOfRange(bytes,position,position + length);
}
}
/**
* Writes the given array of bytes to the BLOB value that this Blob object
* represents, starting at position pos, and returns the number of bytes
* written. The array of bytes will overwrite the existing bytes in the Blob
* object starting at the position pos. If the end of the Blob value is
* reached while writing the array of bytes, then the length of the Blob
* value will be increased to accomodate the extra bytes.
*
* @param position
* the position in the BLOB object at which to start writing
* @param bytes
* the array of bytes to be written to the BLOB value that this
* Blob object represents
*
* @return the number of bytes written
*
* @throws DBException
* if there is an error accessing the BLOB value
*/
public int setBytes(int position, byte[] bytes) throws DBException
{
if(blob != null)
{
try
{
return blob.setBytes(position + 1,bytes);
}
catch(SQLException e)
{
throw new DBException(null,e);
}
}
else
{
System.arraycopy(bytes,0,this.bytes,position,bytes.length);
return bytes.length;
}
}
/**
* Writes all or part of the given byte array to the BLOB value that this
* Blob object represents and returns the number of bytes written. Writing
* starts at position pos in the BLOB value; len bytes from the given byte
* array are written. The array of bytes will overwrite the existing bytes
* in the Blob object starting at the position pos. If the end of the Blob
* value is reached while writing the array of bytes, then the length of the
* Blob value will be increased to accomodate the extra bytes.
*
* @param position
* the position in the BLOB object at which to start writing
* @param bytes
* the array of bytes to be written to this BLOB object
* @param offset
* the offset into the array bytes at which to start reading the
* bytes to be set
* @param length
* the number of bytes to be written to the BLOB value from the
* array of bytes bytes
* @return the number of bytes written
* @throws DBException
* if there is an error accessing the BLOB value
*/
public int setBytes(int position, byte[] bytes, int offset, int length) throws DBException
{
if(blob != null)
{
try
{
return blob.setBytes(position + 1,bytes,offset,length);
}
catch(SQLException e)
{
throw new DBException(null,e);
}
}
else
{
System.arraycopy(bytes,offset,this.bytes,position,length);
return length;
}
}
/**
* Returns the {@link String} represenation for this {@link XdevBlob}
* containing the length of the BLOB.
*
* @return a String represenation for this {@link XdevBlob}.
*/
@Override
public String toString()
{
return "BLOB, length = " + length();
}
/**
* Returns a copied byte array
of this {@link XdevBlob}.
*
* @return a byte array
*/
public byte[] toByteArray()
{
if(blob != null)
{
try
{
return blob.getBytes(1,(int)blob.length());
}
catch(Exception e)
{
return new byte[0];
}
}
else
{
byte[] array = new byte[bytes.length];
System.arraycopy(bytes,0,array,0,bytes.length);
return array;
}
}
/**
* Compares this {@link XdevBlob} with another {@link ByteHolder}.
*
* The length of the {@link ByteHolder} objects is used for this comparison.
*
*
* @param other
* the other {@link ByteHolder}.
* @return
* - 0 - if the length of both objects is equal
* - -1 - if the length of this {@link ByteHolder} is greater
* - 1 - if the length of this {@link ByteHolder} is smaller
*
*/
public int compareTo(ByteHolder other)
{
long thisLength = length();
long otherLength = other.length();
return thisLength < otherLength ? -1 : thisLength == otherLength ? 0 : 1;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj)
{
if(this == obj)
{
return true;
}
if(obj instanceof ByteHolder)
{
ByteHolder other = (ByteHolder)obj;
if(length() == other.length())
{
return Arrays.equals(toByteArray(),other.toByteArray());
}
}
return false;
}
/**
* Returns a copied {@link Blob} for this {@link XdevBlob}.
*
* @return a {@link Blob}
* @throws DBException
* if {@link Blob} could not be created.
*/
public Blob toJDBCBlob() throws DBException
{
if(blob != null)
{
return blob;
}
try
{
return new SerialBlob(bytes);
}
catch(SerialException e)
{
throw new DBException(null,e);
}
catch(SQLException e)
{
throw new DBException(null,e);
}
}
/**
* Generates a hashcode for this {@link XdevBlob}.
*
* @return a hashcode
*/
@Override
public int hashCode()
{
return Arrays.hashCode(toByteArray());
}
private void writeObject(ObjectOutputStream out) throws IOException
{
if(blob != null)
{
try
{
out.writeInt((int)blob.length());
byte[] buffer = new byte[1024];
InputStream in = blob.getBinaryStream();
try
{
int read;
while((read = in.read(buffer)) != -1)
{
out.write(buffer,0,read);
}
}
finally
{
in.close();
}
}
catch(SQLException e)
{
throw new IOException(e);
}
}
else
{
out.writeInt(bytes.length);
out.write(bytes);
}
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
int len = in.readInt();
this.bytes = new byte[len];
in.readFully(this.bytes);
}
}