You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by jv...@apache.org on 2013/04/25 13:47:34 UTC

[3/4] git commit: moved IoBuffer to codec project because it's usefull only for decoder implementation

moved IoBuffer to codec project because it's usefull only for decoder implementation


Project: http://git-wip-us.apache.org/repos/asf/mina/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina/commit/80967391
Tree: http://git-wip-us.apache.org/repos/asf/mina/tree/80967391
Diff: http://git-wip-us.apache.org/repos/asf/mina/diff/80967391

Branch: refs/heads/trunk
Commit: 809673918572c3c5e0674385fed0773341521e89
Parents: 6e056d8
Author: jvermillard <jv...@apache.org>
Authored: Thu Apr 25 13:45:17 2013 +0200
Committer: jvermillard <jv...@apache.org>
Committed: Thu Apr 25 13:45:17 2013 +0200

----------------------------------------------------------------------
 .../main/java/org/apache/mina/codec/IoBuffer.java  |  995 ++++++++++++++
 .../java/org/apache/mina/codec/IoBufferTest.java   |  797 ++++++++++++
 .../main/java/org/apache/mina/util/IoBuffer.java   | 1008 ---------------
 .../java/org/apache/mina/util/IoBufferTest.java    |  796 ------------
 4 files changed, 1792 insertions(+), 1804 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina/blob/80967391/codec/src/main/java/org/apache/mina/codec/IoBuffer.java
