summaryrefslogtreecommitdiffstats
path: root/simple/simple-transport/src/main/java/org/simpleframework/transport/TransportCursor.java
diff options
context:
space:
mode:
Diffstat (limited to 'simple/simple-transport/src/main/java/org/simpleframework/transport/TransportCursor.java')
-rw-r--r--simple/simple-transport/src/main/java/org/simpleframework/transport/TransportCursor.java260
1 files changed, 260 insertions, 0 deletions
diff --git a/simple/simple-transport/src/main/java/org/simpleframework/transport/TransportCursor.java b/simple/simple-transport/src/main/java/org/simpleframework/transport/TransportCursor.java
new file mode 100644
index 0000000..d25cb90
--- /dev/null
+++ b/simple/simple-transport/src/main/java/org/simpleframework/transport/TransportCursor.java
@@ -0,0 +1,260 @@
+/*
+ * TransportCursor.java February 2007
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package org.simpleframework.transport;
+
+import java.io.IOException;
+
+/**
+ * The <code>TransportCursor</code> object represents a cursor that
+ * can read and buffer data from an underlying transport. If the
+ * number of bytes read from the cursor is more than required for
+ * the HTTP request then those bytes can be pushed back in to the
+ * cursor using the <code>reset</code> method. This will only allow
+ * the last read to be reset within the cursor safely.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.transport.Transport
+ */
+public class TransportCursor implements ByteCursor {
+
+ /**
+ * This is the stream for the bytes read by this cursor object.
+ */
+ private ByteReader reader;
+
+ /**
+ * This is the buffer used to collect the bytes pushed back.
+ */
+ private byte[] buffer;
+
+ /**
+ * This is the number of bytes that have been pushed back.
+ */
+ private int count;
+
+ /**
+ * This is the mark from the last read from this cursor object.
+ */
+ private int mark;
+
+ /**
+ * This is the position to read data from the internal buffer.
+ */
+ private int pos;
+
+ /**
+ * This is the maximum number of bytes that can be pushed back.
+ */
+ private int limit;
+
+ /**
+ * Constructor for the <code>TransportCursor</code> object. This
+ * requires a transport to read the bytes from. By default this
+ * will create a buffer of of the specified size to read the
+ * input in to which enabled bytes to be buffered internally.
+ *
+ * @param transport this is the underlying transport to use
+ */
+ public TransportCursor(Transport transport) {
+ this(transport, 2048);
+ }
+
+ /**
+ * Constructor for the <code>TransportCursor</code> object. This
+ * requires a transport to read the bytes from. By default this
+ * will create a buffer of of the specified size to read the
+ * input in to which enabled bytes to be buffered internally.
+ *
+ * @param transport this is the underlying transport to use
+ * @param size this is the size of the internal buffer to use
+ */
+ public TransportCursor(Transport transport, int size) {
+ this.reader = new TransportReader(transport, size);
+ this.buffer = new byte[0];
+ this.limit = size;
+ }
+
+ /**
+ * Determines whether the cursor is still open. The cursor is
+ * considered open if there are still bytes to read. If there is
+ * still bytes buffered and the underlying transport is closed
+ * then the cursor is still considered open.
+ *
+ * @return true if there is nothing more to be read from this
+ */
+ public boolean isOpen() throws IOException {
+ return reader.isOpen();
+ }
+
+ /**
+ * Determines whether the cursor is ready for reading. When the
+ * cursor is ready then it guarantees that some amount of bytes
+ * can be read from the underlying stream without blocking.
+ *
+ * @return true if some data can be read without blocking
+ */
+ public boolean isReady() throws IOException {
+ return ready() > 0;
+ }
+
+ /**
+ * Provides the number of bytes that can be read from the stream
+ * without blocking. This is typically the number of buffered or
+ * available bytes within the stream. When this reaches zero then
+ * the cursor may perform a blocking read.
+ *
+ * @return the number of bytes that can be read without blocking
+ */
+ public int ready() throws IOException {
+ if(count > 0) {
+ return count;
+ }
+ return reader.ready();
+ }
+
+ /**
+ * Reads a block of bytes from the underlying stream. This will
+ * read up to the requested number of bytes from the underlying
+ * stream. If there are no ready bytes on the stream this can
+ * return zero, representing the fact that nothing was read.
+ *
+ * @param data this is the array to read the bytes in to
+ *
+ * @return this returns the number of bytes read from the stream
+ */
+ public int read(byte[] data) throws IOException {
+ return read(data, 0, data.length);
+ }
+
+ /**
+ * Reads a block of bytes from the underlying stream. This will
+ * read up to the requested number of bytes from the underlying
+ * stream. If there are no ready bytes on the stream this can
+ * return zero, representing the fact that nothing was read.
+ *
+ * @param data this is the array to read the bytes in to
+ * @param off this is the offset to begin writing the bytes to
+ * @param len this is the number of bytes that are requested
+ *
+ * @return this returns the number of bytes read from the stream
+ */
+ public int read(byte[] data, int off, int len) throws IOException {
+ if(count <= 0) {
+ mark = pos;
+ return reader.read(data, off, len);
+ }
+ int size = Math.min(count, len);
+
+ if(size > 0) {
+ System.arraycopy(buffer, pos, data, off, size);
+ mark = pos;
+ pos += size;
+ count -= size;
+ }
+ return size;
+ }
+
+ /**
+ * Pushes the provided data on to the cursor. Data pushed on to
+ * the cursor will be the next data read from the cursor. This
+ * complements the <code>reset</code> method which will reset
+ * the cursors position on a stream. Allowing data to be pushed
+ * on to the cursor allows more flexibility.
+ *
+ * @param data this is the data to be pushed on to the cursor
+ */
+ public void push(byte[] data) throws IOException {
+ push(data, 0, data.length);
+ }
+
+ /**
+ * Pushes the provided data on to the cursor. Data pushed on to
+ * the cursor will be the next data read from the cursor. This
+ * complements the <code>reset</code> method which will reset
+ * the cursors position on a stream. Allowing data to be pushed
+ * on to the cursor allows more flexibility.
+ *
+ * @param data this is the data to be pushed on to the cursor
+ * @param off this is the offset to begin reading the bytes
+ * @param len this is the number of bytes that are to be used
+ */
+ public void push(byte[] data, int off, int len) throws IOException {
+ int size = buffer.length;
+
+ if(size < len + count) {
+ expand(len + count);
+ }
+ int start = pos - len;
+
+ if(len > 0) {
+ System.arraycopy(data, off, buffer, start, len);
+ mark = start;
+ pos = start;
+ count += len;
+ }
+ }
+
+ /**
+ * This is used to ensure that there is enough space in the buffer
+ * to allow for more bytes to be added. If the buffer is already
+ * larger than the required capacity the this will do nothing.
+ *
+ * @param capacity the minimum size needed for the buffer
+ */
+ private void expand(int capacity) throws IOException {
+ if(capacity > limit) {
+ throw new TransportException("Capacity limit exceeded");
+ }
+ byte[] temp = new byte[capacity];
+ int start = capacity - count;
+ int shift = pos - mark
+ ;
+ if(count > 0) {
+ System.arraycopy(buffer, pos, temp, start, count);
+ }
+ pos = capacity - count;
+ mark = pos - shift;
+ buffer = temp;
+ }
+
+ /**
+ * Moves the cursor backward within the stream. This ensures
+ * that any bytes read from the last read can be pushed back
+ * in to the stream so that they can be read again. This will
+ * throw an exception if the reset can not be performed.
+ *
+ * @param size this is the number of bytes to reset back
+ *
+ * @return this is the number of bytes that have been reset
+ */
+ public int reset(int size) throws IOException {
+ if(mark == pos) {
+ return reader.reset(size);
+ }
+ if(pos - size < mark) {
+ size = pos - mark;
+ }
+ if(size > 0) {
+ count += size;
+ pos -= size;
+ }
+ return size;
+ }
+}