org.apache.cayenne.util.MemoryBlob Maven / Gradle / Ivy
Show all versions of cayenne-client-nodeps
/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
****************************************************************/
package org.apache.cayenne.util;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.SQLException;
import org.apache.cayenne.CayenneRuntimeException;
/**
* A Blob implementation that stores content in memory.
*
* This implementation is based on jdbcBlob from HSQLDB (copyright HSQL Development
* Group).
*
*
* @since 1.2
* @author Andrus Adamchik, based on HSQLDB jdbcBlob.
*/
public class MemoryBlob implements Blob {
volatile byte[] data;
public MemoryBlob() {
this(new byte[0]);
}
/**
* Constructs a new MemoryBlob instance wrapping the given octet sequence.
*
* @param data the octet sequence representing the Blob value
* @throws CayenneRuntimeException if the argument is null
*/
public MemoryBlob(byte[] data) {
if (data == null) {
throw new CayenneRuntimeException("Null data");
}
this.data = data;
}
/**
* Returns the number of bytes in the BLOB
value designated by this
* Blob
object.
*
* @return length of the BLOB
in bytes
* @exception SQLException if there is an error accessing the length of the
* BLOB
*/
public long length() throws SQLException {
return data.length;
}
/**
* 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
.
*
* The official specification is ambiguous in that it does not precisely indicate the
* policy to be observed when pos > this.length() - length. One policy would be to
* retrieve the octets from pos to this.length(). Another would be to throw an
* exception. This implementation observes the later policy.
*
* @param pos the ordinal position of the first byte in the BLOB
value
* to be extracted; the first byte is at position 1
* @param length the number of consecutive bytes to be copied
* @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
* @exception SQLException if there is an error accessing the BLOB
* value
*/
public byte[] getBytes(long pos, final int length) throws SQLException {
final byte[] ldata = data;
final int dlen = ldata.length;
pos--;
if (pos < 0 || pos > dlen) {
throw new SQLException("Invalid pos: " + (pos + 1));
}
if (length < 0 || length > dlen - pos) {
throw new SQLException("length: " + length);
}
final byte[] out = new byte[length];
System.arraycopy(ldata, (int) pos, out, 0, length);
return out;
}
/**
* Retrieves the BLOB
value designated by this Blob
* instance as a stream.
*
* @return a stream containing the BLOB
data
* @exception SQLException if there is an error accessing the BLOB
* value
*/
public InputStream getBinaryStream() throws SQLException {
return new ByteArrayInputStream(data);
}
/**
* Retrieves the byte position at which the specified byte array pattern
* begins within the BLOB
value that this Blob
object
* represents. The search for pattern
begins at position
* start
.
*
*
* @param pattern the byte array for which to search
* @param start the position at which to begin searching; the first position is 1
* @return the position at which the pattern appears, else -1
* @exception SQLException if there is an error accessing the BLOB
*/
public long position(final byte[] pattern, long start) throws SQLException {
final byte[] ldata = data;
final int dlen = ldata.length;
if (start > dlen || pattern == null) {
return -1;
}
else if (start < 1) {
start = 0;
}
else {
start--;
}
final int plen = pattern.length;
if (plen == 0 || start > dlen - plen) {
return -1;
}
final int stop = dlen - plen;
final byte b0 = pattern[0];
outer_loop: for (int i = (int) start; i <= stop; i++) {
if (ldata[i] != b0) {
continue;
}
int len = plen;
int doffset = i;
int poffset = 0;
while (len-- > 0) {
if (ldata[doffset++] != pattern[poffset++]) {
continue outer_loop;
}
}
return i + 1;
}
return -1;
}
/**
* Retrieves the byte position in the BLOB
value designated by this
* Blob
object at which pattern
begins. The search
* begins at position start
.
*
* @param pattern the Blob
object designating the BLOB
* value for which to search
* @param start the position in the BLOB
value at which to begin
* searching; the first position is 1
* @return the position at which the pattern begins, else -1
* @exception SQLException if there is an error accessing the BLOB
* value
*/
public long position(final Blob pattern, long start) throws SQLException {
final byte[] ldata = data;
final int dlen = ldata.length;
if (start > dlen || pattern == null) {
return -1;
}
else if (start < 1) {
start = 0;
}
else {
start--;
}
final long plen = pattern.length();
if (plen == 0 || start > dlen - plen) {
return -1;
}
// by now, we know plen <= Integer.MAX_VALUE
final int iplen = (int) plen;
byte[] bap;
if (pattern instanceof MemoryBlob) {
bap = ((MemoryBlob) pattern).data;
}
else {
bap = pattern.getBytes(1, iplen);
}
final int stop = dlen - iplen;
final byte b0 = bap[0];
outer_loop: for (int i = (int) start; i <= stop; i++) {
if (ldata[i] != b0) {
continue;
}
int len = iplen;
int doffset = i;
int poffset = 0;
while (len-- > 0) {
if (ldata[doffset++] != bap[poffset++]) {
continue outer_loop;
}
}
return i + 1;
}
return -1;
}
/**
* Always throws an exception.
*/
public int setBytes(long pos, byte[] bytes) throws SQLException {
throw new SQLException("Not supported");
}
/**
* Always throws an exception.
*/
public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException {
throw new SQLException("Not supported");
}
/**
* Always throws an exception.
*/
public OutputStream setBinaryStream(long pos) throws SQLException {
throw new SQLException("Not supported");
}
/**
* Truncates the BLOB
value that this Blob
object
* represents to be len
bytes in length.
*
* @param len the length, in bytes, to which the BLOB
value that this
* Blob
object represents should be truncated
* @exception SQLException if there is an error accessing the BLOB
* value
*/
public void truncate(final long len) throws SQLException {
final byte[] ldata = data;
if (len < 0 || len > ldata.length) {
throw new SQLException("Invalid length: " + Long.toString(len));
}
if (len == ldata.length) {
return;
}
byte[] newData = new byte[(int) len];
System.arraycopy(ldata, 0, newData, 0, (int) len);
data = newData;
}
}