----------------------------------------------------------------------
diff --git a/codec/src/main/java/org/apache/mina/codec/IoBuffer.java b/codec/src/main/java/org/apache/mina/codec/IoBuffer.java
new file mode 100644
index 0000000..3ce26ff
--- /dev/null
+++ b/codec/src/main/java/org/apache/mina/codec/IoBuffer.java
@@ -0,0 +1,995 @@
+/**
+ * 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.mina.codec;
+
+import java.nio.Buffer;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.InvalidMarkException;
+import java.nio.ReadOnlyBufferException;
+
+/**
+ * A proxy class used to manage ByteBuffers as if they were just a big ByteBuffer. We can add as many buffers as needed,
+ * when accumulating data. From the user PoV, the methods are the very same than what we can get from ByteBuffer. <br/>
+ * IoBuffer instances are *not* thread safe.
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+public class IoBuffer {
+    /** The list of ByteBuffers were we store the data */
+    private BufferList buffers = new BufferList();
+
+    /** The maximal position in the IoBuffer */
+    private int limit;
+
+    /** The current position in the buffer */
+    private int position;
+
+    /** The marked position, for the next reset() */
+    private int mark;
+
+    /** Tells if the stored buffers are direct or heap */
+    private BufferType type;
+
+    /** Tells if the IoBuffer is readonly */
+    private boolean readOnly;
+
+    /** The bytes order (BIG_INDIAN or LITTLE_INDIAN) */
+    private ByteOrder order = ByteOrder.BIG_ENDIAN;
+
+    /** The two types of buffer we handle */
+    public enum BufferType {
+        HEAP, DIRECT;
+    }
+
+    /** A empty bytes array */
+    private final static byte[] EMPTY_BYTES = new byte[] {};
+
+    /** <code>UNSET_MARK</code> means the mark has not been set. */
+    private final static int UNSET_MARK = -1;
+
+    /**
+     * Construct a IoBuffer, with no buffer in it
+     */
+    public IoBuffer() {
+        position = 0;
+        mark = 0;
+        limit = 0;
+        type = null;
+        order = null;
+    }
+
+    /**
+     * Construct an empty IoBuffer with a defined type (either HEAP or DIRECT)
+     * 
+     * @param bufferType the type of buffer to use : BufferType.HEAP or BufferType.DIRECT
+     */
+    public IoBuffer(BufferType bufferType) {
+        position = 0;
+        mark = 0;
+        limit = 0;
+        type = bufferType;
+    }
+
+    /**
+     * Construct a IoBuffer with some ByteBuffers. The IoBuffer type will be selected from the first ByteBuffer type, so
+     * will the order.
+     * 
+     * @param byteBuffers the ByteBuffers added to the IoBuffer list
+     */
+    public IoBuffer(ByteBuffer... byteBuffers) {
+        if ((byteBuffers == null) || (byteBuffers.length == 0)) {
+            position = 0;
+            mark = 0;
+            limit = 0;
+            type = null;
+            order = null;
+        } else {
+            for (ByteBuffer byteBuffer : byteBuffers) {
+                if (type == null) {
+                    type = byteBuffer.isDirect() ? BufferType.DIRECT : BufferType.HEAP;
+                }
+
+                if (byteBuffer.limit() > 0) {
+                    buffers.add(byteBuffer);
+                }
+            }
+        }
+    }
+
+    /**
+     * Construct a IoBuffer from an existing IoBuffer.
+     * 
+     * @param ioBuffer the IoBuffer we want to copy
+     */
+    public IoBuffer(IoBuffer ioBuffer) {
+        // Find the position to start with
+        BufferNode node = ioBuffer.buffers.getFirst();
+        int pos = 0;
+
+        while (node != null) {
+            if (node.offset + node.buffer.limit() < position) {
+                node = buffers.getNext();
+                pos = node.offset + node.buffer.limit();
+            } else {
+                buffers.add(node.buffer);
+            }
+        }
+
+        position = position - pos;
+        mark = 0;
+        limit = ioBuffer.limit() - pos;
+        type = ioBuffer.type;
+        order = ioBuffer.order();
+    }
+
+    /**
+     * Adds a new ByteBuffer at the end of the list of buffers.
+     * 
+     * @param byteBuffer The added ByteBuffer
+     * @return The modified IoBuffer
+     */
+    public IoBuffer add(ByteBuffer... byteBuffers) {
+        for (ByteBuffer byteBuffer : byteBuffers) {
+            if (byteBuffer.limit() > 0) {
+                buffers.add(byteBuffer);
+            }
+        }
+
+        return this;
+    }
+
+    /**
+     * Allocate a Heap IoBuffer with a defined capacity
+     * 
+     * @param capacity The number of bytes to store
+     * @return The allocated IoBuffer
+     */
+    public static IoBuffer allocate(int capacity) {
+        if (capacity >= 0) {
+            ByteBuffer byteBuffer = ByteBuffer.allocate(capacity);
+
+            return new IoBuffer(byteBuffer);
+        } else {
+            throw new IllegalArgumentException("Cannot allocate an IoBuffer with a negative value : " + capacity);
+        }
+    }
+
+    /**
+     * Allocate a Direct IoBuffer with a defined capacity
+     * 
+     * @param capacity The number of bytes to store
+     * @return The allocated IoBuffer
+     */
+    public static IoBuffer allocateDirect(int capacity) {
+        if (capacity >= 0) {
+            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(capacity);
+
+            return new IoBuffer(byteBuffer);
+        } else {
+            throw new IllegalArgumentException("Cannot allocate an IoBuffer with a negative value : " + capacity);
+        }
+    }
+
+    /**
+     * @see ByteBuffer#array() Returns the byte array which this IoBuffer is based on, up to the sum of each contained
+     *      ByteBuffer's limit().<br/>
+     *      This array can be modified, but this won't modify the content of the underlying ByteBuffer instances,
+     *      contrary to the ByteBuffer.array() method.
+     * 
+     * @return the byte array which this IoBuffer is based on.
+     * @exception ReadOnlyBufferException if this IoBuffer is based on a read-only array.
+     * @exception UnsupportedOperationException if this IoBuffer is not based on an array.
+     */
+    public byte[] array() {
+        if (isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+
+        if (buffers.size == 0) {
+            return EMPTY_BYTES;
+        }
+
+        byte[] array = new byte[buffers.length];
+        BufferNode node = buffers.getFirst();
+        int pos = 0;
+
+        while (node != null) {
+            ByteBuffer buffer = node.buffer;
+            byte[] src = buffer.array();
+            int length = buffer.limit();
+
+            System.arraycopy(src, 0, array, pos, length);
+            pos += length;
+
+            node = buffers.getNext();
+        }
+
+        return array;
+    }
+
+    /**
+     * @see ByteBuffer#arrayOffset() Returns the offset of the byte array which this IoBuffer is based on, if there is
+     *      one.
+     *      <p>
+     *      The offset is the index of the array which corresponds to the zero position of the IoBuffer.
+     * 
+     * @return the offset of the byte array which this IoBuffer is based on.
+     * @exception ReadOnlyBufferException if this IoBuffer is based on a read-only array.
+     * @exception UnsupportedOperationException if this IoBuffer is not based on an array.
+     */
+    public int arrayOffset() {
+        if (isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+
+        // The offset is always 0
+        return 0;
+    }
+
+    /**
+     * @see ByteBuffer#asReadOnlyBuffer()
+     */
+    public IoBuffer asReadOnlyBuffer() {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @return the IoBuffer total capacity
+     */
+    public int capacity() {
+        return limit;
+    }
+
+    /**
+     * @see Buffer#clear() Clears this IoBuffer.
+     *      <p>
+     *      the following internal changes take place:
+     *      <ul>
+     *      <li>the current position is reset back to the start of the buffer</li>
+     *      <li>the value of the buffer limit is made equal to the capacity</li>
+     *      <li>and mark is cleared</li>
+     *      </ul>
+     *      Note that the resulting IoBuffer might be wider than the original one, simply because we will extent the
+     *      ByteBuffers limit to their capacity.
+     * 
+     * @return this buffer.
+     */
+    public IoBuffer clear() {
+        position = 0;
+        mark = UNSET_MARK;
+
+        BufferNode node = buffers.head;
+        int offset = 0;
+
+        while (node != null) {
+            node.buffer.clear();
+            node.offset = offset;
+            offset += node.buffer.limit();
+            node = node.next;
+        }
+
+        limit = offset;
+        buffers.length = 0;
+        buffers.current = buffers.head;
+
+        return this;
+    }
+
+    /**
+     * @see ByteBuffer#compact()
+     */
+    public IoBuffer compact() {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#compareTo(ByteBuffer)
+     */
+    public int compareTo(IoBuffer buffer) {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#duplicate()
+     */
+    public IoBuffer duplicate() {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#equals(Object)
+     */
+    @Override
+    public boolean equals(Object object) {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see Buffer#flip() Flips this buffer.
+     *      <p>
+     *      The limit is set to the current position, then the position is set to zero, and the mark is cleared.
+     *      <p>
+     *      The content of this IoBuffer is not changed.
+     * 
+     * @return this IoBuffer.
+     */
+    public IoBuffer flip() {
+        limit = position;
+        position = 0;
+        mark = UNSET_MARK;
+
+        return this;
+    }
+
+    /**
+     * Get a single byte for the IoBuffer at the current position. Increment the current position.
+     * 
+     * @return The byte found a the current position.
+     */
+    public byte get() {
+        if (position >= limit) {
+            // No more byte to read
+            throw new BufferUnderflowException();
+        }
+
+        // find the byte from the current buffer now
+        BufferNode currentNode = buffers.getCurrent();
+
+        // If the position is within the current buffer, then get the data from it
+        int bufferPosition = position - currentNode.offset;
+
+        if (bufferPosition < currentNode.buffer.limit()) {
+            position++;
+
+            return currentNode.buffer.get();
+        } else {
+            // We have exhausted the current buffer, let's see if we have one more
+            currentNode = buffers.getNext();
+
+            if (currentNode == null) {
+                // No more buffers
+                throw new BufferUnderflowException();
+            } else {
+                position++;
+                currentNode.buffer.position(0);
+
+                return currentNode.buffer.get();
+            }
+        }
+    }
+
+    /**
+     * @see ByteBuffer#get(byte[]) Reads bytes from the current position into the specified byte array and increases the
+     *      position by the number of bytes read.
+     *      <p>
+     *      Calling this method has the same effect as {@code get(dest, 0, dest.length)}.
+     * 
+     * @param dest the destination byte array.
+     * @return this IoBuffer.
+     * @exception BufferUnderflowException if {@code dest.length} is greater than {@code remaining()}.
+     */
+    public IoBuffer get(byte[] dst) {
+        if (dst.length > remaining()) {
+            throw new BufferUnderflowException();
+        }
+
+        int size = dst.length;
+        int destPos = 0;
+        BufferNode node = buffers.current;
+
+        while (size > 0) {
+            int length = node.buffer.limit() - node.buffer.position();
+            System.arraycopy(node.buffer.array(), node.buffer.position(), dst, destPos, length);
+            destPos += length;
+            node = buffers.getNext();
+        }
+
+        return this;
+    }
+
+    /**
+     * @see ByteBuffer#get(byte[],int,int)
+     */
+    public IoBuffer get(byte[] dst, int offset, int length) {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#get(int) Returns the byte at the specified index and does not change the position.
+     * 
+     * @param index the index, must not be negative and less than limit.
+     * @return the byte at the specified index.
+     * @exception IndexOutOfBoundsException if index is invalid.
+     */
+    public byte get(int index) {
+        if ((index < 0) || (index >= limit)) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        BufferNode currentNode = buffers.current;
+        BufferNode node = buffers.getFirst();
+
+        while (node != null) {
+            if (node.offset + node.buffer.limit() > index) {
+                byte result = node.buffer.get(index - node.offset);
+
+                // Reset the initial position before returning
+                buffers.current = currentNode;
+
+                return result;
+            } else {
+                node = buffers.getNext();
+            }
+        }
+
+        // Reset the initial position before returning
+        buffers.current = currentNode;
+
+        // Unlikely to happen
+        throw new IndexOutOfBoundsException();
+    }
+
+    /**
+     * @see ByteBuffer#getChar()
+     */
+    public IoBuffer getChar() {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#getChar(int)
+     */
+    public IoBuffer getChar(int index) {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#getDouble()
+     */
+    public IoBuffer getDouble() {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#getDouble(int)
+     */
+    public IoBuffer getDouble(int index) {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#getFloat()
+     */
+    public IoBuffer getFloat() {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#getFloat(int)
+     */
+    public IoBuffer getFloat(int index) {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#getInt() Returns the int at the current position and increases the position by 4.
+     *      <p>
+     *      The 4 bytes starting at the current position are composed into a int according to the current byte order and
+     *      returned.
+     * 
+     * @return the int at the current position.
+     * @exception BufferUnderflowException if the position is greater than {@code limit - 4}.
+     */
+    public int getInt() {
+        int newPosition = position + 4;
+
+        if (newPosition > limit) {
+            throw new BufferUnderflowException();
+        }
+
+        int result = loadInt(position);
+        position = newPosition;
+
+        return result;
+    }
+
+    /**
+     * Load an int from the underlying byteBuffers, taking the order into account.
+     */
+    private final int loadInt(int index) {
+        int bytes = 0;
+
+        if (order == ByteOrder.BIG_ENDIAN) {
+            for (int i = 0; i < 4; i++) {
+                bytes = bytes << 8;
+                bytes = bytes | (get() & 0xFF);
+            }
+        } else {
+            for (int i = 0; i < 4; i++) {
+                int val = get() & 0xFF;
+                bytes = bytes | (val << (i << 3));
+            }
+        }
+
+        return bytes;
+    }
+
+    /**
+     * @see ByteBuffer#getInt(int)
+     */
+    public IoBuffer getInt(int index) {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#getLong()
+     */
+    public IoBuffer getLong() {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#getLong(int)
+     */
+    public IoBuffer getLong(int index) {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#getShort()
+     */
+    public IoBuffer getShort() {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#getShort(int)
+     */
+    public IoBuffer getShort(int index) {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see Buffer#hasRemaining() Indicates if there are elements remaining in this IoBuffer, that is if
+     *      {@code position < limit}.
+     * 
+     * @return {@code true} if there are elements remaining in this IoBuffer, {@code false} otherwise.
+     */
+    public boolean hasRemaining() {
+        return position < limit;
+    }
+
+    /**
+     * @see ByteBuffer#isDirect() Tells if the stored ByteBuffers are Direct buffers or Heap Buffers
+     * @return <code>true</code> if we are storing Direct buffers, <code>false</code> otherwise.
+     */
+    public boolean isDirect() {
+        return type == BufferType.DIRECT;
+    }
+
+    /**
+     * @see Buffer#isReadOnly() Indicates whether this IoBuffer is read-only.
+     * 
+     * @return {@code true} if this IoBuffer is read-only, {@code false} otherwise.
+     */
+    public boolean isReadOnly() {
+        return readOnly;
+    }
+
+    /**
+     * @return the IoBuffer limit
+     */
+    public int limit() {
+        return limit;
+    }
+
+    /**
+     * @see Buffer#mark() Marks the current position, so that the position may return to this point later by calling
+     *      <code>reset()</code>.
+     * 
+     * @return this IoBuffer.
+     */
+    public IoBuffer mark() {
+        mark = position;
+
+        return this;
+    }
+
+    /**
+     * @see ByteBuffer#order() Returns the byte order used by this Iouffer when converting bytes from/to other primitive
+     *      types.
+     *      <p>
+     *      The default byte order of byte buffer is always {@link ByteOrder#BIG_ENDIAN BIG_ENDIAN}
+     * 
+     * @return the byte order used by this IoBuffer when converting bytes from/to other primitive types.
+     */
+    public ByteOrder order() {
+        return order;
+    }
+
+    /**
+     * @see ByteBuffer#order(ByteOrder) Sets the byte order of this IoBuffer.
+     * 
+     * @param byteOrder the byte order to set. If {@code null} then the order will be {@link ByteOrder#LITTLE_ENDIAN
+     *        LITTLE_ENDIAN}.
+     * @return this IoBuffer.
+     * @see ByteOrder
+     */
+    public IoBuffer order(ByteOrder bo) {
+        if (bo == null) {
+            order = ByteOrder.LITTLE_ENDIAN;
+        } else {
+            order = bo;
+        }
+
+        return this;
+    }
+
+    /**
+     * @see Buffer#position()
+     * @return The current position across all the ByteBuffers contained in the IoBuffer
+     */
+    public int position() {
+        return position;
+    }
+
+    /**
+     * @see Buffer#position(int) Sets the position in the IoBuffer.
+     *      <p>
+     *      If the mark is set and it is greater than the new position, then it is cleared.
+     * 
+     * @param newPosition the new position, must be not negative and not greater than limit.
+     * @return this IoBuffer.
+     * @exception IllegalArgumentException if <code>newPosition</code> is invalid.
+     */
+    public IoBuffer position(int newPosition) {
+        if (newPosition < 0) {
+            throw new IllegalArgumentException("The new position(" + newPosition + ") is negative");
+        }
+
+        if (newPosition >= limit) {
+            throw new IllegalArgumentException("The new position(" + newPosition
+                    + ") is larger than this buffer limit (" + limit());
+        }
+
+        if (buffers.head == null) {
+            throw new IllegalArgumentException("Cannot set a position over an empty buffer");
+        }
+
+        // Find the right current buffer
+        BufferNode currentNode = buffers.getCurrent();
+
+        // The new position might not be on the current buffer.
+        if ((newPosition < currentNode.offset) || (newPosition >= currentNode.offset + currentNode.buffer.limit())) {
+            // Ok, we aren't on the current buffer. Find the new current buffer
+            BufferNode node = buffers.head;
+            int counter = 0;
+
+            while (node != null) {
+                int limit = node.buffer.limit();
+                counter += limit;
+
+                if (counter >= newPosition) {
+                    // Found
+                    currentNode = node;
+                    break;
+                } else {
+                    node = node.next;
+                }
+            }
+        }
+
+        position = newPosition;
+        currentNode.buffer.position(position - currentNode.offset);
+        buffers.current = currentNode;
+
+        return this;
+    }
+
+    /**
+     * @see Buffer#remaining() Returns the number of remaining elements in this IoBuffer, that is
+     *      {@code limit - position}.
+     * 
+     * @return the number of remaining elements in this IoBuffer.
+     */
+    public int remaining() {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see Buffer#reset() Resets the position of this IoBuffer to the <code>mark</code>.
+     * 
+     * @return this IoBuffer.
+     * @exception InvalidMarkException if the mark is not set.
+     */
+    public IoBuffer reset() {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see Buffer#rewind() Rewinds this IoBuffer.
+     *      <p>
+     *      The position is set to zero, and the mark is cleared. The content of this IoBuffer is not changed.
+     * 
+     * @return this IoBuffer.
+     */
+    public IoBuffer rewind() {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#slice()
+     */
+    public IoBuffer slice() {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#wrap(byte[])
+     */
+    public IoBuffer wrap(byte[] array) {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @see ByteBuffer#wrap(byte[], int, int)
+     */
+    public IoBuffer wrap(byte[] array, int offset, int length) {
+        // TODO code me !
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns a string representing the IoBuffer.
+     * 
+     * @return a String representation of the IoBuffer
+     */
+    @Override
+    public String toString() {
+        return "IoBuffer[pos=" + position + " lim=" + limit + " mrk=" + mark + "]";
+    }
+
+    // ------------------------------------------------------------------------------------------------
+    // private inner data structure
+    // ------------------------------------------------------------------------------------------------
+    /**
+     * A container for ByterBuffers stored in the buffers list
+     */
+    private class BufferNode {
+        /** The stored buffer */
+        private ByteBuffer buffer;
+
+        /** The next buffer in the list */
+        private BufferNode next;
+
+        /** The position of this buffer in the IoBuffer list of bytes */
+        private int offset;
+
+        /**
+         * Creates a new entry in the list
+         * 
+         * @param entry The added ByteBuffer
+         */
+        private BufferNode(ByteBuffer byteBuffer) {
+            this.buffer = byteBuffer;
+        }
+
+        @Override
+        public String toString() {
+            return buffer.toString() + ", Offset:" + offset + (next != null ? " --> \n  " : "");
+        }
+    }
+
+    /**
+     * A LinkedList storing all the ByteBuffers. It can only be browsed forward.
+     */
+    private class BufferList {
+        /** The first ByteBuffer in the list */
+        private BufferNode head;
+
+        /** The last ByteBuffer in the list */
+        private BufferNode tail;
+
+        /** The current ByteBuffer in the list */
+        private BufferNode current;
+
+        /** The number of nodes in the list */
+        private int size;
+
+        /** The number of bytes in the list */
+        private int length;
+
+        /** A flag used to indicate that we already have navigated past the tail of the list. */
+        private boolean pastTail;
+
+        /**
+         * Creates an empty list
+         */
+        private BufferList() {
+            head = tail = current = null;
+            size = 0;
+            length = 0;
+            pastTail = false;
+        }
+
+        /**
+         * Creates a list with one ByteBuffer
+         * 
+         * @param byteBuffer The added ByteBuffer
+         */
+        private BufferList(ByteBuffer byteBuffer) {
+            BufferNode node = new BufferNode(byteBuffer);
+            head = tail = current = node;
+            size = 1;
+            length = byteBuffer.limit();
+            pastTail = false;
+        }
+
+        /**
+         * Adds a new ByteBuffer in the list
+         * 
+         * @param byteBuffer The added ByteBuffer
+         */
+        private void add(ByteBuffer byteBuffer) {
+            assert (byteBuffer != null);
+
+            // Check the buffer type
+            if (type == null) {
+                if (byteBuffer.isDirect()) {
+                    type = BufferType.DIRECT;
+                } else {
+                    type = BufferType.HEAP;
+                }
+            } else {
+                if (isDirect() != byteBuffer.isDirect()) {
+                    throw new RuntimeException();
+                }
+            }
+
+            // Check the ByteOrder
+            if (size == 0) {
+                order = byteBuffer.order();
+            } else if (order != byteBuffer.order()) {
+                throw new RuntimeException();
+            }
+
+            BufferNode newNode = new BufferNode(byteBuffer);
+            newNode.offset = length;
+
+            if (size == 0) {
+                head = tail = current = newNode;
+            } else {
+                tail.next = newNode;
+                tail = newNode;
+            }
+
+            size++;
+            length += byteBuffer.limit();
+            limit = length;
+            pastTail = false;
+        }
+
+        /**
+         * Get the first BufferNode in the list. The current pointer will move forward, after having be reset to the
+         * beginning of the list
+         * 
+         * @return The first BufferNode in the list
+         */
+        private BufferNode getFirst() {
+            if (head == null) {
+                return null;
+            }
+
+            current = head.next;
+            pastTail = false;
+
+            return head;
+        }
+
+        /**
+         * Get the next BufferNode from the list. If this is the first time this method is called, it will return the
+         * same value than a getFirst().
+         * 
+         * @return The next BufferNode in the list, moving forward in the list at the same time
+         */
+        private BufferNode getNext() {
+            if (current == null) {
+                return null;
+            }
+
+            if (current == tail) {
+                if (pastTail) {
+                    return null;
+                } else {
+                    pastTail = true;
+
+                    return current;
+                }
+            } else {
+                current = current.next;
+
+                return current;
+            }
+        }
+
+        /**
+         * Gets the current BufferNode from the list, if we aren't already past the tail.
+         * 
+         * @return The current BufferNode
+         */
+        private BufferNode getCurrent() {
+            if (pastTail) {
+                return null;
+            }
+
+            return current;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+
+            BufferNode node = head;
+
+            while (node != null) {
+                if (node == current) {
+                    sb.append("**");
+                }
+
+                sb.append(node);
+                node = node.next;
+            }
+
+            return sb.toString();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina/blob/80967391/codec/src/test/java/org/apache/mina/codec/IoBufferTest.java
----------------------------------------------------------------------
diff --git a/codec/src/test/java/org/apache/mina/codec/IoBufferTest.java b/codec/src/test/java/org/apache/mina/codec/IoBufferTest.java
new file mode 100644
index 0000000..7349eb8
--- /dev/null
+++ b/codec/src/test/java/org/apache/mina/codec/IoBufferTest.java
@@ -0,0 +1,797 @@
+/**
+ * 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.mina.codec;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+import org.apache.mina.codec.IoBuffer;
+import org.junit.Test;
+
+/**
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+public class IoBufferTest {
+    /**
+     * Test the addition of 3 heap buffers with data
+     */
+    @Test
+    public void testAddHeapBuffers() {
+        ByteBuffer bb1 = ByteBuffer.allocate(5);
+        bb1.put("012".getBytes());
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(5);
+        bb2.put("345".getBytes());
+        bb2.flip();
+
+        ByteBuffer bb3 = ByteBuffer.allocate(5);
+        bb3.put("6789".getBytes());
+        bb3.flip();
+
+        IoBuffer ioBuffer = new IoBuffer();
+        ioBuffer.add(bb1, bb2).add(bb3);
+
+        assertEquals(0, ioBuffer.position());
+        assertEquals(10, ioBuffer.limit());
+        assertEquals(10, ioBuffer.capacity());
+        assertTrue(ioBuffer.hasRemaining());
+
+        for (int i = 0; i < 10; i++) {
+            assertTrue(ioBuffer.hasRemaining());
+            assertEquals("0123456789".charAt(i), ioBuffer.get());
+        }
+
+        try {
+            assertFalse(ioBuffer.hasRemaining());
+            ioBuffer.get();
+            fail();
+        } catch (BufferUnderflowException bufe) {
+            assertTrue(true);
+        }
+    }
+
+    /**
+     * Test the addition of 3 heap buffers, one being empty
+     */
+    @Test
+    public void testAddHeapBuffersOneEmpty() {
+        ByteBuffer bb1 = ByteBuffer.allocate(5);
+        bb1.put("012".getBytes());
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(0);
+
+        ByteBuffer bb3 = ByteBuffer.allocate(5);
+        bb3.put("3456".getBytes());
+        bb3.flip();
+
+        IoBuffer ioBuffer = new IoBuffer();
+        ioBuffer.add(bb1, bb2).add(bb3);
+
+        assertEquals(0, ioBuffer.position());
+        assertEquals(7, ioBuffer.limit());
+        assertEquals(7, ioBuffer.capacity());
+
+        for (int i = 0; i < 7; i++) {
+            assertTrue(ioBuffer.hasRemaining());
+            assertEquals("0123456".charAt(i), ioBuffer.get());
+        }
+
+        try {
+            ioBuffer.get();
+            fail();
+        } catch (BufferUnderflowException bufe) {
+            assertTrue(true);
+        }
+    }
+
+    /**
+     * Test the addition of mixed type buffers
+     */
+    @Test(expected = RuntimeException.class)
+    public void testAddMixedTypeBuffers() {
+        ByteBuffer bb1 = ByteBuffer.allocate(5);
+        bb1.put("012".getBytes());
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocateDirect(5);
+        bb2.put("3456".getBytes());
+        bb2.flip();
+
+        IoBuffer ioBuffer = new IoBuffer();
+        ioBuffer.add(bb1, bb2);
+    }
+
+    /**
+     * Test the addition of mixed order buffers
+     */
+    @Test(expected = RuntimeException.class)
+    public void testAddMixedOrderBuffers() {
+        ByteBuffer bb1 = ByteBuffer.allocate(5);
+        bb1.order(ByteOrder.LITTLE_ENDIAN);
+        bb1.put("012".getBytes());
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocateDirect(5);
+        bb1.order(ByteOrder.BIG_ENDIAN);
+        bb2.put("3456".getBytes());
+        bb2.flip();
+
+        IoBuffer ioBuffer = new IoBuffer();
+        ioBuffer.add(bb1, bb2);
+    }
+
+    //-------------------------------------------------------------------------
+    // Test the allocate(int) method
+    // 1) allocation with a negative value
+    // 2) allocation with a 0 length
+    // 3) allocation with a 1024 value
+    //-------------------------------------------------------------------------
+    /**
+     * Test the allocation of a new heap IoBuffer with a negative value
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testAllocateNegative() {
+        IoBuffer.allocate(-1);
+    }
+
+    /**
+     * Test the allocation of a new heap IoBuffer with no byte in it
+     */
+    @Test
+    public void testAllocate0() {
+        IoBuffer ioBuffer = IoBuffer.allocate(0);
+
+        assertFalse(ioBuffer.isDirect());
+        assertEquals(0, ioBuffer.capacity());
+        assertEquals(0, ioBuffer.limit());
+        assertEquals(0, ioBuffer.position());
+        assertFalse(ioBuffer.hasRemaining());
+    }
+
+    /**
+     * Test the allocation of a new heap IoBuffer with 1024 bytes
+     */
+    @Test
+    public void testAllocate1024() {
+        IoBuffer ioBuffer = IoBuffer.allocate(1024);
+
+        assertFalse(ioBuffer.isDirect());
+        assertEquals(1024, ioBuffer.capacity());
+        assertEquals(1024, ioBuffer.limit());
+        assertEquals(0, ioBuffer.position());
+        assertTrue(ioBuffer.hasRemaining());
+    }
+
+    //-------------------------------------------------------------------------
+    // Test the allocateDirect(int) method. We check :
+    // 1) allocation with a negative value
+    // 2) allocation with a 0 length
+    // 3) allocation with a 1024 value
+    //-------------------------------------------------------------------------
+    /**
+     * Test the allocation of a new heap IoBuffer with a negative value
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testAllocateDirectNegative() {
+        IoBuffer.allocate(-1);
+    }
+
+    /**
+     * Test the allocation of a new direct IoBuffer with no byte in it
+     */
+    @Test
+    public void testAllocateDirect0() {
+        IoBuffer ioBuffer = IoBuffer.allocateDirect(0);
+
+        assertTrue(ioBuffer.isDirect());
+        assertEquals(0, ioBuffer.capacity());
+        assertEquals(0, ioBuffer.limit());
+        assertEquals(0, ioBuffer.position());
+        assertFalse(ioBuffer.hasRemaining());
+    }
+
+    /**
+     * Test the allocation of a new direct IoBuffer with 1024 bytes
+     */
+    @Test
+    public void testAllocateDirect1024() {
+        IoBuffer ioBuffer = IoBuffer.allocateDirect(1024);
+
+        assertTrue(ioBuffer.isDirect());
+        assertEquals(1024, ioBuffer.capacity());
+        assertEquals(1024, ioBuffer.limit());
+        assertEquals(0, ioBuffer.position());
+        assertTrue(ioBuffer.hasRemaining());
+    }
+
+    /**
+     * Test the get() method on a IoBuffer containing one ByteBuffer with 3 bytes
+     */
+    @Test
+    public void testGetOneBuffer3Bytes() {
+        ByteBuffer bb = ByteBuffer.allocate(5);
+        bb.put("012".getBytes());
+        bb.flip();
+
+        IoBuffer ioBuffer = new IoBuffer(bb);
+        assertEquals(0, ioBuffer.position());
+        assertEquals(3, ioBuffer.limit());
+
+        assertTrue(ioBuffer.hasRemaining());
+        assertEquals('0', ioBuffer.get());
+        assertTrue(ioBuffer.hasRemaining());
+        assertEquals('1', ioBuffer.get());
+        assertTrue(ioBuffer.hasRemaining());
+        assertEquals('2', ioBuffer.get());
+
+        try {
+            assertFalse(ioBuffer.hasRemaining());
+            ioBuffer.get();
+            fail();
+        } catch (BufferUnderflowException bufe) {
+            // expected
+            assertEquals(3, ioBuffer.position());
+        }
+    }
+
+    /**
+     * Test the get() method on a IoBuffer containing one ByteBuffer with 0 bytes
+     */
+    @Test
+    public void testGetOneBuffer0Bytes() {
+        ByteBuffer bb = ByteBuffer.allocate(0);
+
+        IoBuffer ioBuffer = new IoBuffer(bb);
+        assertEquals(0, ioBuffer.position());
+        assertEquals(0, ioBuffer.limit());
+
+        try {
+            assertFalse(ioBuffer.hasRemaining());
+            ioBuffer.get();
+            fail();
+        } catch (BufferUnderflowException bufe) {
+            // expected
+            assertEquals(0, ioBuffer.position());
+        }
+    }
+
+    /**
+     * Test the get() method on a IoBuffer containing two ByteBuffer with 3 bytes
+     */
+    @Test
+    public void testGetTwoBuffer3Bytes() {
+        ByteBuffer bb1 = ByteBuffer.allocate(5);
+        bb1.put("012".getBytes());
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(5);
+        bb2.put("345".getBytes());
+        bb2.flip();
+
+        IoBuffer ioBuffer = new IoBuffer(bb1, bb2);
+
+        assertEquals(0, ioBuffer.position());
+        assertEquals(6, ioBuffer.limit());
+        assertTrue(ioBuffer.hasRemaining());
+
+        assertEquals('0', ioBuffer.get());
+        assertTrue(ioBuffer.hasRemaining());
+        assertEquals('1', ioBuffer.get());
+        assertTrue(ioBuffer.hasRemaining());
+        assertEquals('2', ioBuffer.get());
+        assertTrue(ioBuffer.hasRemaining());
+        assertEquals('3', ioBuffer.get());
+        assertTrue(ioBuffer.hasRemaining());
+        assertEquals('4', ioBuffer.get());
+        assertTrue(ioBuffer.hasRemaining());
+        assertEquals('5', ioBuffer.get());
+
+        try {
+            assertFalse(ioBuffer.hasRemaining());
+            ioBuffer.get();
+            fail();
+        } catch (BufferUnderflowException bufe) {
+            // expected
+            assertEquals(6, ioBuffer.position());
+        }
+    }
+
+    //-------------------------------------------------------------------------
+    // Test the array() method. We will check those cases :
+    // 1) array over an empty buffer: we should get an empty byte array
+    // 2) array over a buffer with one single empty ByteBuffer 
+    // 3) array over a buffer with one single ByteBuffer with data
+    // 4) array over a buffer containing many ByteBuffers 
+    //-------------------------------------------------------------------------
+    /**
+     * Test the array method for a IoBuffer containing one empty ByteBuffer
+     */
+    @Test
+    public void testArrayEmptyByteBuffer() {
+        IoBuffer ioBuffer = new IoBuffer();
+
+        byte[] array = ioBuffer.array();
+        assertNotNull(array);
+        assertEquals(0, array.length);
+        assertTrue(Arrays.equals(new byte[] {}, array));
+    }
+
+    /**
+     * Test the array method for a IoBuffer containing one ByteBuffer (cases 2 and 3)
+     */
+    @Test
+    public void testArrayOneByteBuffer() {
+        ByteBuffer bb1 = ByteBuffer.allocate(5);
+        IoBuffer ioBuffer = new IoBuffer(bb1);
+
+        // Empty buffer first
+        byte[] array = ioBuffer.array();
+        assertNotNull(array);
+        assertEquals(5, array.length);
+        assertTrue(Arrays.equals(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00 }, array));
+
+        // Buffer with data
+        bb1.put("012".getBytes());
+        bb1.flip();
+
+        ioBuffer = new IoBuffer(bb1);
+
+        array = ioBuffer.array();
+        assertNotNull(array);
+        assertEquals(3, array.length);
+        assertTrue(Arrays.equals(new byte[] { '0', '1', '2' }, array));
+    }
+
+    /**
+     * Test the array method for a IoBuffer containing one ByteBuffer not initialized
+     */
+    @Test
+    public void testArrayByteBufferNotInitialized() {
+        ByteBuffer bb = ByteBuffer.allocate(3);
+        IoBuffer ioBuffer = new IoBuffer(bb);
+
+        byte[] array = ioBuffer.array();
+        assertNotNull(array);
+        assertEquals(3, array.length);
+        assertTrue(Arrays.equals(new byte[] { 0x00, 0x00, 0x00 }, array));
+    }
+
+    /**
+     * Test the array method for a IoBuffer containing three ByteBuffers
+     */
+    @Test
+    public void testArrayThreeByteBuffers() {
+        ByteBuffer bb1 = ByteBuffer.allocate(5);
+        bb1.put("012".getBytes());
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(0);
+
+        ByteBuffer bb3 = ByteBuffer.allocate(5);
+        bb3.put("3456".getBytes());
+        bb3.flip();
+
+        IoBuffer ioBuffer = new IoBuffer(bb1, bb2, bb3);
+
+        byte[] array = ioBuffer.array();
+        assertNotNull(array);
+        assertEquals(7, array.length);
+        assertTrue(Arrays.equals(new byte[] { '0', '1', '2', '3', '4', '5', '6' }, array));
+    }
+
+    /**
+     * Test the getInt() method, on a buffer containing 2 ints in one ByteBuffer
+     */
+    @Test
+    public void testGetInt2IntsOneBB() {
+        ByteBuffer bb = ByteBuffer.allocate(8);
+        bb.putInt(12345);
+        bb.putInt(67890);
+        bb.flip();
+
+        IoBuffer ioBuffer = new IoBuffer(bb);
+        assertEquals(12345, ioBuffer.getInt());
+        assertEquals(67890, ioBuffer.getInt());
+    }
+
+    /**
+     * Test the getInt() method, on a buffer containing 2 ints in two ByteBuffers
+     */
+    @Test
+    public void testGetInt2Ints2BBs() {
+        ByteBuffer bb1 = ByteBuffer.allocate(4);
+        bb1.putInt(12345);
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(4);
+        bb2.putInt(67890);
+        bb2.flip();
+
+        IoBuffer ioBuffer = new IoBuffer(bb1, bb2);
+
+        assertEquals(12345, ioBuffer.getInt());
+        assertEquals(67890, ioBuffer.getInt());
+    }
+
+    /**
+     * Test the getInt() method, on a buffer containing 2 ints in two ByteBuffers
+     * with LittleInidan order
+     */
+    @Test
+    public void testGetInt2Ints2BBsLittleIndian() {
+        ByteBuffer bb1 = ByteBuffer.allocate(4);
+        bb1.order(ByteOrder.LITTLE_ENDIAN);
+        bb1.putInt(12345);
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(4);
+        bb2.order(ByteOrder.LITTLE_ENDIAN);
+        bb2.putInt(67890);
+        bb2.flip();
+
+        IoBuffer ioBuffer = new IoBuffer(bb1, bb2);
+
+        assertEquals(12345, ioBuffer.getInt());
+        assertEquals(67890, ioBuffer.getInt());
+    }
+
+    /**
+     * Test the getInt() method, on a buffer containing 1 int spread in two ByteBuffers
+     */
+    @Test
+    public void testGetInt1Int2BBs() {
+        ByteBuffer bb1 = ByteBuffer.allocate(1);
+        bb1.put((byte) 0x01);
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(3);
+        bb2.put(new byte[] { 0x02, 0x03, 0x04 });
+        bb2.flip();
+
+        IoBuffer ioBuffer = new IoBuffer(bb1, bb2);
+
+        assertEquals(0x01020304, ioBuffer.getInt());
+    }
+
+    /**
+     * Test the getInt() method, on a buffer containing 1 incomplet int spread in two ByteBuffers
+     */
+    @Test(expected = BufferUnderflowException.class)
+    public void testGetIntIncompletInt2BBs() {
+        ByteBuffer bb1 = ByteBuffer.allocate(1);
+        bb1.put((byte) 0x01);
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(2);
+        bb2.put(new byte[] { 0x02, 0x03 });
+        bb2.flip();
+
+        IoBuffer ioBuffer = new IoBuffer(bb1, bb2);
+
+        ioBuffer.getInt();
+    }
+
+    /**
+     * test the get(int) method on one buffer
+     */
+    @Test
+    public void testGetIntOneBuffer() {
+        ByteBuffer bb = ByteBuffer.allocate(4);
+        bb.put("0123".getBytes());
+        bb.flip();
+
+        IoBuffer ioBuffer = new IoBuffer(bb);
+
+        assertEquals('0', ioBuffer.get());
+        assertEquals('1', ioBuffer.get());
+        assertEquals('0', ioBuffer.get(0));
+        assertEquals('3', ioBuffer.get(3));
+        assertEquals('1', ioBuffer.get(1));
+        assertEquals('2', ioBuffer.get(2));
+        assertEquals('2', ioBuffer.get());
+
+        try {
+            ioBuffer.get(4);
+            fail();
+        } catch (IndexOutOfBoundsException ioobe) {
+            // expected
+            assertTrue(true);
+        }
+    }
+
+    /**
+     * test the get(int) method on two buffers
+     */
+    @Test
+    public void testGetIntTwoBuffer() {
+        ByteBuffer bb1 = ByteBuffer.allocate(4);
+        bb1.put("0123".getBytes());
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(4);
+        bb2.put("4567".getBytes());
+        bb2.flip();
+
+        IoBuffer ioBuffer = new IoBuffer(bb1, bb2);
+
+        assertEquals('0', ioBuffer.get());
+        assertEquals('1', ioBuffer.get());
+        assertEquals('0', ioBuffer.get(0));
+        assertEquals('4', ioBuffer.get(4));
+        assertEquals('7', ioBuffer.get(7));
+        assertEquals('2', ioBuffer.get(2));
+        assertEquals('2', ioBuffer.get());
+        assertEquals('3', ioBuffer.get());
+        assertEquals('4', ioBuffer.get());
+
+        try {
+            ioBuffer.get(8);
+            fail();
+        } catch (IndexOutOfBoundsException ioobe) {
+            // expected
+            assertTrue(true);
+        }
+    }
+
+    //-------------------------------------------------------------------------
+    // The the clear method. It will erase all the data in all the inner
+    // ByteBuffer, thus the buffer size might increase.
+    // We will check those case :
+    // 1) clear an empty buffer
+    // 2) clear a buffer with one ByteBuffer
+    // 3) clear a buffer with numerous ByteBuffers
+    //-------------------------------------------------------------------------
+    /**
+     * Test the clear() method
+     */
+    @Test
+    public void testClearEmptyBuffer() {
+        ByteBuffer bb1 = ByteBuffer.allocate(4);
+        bb1.put("012".getBytes());
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(4);
+        bb2.put("345".getBytes());
+        bb2.flip();
+
+        IoBuffer ioBuffer = new IoBuffer(bb1, bb2);
+
+        assertEquals(6, ioBuffer.limit());
+
+        // Move forward a bit
+        ioBuffer.get();
+        ioBuffer.get();
+
+        // Clear
+        ioBuffer.clear();
+
+        // We should be back to the origin
+        assertEquals(0, ioBuffer.position());
+
+        // The limit must have grown
+        assertEquals(8, ioBuffer.limit());
+    }
+
+    /**
+     * Test the flip() method
+     */
+    @Test
+    public void testFlip() {
+        ByteBuffer bb1 = ByteBuffer.allocate(4);
+        bb1.put("0123".getBytes());
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(4);
+        bb2.put("4567".getBytes());
+        bb2.flip();
+
+        IoBuffer ioBuffer = new IoBuffer(bb1, bb2);
+
+        // Move forward a bit
+        ioBuffer.get();
+        ioBuffer.get();
+
+        // Clear
+        ioBuffer.clear();
+
+        // We should be back to the origin
+        assertEquals(0, ioBuffer.position());
+        assertEquals(8, ioBuffer.limit());
+    }
+
+    //-------------------------------------------------------------------------
+    // Test the position() method
+    // We will test that the position() metho returns the correct result in 
+    // those cases :
+    // 1) the buffer is empty : must return 0
+    // 2) must return a value between 0 and limit()
+    //-------------------------------------------------------------------------
+    /**
+     * Test the position method over an emptyIoBuffer
+     */
+    @Test
+    public void testPositionEmptyBuffer() {
+        IoBuffer ioBuffer = new IoBuffer();
+
+        assertEquals(0, ioBuffer.position());
+    }
+
+    /**
+     * Test the position method over a buffer
+     */
+    @Test
+    public void testPositionBuffer() {
+        ByteBuffer bb1 = ByteBuffer.allocate(4);
+        bb1.put("012".getBytes());
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(4);
+        bb2.put("3456".getBytes());
+        bb2.flip();
+
+        ByteBuffer bb3 = ByteBuffer.allocate(4);
+        bb3.put("789".getBytes());
+        bb3.flip();
+
+        // The resulting buffer will be seen as "0123456789"
+        IoBuffer ioBuffer = new IoBuffer(bb1, bb2, bb3);
+
+        // Iterate and check the position
+        for (int i = 0; i < ioBuffer.limit(); i++) {
+            assertEquals(i, ioBuffer.position());
+            ioBuffer.get();
+        }
+    }
+
+    //-------------------------------------------------------------------------
+    // Test the position(int) method
+    // We will test many different cases
+    // 1) a position() in an empty buffer
+    // 2) a position() with a negative value
+    // 3) a position() with a value above the limit
+    // 4) a position() within the current buffer
+    //  4-1) at the beginning of the current buffer
+    //  4-2) at the end of the current buffer
+    //  4-3) in the middle of the current buffer
+    // 5) a position() before the current buffer
+    // 6) a position() after the current buffer
+    //-------------------------------------------------------------------------
+    /**
+     * Test the position method over an emptyIoBuffer
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testPositionIntEmptyBuffer() {
+        IoBuffer ioBuffer = new IoBuffer();
+
+        ioBuffer.position(0);
+    }
+
+    /**
+     * Test the position method with a negative value
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testPositionNegativeValue() {
+        ByteBuffer bb1 = ByteBuffer.allocate(4);
+        bb1.put("0123".getBytes());
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(4);
+        bb2.put("4567".getBytes());
+        bb2.flip();
+
+        ByteBuffer bb3 = ByteBuffer.allocate(4);
+        bb3.put("89".getBytes());
+        bb3.flip();
+
+        IoBuffer ioBuffer = new IoBuffer(bb1, bb2, bb3);
+
+        ioBuffer.position(-1);
+    }
+
+    /**
+     * Test the position method with a value above the buffer size
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testPositionAboveValue() {
+        ByteBuffer bb1 = ByteBuffer.allocate(4);
+        bb1.put("012".getBytes());
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(4);
+        bb2.put("3456".getBytes());
+        bb2.flip();
+
+        ByteBuffer bb3 = ByteBuffer.allocate(4);
+        bb3.put("789".getBytes());
+        bb3.flip();
+
+        // The resulting buffer will be seen as "0123456789"
+        IoBuffer ioBuffer = new IoBuffer(bb1, bb2, bb3);
+
+        ioBuffer.position(10);
+    }
+
+    /**
+     * Test the position method in the current buffer
+     */
+    @Test
+    public void testPositionCurrentBuffer() {
+        ByteBuffer bb1 = ByteBuffer.allocate(4);
+        bb1.put("012".getBytes());
+        bb1.flip();
+
+        ByteBuffer bb2 = ByteBuffer.allocate(4);
+        bb2.put("3456".getBytes());
+        bb2.flip();
+
+        ByteBuffer bb3 = ByteBuffer.allocate(4);
+        bb3.put("789".getBytes());
+        bb3.flip();
+
+        // The resulting buffer will be seen as "0123456789"
+        IoBuffer ioBuffer = new IoBuffer(bb1, bb2, bb3);
+
+        // Set the position in the middle of bb2 (4-3)
+        ioBuffer.position(5);
+
+        assertEquals('5', ioBuffer.get());
+
+        // Set the position at the beginning of bb2 (4-1)
+        ioBuffer.position(3);
+
+        assertEquals('3', ioBuffer.get());
+
+        // Set the position at the end of bb2 (4-2)
+        ioBuffer.position(6);
+
+        assertEquals('6', ioBuffer.get());
+
+        // Set a position before the current buffer (5)
+        ioBuffer.position(2);
+        assertEquals('2', ioBuffer.get());
+
+        // Set a position after the current buffer (6)
+        ioBuffer.position(7);
+        assertEquals('7', ioBuffer.get());
+
+        // Now, let's see if we can get all the elements correctly
+        // if we set the position from 0 to end
+        for (int i = 0; i < ioBuffer.limit(); i++) {
+            ioBuffer.position(i);
+            assertEquals('0' + i, ioBuffer.get());
+        }
+
+        // Same, in revert order
+        for (int i = ioBuffer.limit() - 1; i >= 0; i--) {
+            ioBuffer.position(i);
+            assertEquals('0' + i, ioBuffer.get());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina/blob/80967391/core/src/main/java/org/apache/mina/util/IoBuffer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/util/IoBuffer.java b/core/src/main/java/org/apache/mina/util/IoBuffer.java
deleted file mode 100644
index aac97b6..0000000
--- a/core/src/main/java/org/apache/mina/util/IoBuffer.java
+++ /dev/null
@@ -1,1008 +0,0 @@
-/**
- * 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.mina.util;
-
-import java.nio.Buffer;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.InvalidMarkException;
-import java.nio.ReadOnlyBufferException;
-
-/**
- * A proxy class used to manage ByteBuffers as if they were just a big ByteBuffer. We can
- * add as many buffers as needed, when accumulating data. From the user PoV, the methods
- * are the very same than what we can get from ByteBuffer.
- * <br/>
- * IoBuffer instances are *not* thred safe.
- * 
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
-public class IoBuffer {
-    /** The list of ByteBuffers were we store the data */
-    private BufferList buffers = new BufferList();
-
-    /** The maximal position in the IoBuffer */
-    private int limit;
-
-    /** The current position in the buffer */
-    private int position;
-
-    /** The marked position, for the next reset() */
-    private int mark;
-
-    /** Tells if the stored buffers are direct or heap */
-    private BufferType type;
-
-    /** Tells if the IoBuffer is readonly */
-    private boolean readOnly;
-
-    /** The bytes order (BIG_INDIAN or LITTLE_INDIAN) */
-    private ByteOrder order = ByteOrder.BIG_ENDIAN;
-
-    /** The two types of buffer we handle */
-    public enum BufferType {
-        HEAP, DIRECT;
-    }
-
-    /** A empty bytes array */
-    private final static byte[] EMPTY_BYTES = new byte[] {};
-
-    /** <code>UNSET_MARK</code> means the mark has not been set. */
-    private final static int UNSET_MARK = -1;
-
-    /**
-     * Construct a IoBuffer, with no buffer in it
-     */
-    public IoBuffer() {
-        position = 0;
-        mark = 0;
-        limit = 0;
-        type = null;
-        order = null;
-    }
-
-    /**
-     * Construct an empty IoBuffer with a defined type (either HEAP or DIRECT)
-     * 
-     * @param bufferType the type of buffer to use : BufferType.HEAP or BufferType.DIRECT
-     */
-    public IoBuffer(BufferType bufferType) {
-        position = 0;
-        mark = 0;
-        limit = 0;
-        type = bufferType;
-    }
-
-    /**
-     * Construct a IoBuffer with some ByteBuffers. The IoBuffer type will be selected
-     * from the first ByteBuffer type, so will the order.
-     * @param byteBuffers the ByteBuffers added to the IoBuffer list
-     */
-    public IoBuffer(ByteBuffer... byteBuffers) {
-        if ((byteBuffers == null) || (byteBuffers.length == 0)) {
-            position = 0;
-            mark = 0;
-            limit = 0;
-            type = null;
-            order = null;
-        } else {
-            for (ByteBuffer byteBuffer : byteBuffers) {
-                if (type == null) {
-                    type = byteBuffer.isDirect() ? BufferType.DIRECT : BufferType.HEAP;
-                }
-
-                if (byteBuffer.limit() > 0) {
-                    buffers.add(byteBuffer);
-                }
-            }
-        }
-    }
-
-    /**
-     * Construct a IoBuffer from an existing IoBuffer.
-     * @param ioBuffer the IoBuffer we want to copy
-     */
-    public IoBuffer(IoBuffer ioBuffer) {
-        // Find the position to start with
-        BufferNode node = ioBuffer.buffers.getFirst();
-        int pos = 0;
-
-        while (node != null) {
-            if (node.offset + node.buffer.limit() < position) {
-                node = buffers.getNext();
-                pos = node.offset + node.buffer.limit();
-            } else {
-                buffers.add(node.buffer);
-            }
-        }
-
-        position = position - pos;
-        mark = 0;
-        limit = ioBuffer.limit() - pos;
-        type = ioBuffer.type;
-        order = ioBuffer.order();
-    }
-
-    /**
-     * Adds a new ByteBuffer at the end of the list of buffers.
-     * 
-     * @param byteBuffer The added ByteBuffer
-     * @return The modified IoBuffer
-     */
-    public IoBuffer add(ByteBuffer... byteBuffers) {
-        for (ByteBuffer byteBuffer : byteBuffers) {
-            if (byteBuffer.limit() > 0) {
-                buffers.add(byteBuffer);
-            }
-        }
-
-        return this;
-    }
-
-    /**
-     * Allocate a Heap IoBuffer with a defined capacity
-     * @param capacity The number of bytes to store
-     * @return The allocated IoBuffer
-     */
-    public static IoBuffer allocate(int capacity) {
-        if (capacity >= 0) {
-            ByteBuffer byteBuffer = ByteBuffer.allocate(capacity);
-
-            return new IoBuffer(byteBuffer);
-        } else {
-            throw new IllegalArgumentException("Cannot allocate an IoBuffer with a negative value : " + capacity);
-        }
-    }
-
-    /**
-     * Allocate a Direct IoBuffer with a defined capacity
-     * @param capacity The number of bytes to store
-     * @return The allocated IoBuffer
-     */
-    public static IoBuffer allocateDirect(int capacity) {
-        if (capacity >= 0) {
-            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(capacity);
-
-            return new IoBuffer(byteBuffer);
-        } else {
-            throw new IllegalArgumentException("Cannot allocate an IoBuffer with a negative value : " + capacity);
-        }
-    }
-
-    /**
-     * @see ByteBuffer#array()
-     * Returns the byte array which this IoBuffer is based on, up to the sum of each 
-     * contained ByteBuffer's limit().<br/>
-     * This array can be modified, but this won't modify the content of the underlying
-     * ByteBuffer instances, contrary to the ByteBuffer.array() method.
-     * 
-     * @return the byte array which this IoBuffer is based on.
-     * @exception ReadOnlyBufferException if this IoBuffer is based on a read-only array.
-     * @exception UnsupportedOperationException if this IoBuffer is not based on an array.
-     */
-    public byte[] array() {
-        if (isReadOnly()) {
-            throw new ReadOnlyBufferException();
-        }
-
-        if (buffers.size == 0) {
-            return EMPTY_BYTES;
-        }
-
-        byte[] array = new byte[buffers.length];
-        BufferNode node = buffers.getFirst();
-        int pos = 0;
-
-        while (node != null) {
-            ByteBuffer buffer = node.buffer;
-            byte[] src = buffer.array();
-            int length = buffer.limit();
-
-            System.arraycopy(src, 0, array, pos, length);
-            pos += length;
-
-            node = buffers.getNext();
-        }
-
-        return array;
-    }
-
-    /**
-     * @see ByteBuffer#arrayOffset()
-     * Returns the offset of the byte array which this IoBuffer is based on, if
-     * there is one.
-     * <p>
-     * The offset is the index of the array which corresponds to the zero
-     * position of the IoBuffer.
-     *
-     * @return the offset of the byte array which this IoBuffer is based on.
-     * @exception ReadOnlyBufferException if this IoBuffer is based on a read-only array.
-     * @exception UnsupportedOperationException if this IoBuffer is not based on an array.
-     */
-    public int arrayOffset() {
-        if (isReadOnly()) {
-            throw new ReadOnlyBufferException();
-        }
-
-        // The offset is always 0
-        return 0;
-    }
-
-    /**
-     * @see ByteBuffer#asReadOnlyBuffer()
-     */
-    public IoBuffer asReadOnlyBuffer() {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @return the IoBuffer total capacity
-     */
-    public int capacity() {
-        return limit;
-    }
-
-    /**
-     * @see Buffer#clear()
-     * Clears this IoBuffer.
-     * <p>
-     * the following internal changes take place: 
-     * <ul>
-     * <li>the current position is reset back to the start of the buffer</li> 
-     * <li>the value of the buffer limit is made equal to the capacity</li>
-     * <li>and mark is cleared</li>
-     * </ul>
-     * Note that the resulting IoBuffer might be wider than the original one, simply
-     * because we will extent the ByteBuffers limit to their capacity.
-     *
-     * @return this buffer.
-     */
-    public IoBuffer clear() {
-        position = 0;
-        mark = UNSET_MARK;
-
-        BufferNode node = buffers.head;
-        int offset = 0;
-
-        while (node != null) {
-            node.buffer.clear();
-            node.offset = offset;
-            offset += node.buffer.limit();
-            node = node.next;
-        }
-
-        limit = offset;
-        buffers.length = 0;
-        buffers.current = buffers.head;
-
-        return this;
-    }
-
-    /**
-     * @see ByteBuffer#compact()
-     */
-    public IoBuffer compact() {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#compareTo(ByteBuffer)
-     */
-    public int compareTo(IoBuffer buffer) {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#duplicate()
-     */
-    public IoBuffer duplicate() {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#equals(Object)
-     */
-    public boolean equals(Object object) {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see Buffer#flip()
-     * Flips this buffer.
-     * <p>
-     * The limit is set to the current position, then the position is set to
-     * zero, and the mark is cleared.
-     * <p>
-     * The content of this IoBuffer is not changed.
-     *
-     * @return this IoBuffer.
-     */
-    public IoBuffer flip() {
-        limit = position;
-        position = 0;
-        mark = UNSET_MARK;
-
-        return this;
-    }
-
-    /**
-     * Get a single byte for the IoBuffer at the current position. Increment the current position.
-     * @return The byte found a the current position.
-     */
-    public byte get() {
-        if (position >= limit) {
-            // No more byte to read
-            throw new BufferUnderflowException();
-        }
-
-        // find the byte from the current buffer now
-        BufferNode currentNode = buffers.getCurrent();
-
-        // If the position is within the current buffer, then get the data from it
-        int bufferPosition = position - currentNode.offset;
-
-        if (bufferPosition < currentNode.buffer.limit()) {
-            position++;
-
-            return currentNode.buffer.get();
-        } else {
-            // We have exhausted the current buffer, let's see if we have one more
-            currentNode = buffers.getNext();
-
-            if (currentNode == null) {
-                // No more buffers
-                throw new BufferUnderflowException();
-            } else {
-                position++;
-                currentNode.buffer.position(0);
-
-                return currentNode.buffer.get();
-            }
-        }
-    }
-
-    /**
-     * @see ByteBuffer#get(byte[])
-     * Reads bytes from the current position into the specified byte array and
-     * increases the position by the number of bytes read.
-     * <p>
-     * Calling this method has the same effect as
-     * {@code get(dest, 0, dest.length)}.
-     *
-     * @param dest the destination byte array.
-     * @return this IoBuffer.
-     * @exception BufferUnderflowException if {@code dest.length} is greater than {@code remaining()}.
-     */
-    public IoBuffer get(byte[] dst) {
-        if (dst.length > remaining()) {
-            throw new BufferUnderflowException();
-        }
-
-        int size = dst.length;
-        int destPos = 0;
-        BufferNode node = buffers.current;
-
-        while (size > 0) {
-            int length = node.buffer.limit() - node.buffer.position();
-            System.arraycopy(node.buffer.array(), node.buffer.position(), dst, destPos, length);
-            destPos += length;
-            node = buffers.getNext();
-        }
-
-        return this;
-    }
-
-    /**
-     * @see ByteBuffer#get(byte[],int,int)
-     */
-    public IoBuffer get(byte[] dst, int offset, int length) {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#get(int)
-     * Returns the byte at the specified index and does not change the position.
-     * 
-     * @param index the index, must not be negative and less than limit.
-     * @return the byte at the specified index.
-     * @exception IndexOutOfBoundsException if index is invalid.
-     */
-    public byte get(int index) {
-        if ((index < 0) || (index >= limit)) {
-            throw new IndexOutOfBoundsException();
-        }
-
-        BufferNode currentNode = buffers.current;
-        BufferNode node = buffers.getFirst();
-
-        while (node != null) {
-            if (node.offset + node.buffer.limit() > index) {
-                byte result = node.buffer.get(index - node.offset);
-
-                // Reset the initial position before returning
-                buffers.current = currentNode;
-
-                return result;
-            } else {
-                node = buffers.getNext();
-            }
-        }
-
-        // Reset the initial position before returning
-        buffers.current = currentNode;
-
-        // Unlikely to happen
-        throw new IndexOutOfBoundsException();
-    }
-
-    /**
-     * @see ByteBuffer#getChar()
-     */
-    public IoBuffer getChar() {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#getChar(int)
-     */
-    public IoBuffer getChar(int index) {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#getDouble()
-     */
-    public IoBuffer getDouble() {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#getDouble(int)
-     */
-    public IoBuffer getDouble(int index) {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#getFloat()
-     */
-    public IoBuffer getFloat() {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#getFloat(int)
-     */
-    public IoBuffer getFloat(int index) {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#getInt()
-     * Returns the int at the current position and increases the position by 4.
-     * <p>
-     * The 4 bytes starting at the current position are composed into a int
-     * according to the current byte order and returned.
-     *
-     * @return the int at the current position.
-     * @exception BufferUnderflowException if the position is greater than {@code limit - 4}.
-     */
-    public int getInt() {
-        int newPosition = position + 4;
-
-        if (newPosition > limit) {
-            throw new BufferUnderflowException();
-        }
-
-        int result = loadInt(position);
-        position = newPosition;
-
-        return result;
-    }
-
-    /**
-     * Load an int from the underlying byteBuffers, taking the order into account.
-     */
-    private final int loadInt(int index) {
-        int bytes = 0;
-
-        if (order == ByteOrder.BIG_ENDIAN) {
-            for (int i = 0; i < 4; i++) {
-                bytes = bytes << 8;
-                bytes = bytes | (get() & 0xFF);
-            }
-        } else {
-            for (int i = 0; i < 4; i++) {
-                int val = get() & 0xFF;
-                bytes = bytes | (val << (i << 3));
-            }
-        }
-
-        return bytes;
-    }
-
-    /**
-     * @see ByteBuffer#getInt(int)
-     */
-    public IoBuffer getInt(int index) {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#getLong()
-     */
-    public IoBuffer getLong() {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#getLong(int)
-     */
-    public IoBuffer getLong(int index) {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#getShort()
-     */
-    public IoBuffer getShort() {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#getShort(int)
-     */
-    public IoBuffer getShort(int index) {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#hashCode()
-     */
-    public int hashCode() {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see Buffer#hasRemaining()
-     * Indicates if there are elements remaining in this IoBuffer, that is if
-     * {@code position < limit}.
-     *
-     * @return {@code true} if there are elements remaining in this IoBuffer,
-     * {@code false} otherwise.
-     */
-    public boolean hasRemaining() {
-        return position < limit;
-    }
-
-    /**
-     * @see ByteBuffer#isDirect()
-     * Tells if the stored ByteBuffers are Direct buffers or Heap Buffers
-     * @return <code>true</code> if we are storing Direct buffers, <code>false</code> otherwise.
-     */
-    public boolean isDirect() {
-        return type == BufferType.DIRECT;
-    }
-
-    /**
-     * @see Buffer#isReadOnly()
-     * Indicates whether this IoBuffer is read-only.
-     *
-     * @return {@code true} if this IoBuffer is read-only, {@code false}
-     * otherwise.
-     */
-    public boolean isReadOnly() {
-        return readOnly;
-    }
-
-    /**
-     * @return the IoBuffer limit
-     */
-    public int limit() {
-        return limit;
-    }
-
-    /**
-     * @see Buffer#mark()
-     * Marks the current position, so that the position may return to this point
-     * later by calling <code>reset()</code>.
-     *
-     * @return this IoBuffer.
-     */
-    public IoBuffer mark() {
-        mark = position;
-
-        return this;
-    }
-
-    /**
-     * @see ByteBuffer#order()
-     * Returns the byte order used by this Iouffer when converting bytes from/to
-     * other primitive types.
-     * <p>
-     * The default byte order of byte buffer is always
-     * {@link ByteOrder#BIG_ENDIAN BIG_ENDIAN}
-     *
-     * @return the byte order used by this IoBuffer when converting bytes from/to
-     * other primitive types.
-
-     */
-    public ByteOrder order() {
-        return order;
-    }
-
-    /**
-     * @see ByteBuffer#order(ByteOrder)
-     * Sets the byte order of this IoBuffer.
-     * 
-     * @param byteOrder the byte order to set. If {@code null} then the order
-     * will be {@link ByteOrder#LITTLE_ENDIAN LITTLE_ENDIAN}.
-     * @return this IoBuffer.
-     * @see ByteOrder
-     */
-    public IoBuffer order(ByteOrder bo) {
-        if (bo == null) {
-            order = ByteOrder.LITTLE_ENDIAN;
-        } else {
-            order = bo;
-        }
-
-        return this;
-    }
-
-    /**
-     * @see Buffer#position()
-     * @return The current position across all the ByteBuffers contained in the IoBuffer
-     */
-    public int position() {
-        return position;
-    }
-
-    /**
-     * @see Buffer#position(int)
-     * Sets the position in the IoBuffer.
-     * <p>
-     * If the mark is set and it is greater than the new position, then it is
-     * cleared.
-     *
-     * @param newPosition the new position, must be not negative and not greater than
-     * limit.
-     * @return this IoBuffer.
-     * @exception IllegalArgumentException if <code>newPosition</code> is invalid.
-     */
-    public IoBuffer position(int newPosition) {
-        if (newPosition < 0) {
-            throw new IllegalArgumentException("The new position(" + newPosition + ") is negative");
-        }
-
-        if (newPosition >= limit) {
-            throw new IllegalArgumentException("The new position(" + newPosition
-                    + ") is larger than this buffer limit (" + limit());
-        }
-
-        if (buffers.head == null) {
-            throw new IllegalArgumentException("Cannot set a position over an empty buffer");
-        }
-
-        // Find the right current buffer
-        BufferNode currentNode = buffers.getCurrent();
-
-        // The new position might not be on the current buffer.
-        if ((newPosition < currentNode.offset) || (newPosition >= currentNode.offset + currentNode.buffer.limit())) {
-            // Ok, we aren't on the current buffer. Find the new current buffer
-            BufferNode node = buffers.head;
-            int counter = 0;
-
-            while (node != null) {
-                int limit = node.buffer.limit();
-                counter += limit;
-
-                if (counter >= newPosition) {
-                    // Found
-                    currentNode = node;
-                    break;
-                } else {
-                    node = node.next;
-                }
-            }
-        }
-
-        position = newPosition;
-        currentNode.buffer.position(position - currentNode.offset);
-        buffers.current = currentNode;
-
-        return this;
-    }
-
-    /**
-     * @see Buffer#remaining()
-     * Returns the number of remaining elements in this IoBuffer, that is
-     * {@code limit - position}.
-     *
-     * @return the number of remaining elements in this IoBuffer.
-     */
-    public int remaining() {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see Buffer#reset()
-     * Resets the position of this IoBuffer to the <code>mark</code>.
-     * 
-     * @return this IoBuffer.
-     * @exception InvalidMarkException if the mark is not set.
-     */
-    public IoBuffer reset() {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see Buffer#rewind()
-     * Rewinds this IoBuffer.
-     * <p>
-     * The position is set to zero, and the mark is cleared. The content of this
-     * IoBuffer is not changed.
-     *
-     * @return this IoBuffer.
-     */
-    public IoBuffer rewind() {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#slice()
-     */
-    public IoBuffer slice() {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#wrap(byte[])
-     */
-    public IoBuffer wrap(byte[] array) {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @see ByteBuffer#wrap(byte[], int, int)
-     */
-    public IoBuffer wrap(byte[] array, int offset, int length) {
-        // TODO code me !
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Returns a string representing the IoBuffer.
-     * @return a String representation of the IoBuffer
-     */
-    public String toString() {
-        return "IoBuffer[pos=" + position + " lim=" + limit + " mrk=" + mark + "]";
-    }
-
-    //------------------------------------------------------------------------------------------------
-    // private inner data structure
-    //------------------------------------------------------------------------------------------------
-    /**
-     * A container for ByterBuffers stored in the buffers list
-     */
-    private class BufferNode {
-        /** The stored buffer */
-        private ByteBuffer buffer;
-
-        /** The next buffer in the list */
-        private BufferNode next;
-
-        /** The position of this buffer in the IoBuffer list of bytes */
-        private int offset;
-
-        /**
-         * Creates a new entry in the list
-         * @param entry The added ByteBuffer
-         */
-        private BufferNode(ByteBuffer byteBuffer) {
-            this.buffer = byteBuffer;
-        }
-
-        public String toString() {
-            return buffer.toString() + ", Offset:" + offset + (next != null ? " --> \n  " : "");
-        }
-    }
-
-    /**
-     * A LinkedList storing all the ByteBuffers. It can only be browsed forward.
-     */
-    private class BufferList {
-        /** The first ByteBuffer in the list */
-        private BufferNode head;
-
-        /** The last ByteBuffer in the list */
-        private BufferNode tail;
-
-        /** The current ByteBuffer in the list */
-        private BufferNode current;
-
-        /** The number of nodes in the list */
-        private int size;
-
-        /** The number of bytes in the list */
-        private int length;
-
-        /** A flag used to indicate that we already have navigated past the tail of the list. */
-        private boolean pastTail;
-
-        /**
-         * Creates an empty list
-         */
-        private BufferList() {
-            head = tail = current = null;
-            size = 0;
-            length = 0;
-            pastTail = false;
-        }
-
-        /**
-         * Creates a list with one ByteBuffer
-         * @param byteBuffer The added ByteBuffer
-         */
-        private BufferList(ByteBuffer byteBuffer) {
-            BufferNode node = new BufferNode(byteBuffer);
-            head = tail = current = node;
-            size = 1;
-            length = byteBuffer.limit();
-            pastTail = false;
-        }
-
-        /**
-         * Adds a new ByteBuffer in the list
-         * @param byteBuffer The added ByteBuffer
-         */
-        private void add(ByteBuffer byteBuffer) {
-            assert (byteBuffer != null);
-
-            // Check the buffer type
-            if (type == null) {
-                if (byteBuffer.isDirect()) {
-                    type = BufferType.DIRECT;
-                } else {
-                    type = BufferType.HEAP;
-                }
-            } else {
-                if (isDirect() != byteBuffer.isDirect()) {
-                    throw new RuntimeException();
-                }
-            }
-
-            // Check the ByteOrder
-            if (size == 0) {
-                order = byteBuffer.order();
-            } else if (order != byteBuffer.order()) {
-                throw new RuntimeException();
-            }
-
-            BufferNode newNode = new BufferNode(byteBuffer);
-            newNode.offset = length;
-
-            if (size == 0) {
-                head = tail = current = newNode;
-            } else {
-                tail.next = newNode;
-                tail = newNode;
-            }
-
-            size++;
-            length += byteBuffer.limit();
-            limit = length;
-            pastTail = false;
-        }
-
-        /**
-         * Get the first BufferNode in the list. The current pointer will move
-         * forward, after having be reset to the beginning of the list
-         * @return The first BufferNode in the list
-         */
-        private BufferNode getFirst() {
-            if (head == null) {
-                return null;
-            }
-
-            current = head.next;
-            pastTail = false;
-
-            return head;
-        }
-
-        /**
-         * Get the next BufferNode from the list. If this is the first time this method
-         * is called, it will return the same value than a getFirst().
-         * @return The next BufferNode in the list, moving forward in the list at the same time
-         */
-        private BufferNode getNext() {
-            if (current == null) {
-                return null;
-            }
-
-            if (current == tail) {
-                if (pastTail) {
-                    return null;
-                } else {
-                    pastTail = true;
-
-                    return current;
-                }
-            } else {
-                current = current.next;
-
-                return current;
-            }
-        }
-
-        /**
-         * Gets the current BufferNode from the list, if we aren't already past the tail.
-         * @return The current BufferNode
-         */
-        private BufferNode getCurrent() {
-            if (pastTail) {
-                return null;
-            }
-
-            return current;
-        }
-
-        public String toString() {
-            StringBuilder sb = new StringBuilder();
-
-            BufferNode node = head;
-
-            while (node != null) {
-                if (node == current) {
-                    sb.append("**");
-                }
-
-                sb.append(node);
-                node = node.next;
-            }
-
-            return sb.toString();
-        }
-    }
-}