com.ibm.commons.util.io.CharacterStreamCache Maven / Gradle / Ivy
The newest version!
/*
* © Copyright IBM Corp. 2012-2013
*
* Licensed 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 com.ibm.commons.util.io;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
/**
* Characters buffer.
*
* This class is used to store characters and provides both an Writer for storing the
* bytes and an Reader for reading them. It stores the characters in memory in a highly
* optimized way, with a possibility of using a backing file when the content is bigger
* than a predefined threshold.
*
* @ibm-api
*/
public class CharacterStreamCache {
public static final int DEFAULT_BLOCKSIZE = 8192; // Size of a data block
public static final int DEFAULT_THRESHOLD = 32; // Number of memory blocks before using a file
private int blockSize;
private int threshold;
private int nBlock;
private Block firstBlock;
private Block lastBlock;
private static class Block {
Block next;
int count;
char[] data;
Block(int blockSize) {
data = new char[blockSize];
}
}
/** @ibm-api */
public CharacterStreamCache() {
this.blockSize = DEFAULT_BLOCKSIZE;
this.threshold = DEFAULT_THRESHOLD;
this.nBlock = 1;
firstBlock = lastBlock = new Block(blockSize);
}
/** @ibm-api */
public CharacterStreamCache(int blockSize, int threshold) {
this.blockSize = blockSize;
this.threshold = threshold;
this.nBlock = 1;
firstBlock = lastBlock = new Block(blockSize);
}
/** @ibm-api */
public boolean isEqual(CharacterStreamCache other) {
if(blockSize==other.blockSize) {
long l1 = getLength();
long l2 = other.getLength();
if(l1==l2) {
Block b1 = firstBlock;
Block b2 = other.firstBlock;
while(b1!=null && b2!=null) {
int count = b1.count;
for(int i=0; iblockSize ) {
b = b.next;
pos -= blockSize;
}
return b.data[pos];
}
/**
* Clear the data inside the cache.
* @ibm-api
*/
public void clear() {
this.nBlock = 1;
firstBlock = lastBlock = new Block(blockSize);
}
/**
* Get a reader on the stored data.
* @ibm-api
*/
public Reader getReader() {
return new InternalReader();
}
/**
* Get a writer to store the data.
* @ibm-api
*/
public Writer getWriter() {
return new InternalWriter();
}
/**
* Init the data from an existing reader.
* @ibm-api
*/
public void copyFrom( Reader r ) throws java.io.IOException {
// Fill the entire block
while(true) {
if( lastBlock.count==blockSize ) {
addNewBlock();
}
int read = r.read(lastBlock.data,lastBlock.count,blockSize-lastBlock.count);
if( read<0 ) {
return; // The source stream end is reached
}
lastBlock.count += read;
}
}
/**
* Copy the stream to another stream.
* @ibm-api
*/
public void copyTo(Writer w) throws java.io.IOException {
for( Block b=firstBlock; b!=null; b=b.next ) {
w.write(b.data, 0, b.count);
}
}
/**
* Copy the stream to a file.
* @ibm-api
*/
public void copyTo(File f) throws java.io.IOException {
Writer w = new BufferedWriter(new FileWriter(f));
try {
copyTo(w);
} finally {
w.close();
}
}
/**
* Convert to a char array.
* WARN: this fct is *not* really optimized!
* @ibm-api
*/
public char[] toCharArray() {
int length = (int)getLength();
char[] result = new char[length];
int pos = 0;
for( Block b=firstBlock; b!=null; b=b.next ) {
System.arraycopy( b.data, 0, result, pos, b.count );
pos += b.count;
}
return result;
}
public String toString() {
return new String(toCharArray());
}
/**
* Add a new block to the list.
*/
private final void addNewBlock() {
Block b = new Block(blockSize);
lastBlock.next = b;
lastBlock = b;
nBlock++;
// TODO: manage the persistence to a temporary file when the threshold is reached!
}
protected class InternalReader extends Reader {
private int currentPosition;
private Block currentBlock = firstBlock;
public void close() throws java.io.IOException {
}
public int read() throws IOException {
if( currentBlock!=null ) {
if( currentPosition>=currentBlock.count ) {
if( !nextBlock() || currentBlock.count==0 ) {
return -1;
}
}
return (int)currentBlock.data[currentPosition++] & 0xffff;
}
return -1;
}
public int read(char b[], int off, int len) throws IOException {
if( currentBlock!=null ) {
if( currentPosition>=currentBlock.count ) {
if( !nextBlock() || currentBlock.count==0 ) {
return -1;
}
}
int read = Math.min( len, currentBlock.count-currentPosition );
System.arraycopy( currentBlock.data, currentPosition, b, off, read );
currentPosition += read;
return read;
}
return -1;
}
private boolean nextBlock() {
if( currentBlock!=null ) {
currentBlock = currentBlock.next;
currentPosition = 0;
}
return currentBlock!=null;
}
public long skip(long n) throws IOException {
if( currentBlock!=null ) {
if( currentPosition>=currentBlock.count ) {
if( !nextBlock() ) {
return -1;
}
}
int toSkip = Math.min( (int)n, currentBlock.count-currentPosition );
currentPosition += toSkip;
return toSkip;
}
return -1; // EOF
}
public int available() throws IOException {
if( currentBlock!=null ) {
if ( currentBlock.count-currentPosition > 0 ){
return currentBlock.count-currentPosition;
}
//Check the next block
return currentBlock.next == null ? 0 : currentBlock.count;
}
return 0;
}
}
protected class InternalWriter extends Writer {
public void write(int b) throws IOException {
CharacterStreamCache.this.write(b);
}
public void write(char b[], int off, int len) throws IOException {
CharacterStreamCache.this.write(b,off,len);
}
public void flush() throws IOException {
// Nothing here...
}
public void close() throws IOException {
// Nothing here...
}
}
public final void write(int b) throws IOException {
if( lastBlock.count==blockSize ) {
addNewBlock();
}
lastBlock.data[lastBlock.count++] = (char)b;
}
public final void write(char b[]) throws IOException {
write(b, 0, b.length);
}
public final void write(char b[], int off, int len) throws IOException {
while(len>0 ) {
int max = Math.min( len, blockSize-lastBlock.count );
if( max==0 ) {
addNewBlock();
max = Math.min( len, blockSize );
}
System.arraycopy(b,off,lastBlock.data,lastBlock.count,max);
lastBlock.count += max;
len-=max; off+=max;
}
}
}