package org.geowebcache.io;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import org.springframework.util.Assert;
public class ByteArrayResource implements Resource, Serializable {
private byte[] data;
private int offset;
private int length;
private long lastModified = System.currentTimeMillis();
/**
* Create a new empty ByteArrayResource
*/
public ByteArrayResource() {
this(null);
}
/**
* Create a new ByteArrayResource from the given byte array.
* @param data The array of bytes. It will be retained by the Resource for storage.
*/
public ByteArrayResource(byte[] data) {
this.data = data;
this.offset = 0;
this.length = data == null ? 0 : data.length;
}
/**
* Create a new ByteArrayResource from the given byte array.
* @param data the array of bytes. It will be retained by the Resource for storage.
* @param offset the beginning of the portion of the array to use as content
* @param length the length of the portion of the array to use as content
*/
public ByteArrayResource(byte[] data, int offset, int length) {
this.data = data;
if (data == null) {
this.data = null;
this.offset = 0;
this.length = 0;
} else {
this.data = data;
Assert.isTrue(offset < data.length);
Assert.isTrue(offset + length <= data.length);
this.offset = offset;
this.length = length;
}
}
/**
* Create a new empty ByteArrayResource with a particular initial capacity.
*/
public ByteArrayResource(final int initialCapacity) {
this(new byte[initialCapacity], 0, 0);
}
/**
* @see org.geowebcache.io.Resource#getLastModified()
*/
public long getLastModified() {
return lastModified;
}
/**
* @see org.geowebcache.io.Resource#getSize()
*/
public long getSize() {
return length;
}
/**
* @see org.geowebcache.io.Resource#transferTo(java.nio.channels.WritableByteChannel)
*/
public long transferTo(WritableByteChannel channel) throws IOException {
if (length > 0) {
ByteBuffer buffer = ByteBuffer.wrap(data, offset, length);
long written = 0;
while ((written += channel.write(buffer)) < length) {
;
}
}
return length;
}
/**
* @see org.geowebcache.io.Resource#transferFrom(java.nio.channels.ReadableByteChannel)
*/
public long transferFrom(ReadableByteChannel channel) throws IOException {
if (channel instanceof FileChannel) {
FileChannel fc = (FileChannel) channel;
offset = 0;
length = (int) fc.size();
if (data == null || data.length < length) {
data = new byte[length];
}
ByteBuffer buffer = ByteBuffer.wrap(data);
int read = 0;
while ((read += channel.read(buffer)) < length) {
;
}
} else {
offset = 0;
length = 0;
if (data == null) {
data = new byte[4096];
}
ByteBuffer buffer = ByteBuffer.wrap(data);
int numRead = 0;
while ((numRead = channel.read(buffer)) > -1) {
length += numRead;
if (buffer.position() == buffer.capacity()) {
int position = buffer.position();
expand();
buffer = ByteBuffer.wrap(data);
buffer.position(position);
}
}
}
return length;
}
/**
* @see org.geowebcache.io.Resource#getInputStream()
*/
public SeekableInputStream getInputStream() throws IOException {
if (data == null) {
throw new IOException("no data");
}
return new SeekableInputStream(this);
}
/**
* Get the contents of the resource.
* @return
*/
public byte[] getContents() {
if (data == null || length == 0) {
return null;
}
if (offset == 0 && data.length == length) {
return data;
}
byte[] buff = new byte[length];
System.arraycopy(data, offset, buff, 0, length);
return buff;
}
/**
* Discard the contents.
*/
public void truncate() {
offset = 0;
length = 0;
}
/**
* @see org.geowebcache.io.Resource#getOutputStream()
*/
public OutputStream getOutputStream() throws IOException {
return new SeekableOutputStream(this);
}
private void expand() {
if (data == null) {
data = new byte[4096];
length = 0;
offset = 0;
} else {
byte[] newdata = new byte[(int) (data.length * 1.5)];
System.arraycopy(data, 0, newdata, 0, data.length);
data = newdata;
}
}
public static final class SeekableInputStream extends ByteArrayInputStream {
private final long lower;
public SeekableInputStream(ByteArrayResource res) {
super(res.data == null ? new byte[0] : res.data, res.offset, res.length);
lower = res.offset;
}
public void seek(long pos) throws IOException {
if (pos < 0 || pos > super.count) {
throw new IOException("Can't seek to pos " + pos + ". buffer is 0.."
+ (super.count - 1));
}
super.pos = (int) (lower + pos);
}
public int length() {
return super.count;
}
}
public static final class SeekableOutputStream extends OutputStream {
private ByteArrayResource res;
private int remaining;
public SeekableOutputStream(ByteArrayResource res) {
this.res = res;
remaining = res.data == null ? 0 : res.data.length;
}
@Override
public void write(int b) throws IOException {
if (remaining == 0) {
res.expand();
remaining = res.data.length - res.length;
}
res.data[res.length++] = (byte) b;
--remaining;
}
@Override
public void write(byte buff[], int off, int len) throws IOException {
if (remaining < len) {
while (remaining < len) {
this.res.expand();
remaining = res.data.length - res.length;
}
}
System.arraycopy(buff, off, res.data, res.length, len);
res.length += len;
remaining -= len;
}
}
}