package com.microsoft.sqlserver.jdbc;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
public final class SQLServerBlob extends SQLServerLob implements java.sql.Blob, java.io.Serializable {
private static final long serialVersionUID = -3526170228097889085L;
private static final String R_CANT_SET_NULL = "R_cantSetNull";
private static final String R_INVALID_POSITION_INDEX = "R_invalidPositionIndex";
private static final String R_INVALID_LENGTH = "R_invalidLength";
private static final Logger _LOGGER = Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerBlob");
private static final AtomicInteger BASE_ID = new AtomicInteger(0);
private byte[] value;
private transient SQLServerConnection con;
private boolean isClosed = false;
ArrayList<Closeable> activeStreams = new ArrayList<>(1);
private final String traceID;
public final String toString() {
return traceID;
}
private static int nextInstanceID() {
return BASE_ID.incrementAndGet();
}
@Deprecated
public SQLServerBlob(SQLServerConnection connection, byte[] data) {
traceID = this.getClass().getSimpleName() + nextInstanceID();
con = connection;
if (null == data)
throw new NullPointerException(SQLServerException.getErrString(R_CANT_SET_NULL));
value = data;
if (_LOGGER.isLoggable(Level.FINE)) {
String loggingInfo = (null != connection) ? connection.toString() : "null connection";
_LOGGER.fine(this.toString() + " created by (" + loggingInfo + ")");
}
}
SQLServerBlob(SQLServerConnection connection) {
traceID = this.getClass().getSimpleName() + nextInstanceID();
con = connection;
value = new byte[0];
if (_LOGGER.isLoggable(Level.FINE))
_LOGGER.fine(this.toString() + " created by (" + connection.toString() + ")");
}
SQLServerBlob(BaseInputStream stream) {
traceID = this.getClass().getSimpleName() + nextInstanceID();
activeStreams.add(stream);
if (_LOGGER.isLoggable(Level.FINE))
_LOGGER.fine(this.toString() + " created by (null connection)");
}
@Override
public void free() throws SQLException {
if (!isClosed) {
if (null != activeStreams) {
for (Closeable stream : activeStreams) {
try {
stream.close();
} catch (IOException ioException) {
_LOGGER.fine(this.toString() + " ignored IOException closing stream " + stream + ": "
+ ioException.getMessage());
}
}
activeStreams = null;
}
value = null;
isClosed = true;
}
}
private void checkClosed() throws SQLServerException {
if (isClosed) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_isFreed"));
SQLServerException.makeFromDriverError(con, null, form.format(new Object[] {"Blob"}), null, true);
}
}
@Override
public InputStream getBinaryStream() throws SQLException {
checkClosed();
if (!delayLoadingLob && null == value && !activeStreams.isEmpty()) {
getBytesFromStream();
}
if (null == value && !activeStreams.isEmpty()) {
InputStream stream = (InputStream) activeStreams.get(0);
try {
stream.reset();
} catch (IOException e) {
throw new SQLServerException(e.getMessage(), null, 0, e);
}
return (InputStream) activeStreams.get(0);
} else {
if (value == null) {
throw new SQLServerException("Unexpected Error: blob value is null while all streams are closed.",
null);
}
return getBinaryStreamInternal(0, value.length);
}
}
@Override
public InputStream getBinaryStream(long pos, long length) throws SQLException {
SQLServerException.throwFeatureNotSupportedException();
return null;
}
private InputStream getBinaryStreamInternal(int pos, int length) {
assert null != value;
assert pos >= 0;
assert 0 <= length && length <= value.length - pos;
assert null != activeStreams;
InputStream getterStream = new ByteArrayInputStream(value, pos, length);
activeStreams.add(getterStream);
return getterStream;
}
@Override
public byte[] getBytes(long pos, int length) throws SQLException {
checkClosed();
getBytesFromStream();
if (pos < 1) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString(R_INVALID_POSITION_INDEX));
Object[] msgArgs = {pos};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
if (length < 0) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString(R_INVALID_LENGTH));
Object[] msgArgs = {length};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
pos--;
if (pos > value.length)
pos = value.length;
if (length > value.length - pos)
length = (int) (value.length - pos);
byte[] bTemp = new byte[length];
System.arraycopy(value, (int) pos, bTemp, 0, length);
return bTemp;
}
@Override
public long length() throws SQLException {
checkClosed();
if (value == null && activeStreams.get(0) instanceof BaseInputStream) {
return (long) ((BaseInputStream) activeStreams.get(0)).payloadLength;
}
getBytesFromStream();
return value.length;
}
@Override
void fillFromStream() throws SQLException {
if (!isClosed) {
getBytesFromStream();
}
}
private void getBytesFromStream() throws SQLServerException {
if (null == value) {
BaseInputStream stream = (BaseInputStream) activeStreams.get(0);
try {
stream.reset();
} catch (IOException e) {
throw new SQLServerException(e.getMessage(), null, 0, e);
}
value = stream.getBytes();
}
}
@Override
public long position(java.sql.Blob pattern, long start) throws SQLException {
checkClosed();
getBytesFromStream();
if (start < 1) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString(R_INVALID_POSITION_INDEX));
Object[] msgArgs = {start};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
if (null == pattern)
return -1;
return position(pattern.getBytes((long) 1, (int) pattern.length()), start);
}
@Override
public long position(byte[] bPattern, long start) throws SQLException {
checkClosed();
getBytesFromStream();
if (start < 1) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString(R_INVALID_POSITION_INDEX));
Object[] msgArgs = {start};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
if (null == bPattern)
return -1;
start--;
for (int pos = (int) start; pos <= value.length - bPattern.length; ++pos) {
boolean match = true;
for (int i = 0; i < bPattern.length; ++i) {
if (value[pos + i] != bPattern[i]) {
match = false;
break;
}
}
if (match) {
return pos + 1L;
}
}
return -1;
}
@Override
public void truncate(long len) throws SQLException {
checkClosed();
getBytesFromStream();
if (len < 0) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString(R_INVALID_LENGTH));
Object[] msgArgs = {len};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
if (value.length > len) {
byte[] bNew = new byte[(int) len];
System.arraycopy(value, 0, bNew, 0, (int) len);
value = bNew;
}
}
@Override
public java.io.OutputStream setBinaryStream(long pos) throws SQLException {
checkClosed();
if (pos < 1) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString(R_INVALID_POSITION_INDEX));
SQLServerException.makeFromDriverError(con, null, form.format(new Object[] {pos}), null, true);
}
return new SQLServerBlobOutputStream(this, pos);
}
@Override
public int setBytes(long pos, byte[] bytes) throws SQLException {
checkClosed();
getBytesFromStream();
if (null == bytes)
SQLServerException.makeFromDriverError(con, null, SQLServerException.getErrString(R_CANT_SET_NULL), null,
true);
return setBytes(pos, bytes, 0, bytes.length);
}
@Override
public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException {
checkClosed();
getBytesFromStream();
if (null == bytes)
SQLServerException.makeFromDriverError(con, null, SQLServerException.getErrString(R_CANT_SET_NULL), null,
true);
if (offset < 0 || offset > bytes.length) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidOffset"));
Object[] msgArgs = {offset};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
if (len < 0 || len > bytes.length - offset) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString(R_INVALID_LENGTH));
Object[] msgArgs = {len};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
if (pos <= 0 || pos > value.length + 1) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString(R_INVALID_POSITION_INDEX));
Object[] msgArgs = {pos};
SQLServerException.makeFromDriverError(con, null, form.format(msgArgs), null, true);
}
pos--;
if (len >= value.length - pos) {
DataTypes.getCheckedLength(con, JDBCType.BLOB, pos + len, false);
byte[] combinedValue = new byte[(int) pos + len];
System.arraycopy(value, 0, combinedValue, 0, (int) pos);
System.arraycopy(bytes, offset, combinedValue, (int) pos, len);
value = combinedValue;
} else {
System.arraycopy(bytes, offset, value, (int) pos, len);
}
return len;
}
}
final class SQLServerBlobOutputStream extends java.io.OutputStream {
private SQLServerBlob parentBlob = null;
private long currentPos;
SQLServerBlobOutputStream(SQLServerBlob parentBlob, long startPos) {
this.parentBlob = parentBlob;
this.currentPos = startPos;
}
@Override
public void write(byte[] b) throws IOException {
if (null == b)
return;
write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
try {
int bytesWritten = parentBlob.setBytes(currentPos, b, off, len);
currentPos += bytesWritten;
} catch (SQLException ex) {
throw new IOException(ex.getMessage());
}
}
@Override
public void write(int b) throws java.io.IOException {
byte[] bTemp = new byte[1];
bTemp[0] = (byte) (b & 0xFF);
write(bTemp, 0, bTemp.length);
}
}