/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library 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 library 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 library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.collections.queues.io; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import xxl.core.collections.containers.Container; import xxl.core.collections.containers.io.BlockFileContainer; import xxl.core.collections.queues.FIFOQueue; import xxl.core.collections.queues.Queue; import xxl.core.cursors.sorters.MergeSorter; import xxl.core.functions.AbstractFunction; import xxl.core.functions.Constant; import xxl.core.functions.Function; import xxl.core.io.Block; import xxl.core.io.Convertable; import xxl.core.io.converters.BooleanConverter; import xxl.core.io.converters.ConvertableConverter; import xxl.core.io.converters.Converter; import xxl.core.io.converters.Converters; import xxl.core.io.converters.IntegerConverter; import xxl.core.io.converters.ShortConverter; import xxl.core.util.WrappingRuntimeException; /** * This class provides a queue that uses a container for storing blocks of * serialized data. The <tt>peek</tt> method is not implemented by the block * based queue. A converter is used for serializing and de-serializing the * elements of the queue.<p> * * The block based queue serializes its elements and stores blocks of * this serialized data in a container. In order to keep the order of this * block, the id of the <i>next</i> block is stored at the end of each * block.<p> * * Example usage (1). * <pre> * // create a new block based queue with ... * * BlockBasedQueue queue = new BlockBasedQueue( * * // a map container for storing the serialized elements * * new MapContainer(), * * // a block size of 20 bytes * * 20, * * // an integer converter * * IntegerConverter.DEFAULT_INSTANCE, * * // an input buffer of 0 bytes * * new Constant(0), * * // an output buffer of 4 bytes (size of a serialized integer) * * new Constant(4) * ); * * // open the queue * * queue.open(); * * // create an iteration over 20 random Integers (between 0 and 100) * * Iterator iterator = new Enumerator(20); * * // insert all elements of the given iterator * * for (; iterator.hasNext(); queue.enqueue(iterator.next())); * * // print all elements of the queue * * while (!queue.isEmpty()) * System.out.println(queue.dequeue()); * * System.out.println(); * * // close the open queue after use * * queue.close(); * </pre> * * @see xxl.core.collections.queues.Queue * @see xxl.core.collections.queues.io.StreamQueue * @see xxl.core.collections.queues.FIFOQueue * @see xxl.core.collections.containers.Container */ public class BlockBasedQueue extends StreamQueue implements Convertable, FIFOQueue { /** * A factory method to create a new BlockBasedQueue (see contract for * {@link xxl.core.collections.queues.Queue#FACTORY_METHOD FACTORY_METHOD} in * interface Queue). In contradiction to the contract in Queue it may * only be invoked with a <i>parameter list</i> (for further * details see Function) of objects. The <i>parameter list</i> * will be used for initializing the random access file queue by * calling the constructor * <pre> * BlockBasedQueue((Container) list.get(0), * 256, * (Function) list.get(1), * new Constant(0), * new Constant(0)) * </pre> * * @see Function */ public static final Function<Object,BlockBasedQueue> FACTORY_METHOD = new AbstractFunction<Object,BlockBasedQueue>() { public BlockBasedQueue invoke(List<? extends Object> list) { return new BlockBasedQueue( (Container)list.get(0), 256, (Function)list.get(1), new Constant(0), new Constant(0) ); } }; /** * This function can be used for initialization of external sorting. * * This constructor has a parameter function, which is a factory function for storing intermediate runs. * {@link MergeSorter#MergeSorter(java.util.Iterator, java.util.Comparator, int, int, int, Function, boolean)} * * * {@link MergeSorter} * @param queueContainer container e.g. {@link BlockFileContainer} * @param blockSize in bytes e.g. 4096 * @param converter for data serializing * @return */ public static <T> Function<Function<?, Integer>, Queue<T>> createBlockBasedQueueFunctionForMergeSorter(final Container queueContainer, final int blockSize, final Converter<T> converter){ return new AbstractFunction<Function<?, Integer>, Queue<T>>() { public Queue<T> invoke(Function<?, Integer> function1, Function<?, Integer> function2) { return new BlockBasedQueue(queueContainer, blockSize, converter, function1, function2); } }; } /** * The container is internally used for storing the blocks that * contains the serialized elements of the queue. */ protected Container container; /** * The size of a block stored in the container. Every block stored in * the container contains <tt>blockSize</tt> bytes of serialized data. */ protected int blockSize; /** * The number of bytes stored by blocks in the container. */ protected int bytes; /** * The id of <tt>readBlock</tt>, i.e. the block the queue currently * reads from. */ protected Object readBlockId; /** * The offset of <tt>readBlock</tt>, i.e. the block the queue currently * reads from. */ protected int readBlockOffset; /** * The id of <tt>writeBlock</tt>, i.e. the block the queue currently * writes to. */ protected Object writeBlockId; /** * The offset of <tt>writeBlock</tt>, i.e. the block the queue currently * writes to. */ protected int writeBlockOffset; /** * The block the queue currently reads from. */ protected Block readBlock = null; /** * The block the queue currently writes to. */ protected Block writeBlock = null; /** * A dummy block that is used for getting a new id when a new write * block is created. When creating a new write block, the dummy block * is inserted into the container in order to get a valid id for the * write block. When the creation of the write block is finished, the * the dummy block is updated by the write block. */ protected Block dummyBlock = new Block(new byte[0], 0, 0); /** * The output stream is used for writing serialized elements to the * queue. It is set to an output stream, that writes given bytes to * the current write block. When the current write block is full, a new * block is inserted into the container. In order to be able to * determine the next block, when reading the serialized data, the id * of the new block is stored at the end of the old write block. The * output stream implicitly adjusts the number of bytes contained by * the queue. */ protected OutputStream outputStream = new OutputStream() { public void write(int b) throws IOException { if (bytes > 0 && writeBlock == null) writeBlock = (readBlock != null && writeBlockId.equals(readBlockId)) ? readBlock : (Block)container.get(writeBlockId, false); if (writeBlock == null || writeBlockOffset == blockSize) { Block newWriteBlock = new Block(new byte[blockSize], 0, blockSize); Object newWriteBlockId = container.insert(dummyBlock, false); if (writeBlock != null) { byte[] byteArray = Converters.toByteArray(container.objectIdConverter(), newWriteBlockId); System.arraycopy( writeBlock.array, writeBlock.offset+blockSize-byteArray.length, newWriteBlock.array, ShortConverter.SIZE, byteArray.length ); if (writeBlockId.equals(readBlockId) && readBlockOffset >= blockSize-byteArray.length) { container.remove(readBlockId); readBlockId = newWriteBlockId; readBlockOffset -= blockSize-byteArray.length-ShortConverter.SIZE; readBlock = newWriteBlock; } else { writeBlock.dataOutputStream().writeShort((short)byteArray.length); System.arraycopy( byteArray, 0, writeBlock.array, writeBlock.offset+blockSize-byteArray.length, byteArray.length ); container.update(writeBlockId, writeBlock); } writeBlockOffset = ShortConverter.SIZE+byteArray.length; } else { readBlockId = newWriteBlockId; writeBlockOffset = ShortConverter.SIZE; } writeBlockId = newWriteBlockId; writeBlock = newWriteBlock; } writeBlock.set(writeBlockOffset++, (byte)b); bytes++; } public void close() { if (writeBlock != null) { container.update(writeBlockId, writeBlock); writeBlock = null; if (readBlockId == null || !readBlockId.equals(writeBlockId)) container.unfix(writeBlockId); } } }; /** * Constructs a new block based queue containing the elements that are * stored in blocks of the specified size in the given container. The * specified converter is used for serializing and de-serializing the * elements of the queue and the given factory method is used for * initializing the elements of the queue before the blocks are read * out. The specified functions determine the size of the input buffer * and the output buffer. The given <tt>int</tt> value <tt>size</tt> * and <tt>bytes</tt> determines what is the initial size of the queue * and how many bytes are needed for serializing its elements. * <tt>readBlockId</tt>, <tt>readBlockOffset</tt>, * <tt>writeBlockId</tt> and <tt>writeBlockOffset</tt> specifies the * id and the offset of the actual read and write block. * * @param container the container that is used for storing the blocks * of serialized data. The serialized objects contained by this * container can be used as initial elements of the queue. * @param blockSize the size of a block that is used for storing the * serialized elements of the queue. * @param converter the converter that is used for serializing and * de-serializing the elements of the queue. * @param newObject a factory method that is invoked in order to * initialize the elements of the queue, before a block is read * out. * @param inputBufferSize a function that determines the size of the * input buffer. * @param outputBufferSize a function that determines the size of the * output buffer. * @param size the initial size of the queue. * @param bytes the number of bytes needed for storing the initial * elements of the queue. * @param readBlockId the id of the actual block to read from. * @param readBlockOffset the offset of the actual block to read from. * @param writeBlockId the id of the actual block to write to. * @param writeBlockOffset the offset of the actual block to write to. */ public BlockBasedQueue(Container container, int blockSize, Converter converter, Function newObject, Function inputBufferSize, Function outputBufferSize, int size, int bytes, Object readBlockId, int readBlockOffset, Object writeBlockId, int writeBlockOffset) { super(converter, newObject, inputBufferSize, outputBufferSize, null, null); this.container = container; this.blockSize = blockSize; this.size = size; this.bytes = bytes; this.readBlockId = readBlockId; this.readBlockOffset = readBlockOffset; this.writeBlockId = writeBlockId; this.writeBlockOffset = writeBlockOffset; this.newInputStream = new AbstractFunction(){ public Object invoke(){ return new InputStream() { public int read() throws IOException { if (BlockBasedQueue.this.bytes == 0) return -1; else { int b; if (readBlock == null) readBlock = (writeBlock != null && BlockBasedQueue.this.readBlockId.equals(BlockBasedQueue.this.writeBlockId)) ? writeBlock : (Block)BlockBasedQueue.this.container.get(BlockBasedQueue.this.readBlockId, false); b = readBlock.get(BlockBasedQueue.this.readBlockOffset++)&255; if (--BlockBasedQueue.this.bytes == 0) { BlockBasedQueue.this.container.remove(BlockBasedQueue.this.readBlockId); readBlock = null; BlockBasedQueue.this.readBlockOffset = ShortConverter.SIZE; writeBlock = null; BlockBasedQueue.this.writeBlockOffset = ShortConverter.SIZE; } else if (BlockBasedQueue.this.readBlockOffset == BlockBasedQueue.this.blockSize-readBlock.dataInputStream().readShort()) removeBlock(); return b; } } public int available() { return BlockBasedQueue.this.bytes; } public void close() { if (readBlock != null) { readBlock = null; if (BlockBasedQueue.this.writeBlockId == null || !BlockBasedQueue.this.readBlockId.equals(BlockBasedQueue.this.writeBlockId)) BlockBasedQueue.this.container.unfix(BlockBasedQueue.this.readBlockId); } } }; } }; this.newOutputStream = new AbstractFunction(){ public Object invoke(){ return new OutputStream() { public void write(int b) throws IOException { if (BlockBasedQueue.this.bytes > 0 && writeBlock == null) writeBlock = (readBlock != null && BlockBasedQueue.this.writeBlockId.equals(BlockBasedQueue.this.readBlockId)) ? readBlock : (Block)BlockBasedQueue.this.container.get(BlockBasedQueue.this.writeBlockId, false); if (writeBlock == null || BlockBasedQueue.this.writeBlockOffset == BlockBasedQueue.this.blockSize) { Block newWriteBlock = new Block(new byte[BlockBasedQueue.this.blockSize], 0, BlockBasedQueue.this.blockSize); Object newWriteBlockId = BlockBasedQueue.this.container.insert(dummyBlock, false); if (writeBlock != null) { byte[] byteArray = Converters.toByteArray(BlockBasedQueue.this.container.objectIdConverter(), newWriteBlockId); System.arraycopy( writeBlock.array, writeBlock.offset+BlockBasedQueue.this.blockSize-byteArray.length, newWriteBlock.array, ShortConverter.SIZE, byteArray.length ); if (BlockBasedQueue.this.writeBlockId.equals(BlockBasedQueue.this.readBlockId) && BlockBasedQueue.this.readBlockOffset >= BlockBasedQueue.this.blockSize-byteArray.length) { BlockBasedQueue.this.container.remove(BlockBasedQueue.this.readBlockId); BlockBasedQueue.this.readBlockId = newWriteBlockId; BlockBasedQueue.this.readBlockOffset -= BlockBasedQueue.this.blockSize-byteArray.length-ShortConverter.SIZE; readBlock = newWriteBlock; } else { writeBlock.dataOutputStream().writeShort((short)byteArray.length); System.arraycopy( byteArray, 0, writeBlock.array, writeBlock.offset+BlockBasedQueue.this.blockSize-byteArray.length, byteArray.length ); BlockBasedQueue.this.container.update(BlockBasedQueue.this.writeBlockId, writeBlock); } BlockBasedQueue.this.writeBlockOffset = ShortConverter.SIZE+byteArray.length; } else { BlockBasedQueue.this.readBlockId = newWriteBlockId; BlockBasedQueue.this.writeBlockOffset = ShortConverter.SIZE; } BlockBasedQueue.this.writeBlockId = newWriteBlockId; writeBlock = newWriteBlock; } writeBlock.set(BlockBasedQueue.this.writeBlockOffset++, (byte)b); BlockBasedQueue.this.bytes++; } public void close() { if (writeBlock != null) { BlockBasedQueue.this.container.update(BlockBasedQueue.this.writeBlockId, writeBlock); writeBlock = null; if (BlockBasedQueue.this.readBlockId == null || !BlockBasedQueue.this.readBlockId.equals(BlockBasedQueue.this.writeBlockId)) BlockBasedQueue.this.container.unfix(BlockBasedQueue.this.writeBlockId); } } }; } }; } /** * Constructs a new block based queue containing the elements that are * stored in blocks of the specified size in the given container. The * specified converter is used for serializing and de-serializing the * elements of the queue. The specified functions determine the size * of the input buffer and the output buffer. The given <tt>int</tt> * value <tt>size</tt> and <tt>bytes</tt> determines what is the * initial size of the queue and how many bytes are needed for * serializing its elements. <tt>readBlockId</tt>, * <tt>readBlockOffset</tt>, <tt>writeBlockId</tt> and * <tt>writeBlockOffset</tt> specifies the id and the offset of the * actual read and write block.<br> * This constructor is equivalent to the call of * <code>BlockBasedQueue(container, blockSize, converter, new Constant(null), inputBufferSize, outputBufferSize, size, bytes, readBlockId, readBlockOffset, writeBlockId, writeBlockOffset)</code> * * @param container the container that is used for storing the blocks * of serialized data. The serialized objects contained by this * container can be used as initial elements of the queue. * @param blockSize the size of a block that is used for storing the * serialized elements of the queue. * @param converter the converter that is used for serializing and * de-serializing the elements of the queue. * @param inputBufferSize a function that determines the size of the * input buffer. * @param outputBufferSize a function that determines the size of the * output buffer. * @param size the initial size of the queue. * @param bytes the number of bytes needed for storing the initial * elements of the queue. * @param readBlockId the id of the actual block to read from. * @param readBlockOffset the offset of the actual block to read from. * @param writeBlockId the id of the actual block to write to. * @param writeBlockOffset the offset of the actual block to write to. */ public BlockBasedQueue(Container container, int blockSize, Converter converter, Function inputBufferSize, Function outputBufferSize, int size, int bytes, Object readBlockId, int readBlockOffset, Object writeBlockId, int writeBlockOffset) { this(container, blockSize, converter, new Constant(null), inputBufferSize, outputBufferSize, size, bytes, readBlockId, readBlockOffset, writeBlockId, writeBlockOffset); } /** * Constructs a new block based queue containing the elements that are * stored in blocks of the specified size in the given container. A * convertable converter is used for serializing and de-serializing * the elements of the queue and the given factory method is used for * initializing the elements of the queue before the blocks are read * out. The specified functions determine the size of the input buffer * and the output buffer. The given <tt>int</tt> value <tt>size</tt> * and <tt>bytes</tt> determines what is the initial size of the queue * and how many bytes are needed for serializing its elements. * <tt>readBlockId</tt>, <tt>readBlockOffset</tt>, * <tt>writeBlockId</tt> and <tt>writeBlockOffset</tt> specifies the * id and the offset of the actual read and write block.<br> * This constructor is equivalent to the call of * <code>BlockBasedQueue(container, blockSize, ConvertableConverter.DEFAULT_INSTANCE, new Constant(null), inputBufferSize, outputBufferSize, size, bytes, readBlockId, readBlockOffset, writeBlockId, writeBlockOffset)</code> * * @param container the container that is used for storing the blocks * of serialized data. The serialized objects contained by this * container can be used as initial elements of the queue. * @param blockSize the size of a block that is used for storing the * serialized elements of the queue. * @param newObject a factory method that is invoked in order to * initialize the elements of the queue, before a block is read * out. * @param inputBufferSize a function that determines the size of the * input buffer. * @param outputBufferSize a function that determines the size of the * output buffer. * @param size the initial size of the queue. * @param bytes the number of bytes needed for storing the initial * elements of the queue. * @param readBlockId the id of the actual block to read from. * @param readBlockOffset the offset of the actual block to read from. * @param writeBlockId the id of the actual block to write to. * @param writeBlockOffset the offset of the actual block to write to. */ public BlockBasedQueue(Container container, int blockSize, Function newObject, Function inputBufferSize, Function outputBufferSize, int size, int bytes, Object readBlockId, int readBlockOffset, Object writeBlockId, int writeBlockOffset) { this(container, blockSize, ConvertableConverter.DEFAULT_INSTANCE, newObject, inputBufferSize, outputBufferSize, size, bytes, readBlockId, readBlockOffset, writeBlockId, writeBlockOffset); } /** * Constructs a new block based queue containing the elements that are * stored in blocks of the specified size in the given container (see * methods <tt>read</tt> and <tt>write</tt> for further detail). The * specified converter is used for serializing and de-serializing the * elements of the queue and the given factory method is used for * initializing the elements of the queue before the blocks are read * out. The specified functions determine the size of the input buffer * and the output buffer.<br> * This constructor is equivalent to the call of * <code>BlockBasedQueue(container, blockSize, converter, newObject, inputBufferSize, outputBufferSize, 0, 0, null, ShortConverter.SIZE, null, ShortConverter.SIZE)</code>. * * @param container the container that is used for storing the blocks * of serialized data. The serialized objects contained by this * container can be used as initial elements of the queue. * @param blockSize the size of a block that is used for storing the * serialized elements of the queue. * @param converter the converter that is used for serializing and * de-serializing the elements of the queue. * @param newObject a factory method that is invoked in order to * initialize the elements of the queue, before a block is read * out. * @param inputBufferSize a function that determines the size of the * input buffer. * @param outputBufferSize a function that determines the size of the * output buffer. */ public BlockBasedQueue(Container container, int blockSize, Converter converter, Function newObject, Function inputBufferSize, Function outputBufferSize) { this(container, blockSize, converter, newObject, inputBufferSize, outputBufferSize, 0, 0, null, ShortConverter.SIZE, null, ShortConverter.SIZE); } /** * Constructs a new block based queue containing the elements that are * stored in blocks of the specified size in the given container (see * methods <tt>read</tt> and <tt>write</tt> for further detail). The * specified converter is used for serializing and de-serializing the * elements of the queue. The specified functions determine the size * of the input buffer and the output buffer.<br> * This constructor is equivalent to the call of * <code>BlockBasedQueue(container, blockSize, converter, new Constant(null), inputBufferSize, outputBufferSize)</code>. * * @param container the container that is used for storing the blocks * of serialized data. The serialized objects contained by this * container can be used as initial elements of the queue. * @param blockSize the size of a block that is used for storing the * serialized elements of the queue. * @param converter the converter that is used for serializing and * de-serializing the elements of the queue. * @param inputBufferSize a function that determines the size of the * input buffer. * @param outputBufferSize a function that determines the size of the * output buffer. */ public BlockBasedQueue(Container container, int blockSize, Converter converter, Function inputBufferSize, Function outputBufferSize) { this(container, blockSize, converter, new Constant(null), inputBufferSize, outputBufferSize); } /** * Constructs a new block based queue containing the elements that are * stored in blocks of the specified size in the given container (see * methods <tt>read</tt> and <tt>write</tt> for further detail). A * Convertable converter is used for serializing and de-serializing * the elements of the queue and the given factory method is used for * initializing the elements of the queue before the blocks are read * out. The specified functions determine the size of the input buffer * and the output buffer.<br> * This constructor is equivalent to the call of * <code>BlockBasedQueue(container, blockSize, ConvertableConverter.DEFAULT_INSTANCE, newObject, inputBufferSize, outputBufferSize)</code>. * * @param container the container that is used for storing the blocks * of serialized data. The serialized objects contained by this * container can be used as initial elements of the queue. * @param blockSize the size of a block that is used for storing the * serialized elements of the queue. * @param newObject a factory method that is invoked in order to * initialize the elements of the queue, before a block is read * out. * @param inputBufferSize a function that determines the size of the * input buffer. * @param outputBufferSize a function that determines the size of the * output buffer. */ public BlockBasedQueue (Container container, int blockSize, Function newObject, Function inputBufferSize, Function outputBufferSize) { this(container, blockSize, ConvertableConverter.DEFAULT_INSTANCE, newObject, inputBufferSize, outputBufferSize); } /** * Reads the state of this block based queue from a data input. This * method can be used for initializing the queue with the elements * that are stored in blocks of bytes in the container without using * the corresponding constructor (you know, the one with twelve * parameters).<br> * This implementation reads the fields <tt>size</tt>, <tt>bytes</tt>, * <tt>readBlockId</tt>, <tt>readBlockOffset</tt>, * <tt>writeBlockId</tt> and <tt>writeBlockOffset</tt> from the given * data input. * * @param dataInput the data input to read from the state of this queue. * @throws IOException if an I/O error occurs. */ public void read(DataInput dataInput) throws IOException { size = IntegerConverter.DEFAULT_INSTANCE.readInt(dataInput); bytes = IntegerConverter.DEFAULT_INSTANCE.readInt(dataInput); readBlockId = BooleanConverter.DEFAULT_INSTANCE.readBoolean(dataInput) ? container.objectIdConverter().read(dataInput) : null; readBlockOffset = IntegerConverter.DEFAULT_INSTANCE.readInt(dataInput); writeBlockId = BooleanConverter.DEFAULT_INSTANCE.readBoolean(dataInput) ? container.objectIdConverter().read(dataInput) : null; writeBlockOffset = IntegerConverter.DEFAULT_INSTANCE.readInt(dataInput); } /** * Writes the actual state of this block based queue to a data output. * This method can be used for serializing the actual state of the * queue in order to use it again in another session without calling * the corresponding constructor (you know, the one with twelve * parameters).<br> * This implementation writes the fields <tt>size</tt>, * <tt>bytes</tt>, <tt>readBlockId</tt>, <tt>readBlockOffset</tt>, * <tt>writeBlockId</tt> and <tt>writeBlockOffset</tt> to the given * data output. * * @param dataOutput the data output to write to the state of this queue. * @throws IOException if an I/O error occurs. */ public void write(DataOutput dataOutput) throws IOException { IntegerConverter.DEFAULT_INSTANCE.writeInt(dataOutput, size); IntegerConverter.DEFAULT_INSTANCE.writeInt(dataOutput, bytes); BooleanConverter.DEFAULT_INSTANCE.writeBoolean(dataOutput, readBlockId!=null); if (readBlockId!=null) container.objectIdConverter().write(dataOutput, readBlockId); IntegerConverter.DEFAULT_INSTANCE.writeInt(dataOutput, readBlockOffset); BooleanConverter.DEFAULT_INSTANCE.writeBoolean(dataOutput, writeBlockId!=null); if (writeBlockId!=null) container.objectIdConverter().write(dataOutput, writeBlockId); IntegerConverter.DEFAULT_INSTANCE.writeInt(dataOutput, writeBlockOffset); } /** * Returns the number of bytes needed for serializing the elements of * this queue. * * @return the number of bytes stored by blocks in the container. */ public int bytes() { return bytes; } /** * Returns the id of the the actual block to read from. * * @return the id stored in <tt>readBlockId</tt>. */ public Object readBlockId () { return readBlockId; } /** * Returns the offset of the actual block to read from. * * @return the offset stored in <tt>readBlockOffset</tt>. */ public int readBlockOffset() { return readBlockOffset; } /** * Returns the id of the the actual block to write to. * * @return the id stored in <tt>writeBlockId</tt>. */ public Object writeBlockId() { return writeBlockId; } /** * Returns the offset of the actual block to write to. * * @return the offset stored in <tt>writeBlockOffset</tt>. */ public int writeBlockOffset() { return writeBlockOffset; } /** * Removes the actual block to read from out of the container and sets * <tt>readBlockId</tt> to the id of the <i>next</i> block to read * from. The id of the <i>next</i> block is stored at the end of the * removed block. * @throws IOException if an I/O error occurs. */ protected void removeBlock() throws IOException { container.remove(readBlockId); readBlockId = container.objectIdConverter().read( readBlock.dataInputStream(blockSize-readBlock.dataInputStream().readShort()) ); readBlock = null; readBlockOffset = ShortConverter.SIZE; } /** * Removes all elements from this queue. The queue will be * empty after this call returns so that <tt>size() == 0</tt>.<br> * This implementation removes all blocks from the container and sets * <tt>size</tt> and <tt>bytes</tt> to <tt>0</tt>. Thereafter the * actual blocks are set to <tt>null</tt>. */ public void clear() { if (bytes > 0) { while (!readBlockId.equals(writeBlockId)) { readBlock = (Block)container.get(readBlockId); try { removeBlock(); } catch (IOException ie) { throw new WrappingRuntimeException(ie); } } container.remove(readBlockId); } size = 0; bytes = 0; readBlock = null; readBlockOffset = ShortConverter.SIZE; writeBlock = null; writeBlockOffset = ShortConverter.SIZE; } /** * Closes this queue and releases any system resources associated with * it. This operation is idempotent, i.e., multiple calls of this * method takes the same effect as a single call.<br> * This implementation secures that the actual state of the block to * write to is stored in the container and sets the actual blocks to * <tt>null</tt>. */ public void close() { if (isClosed) return; super.close(); if (writeBlock != null) { container.update(writeBlockId, writeBlock); writeBlock = null; } readBlock = null; } }