001: package com.opensymphony.module.sitemesh.util;
002:
003: import java.io.Reader;
004: import java.io.IOException;
005:
006: /**
007: * This class implements a character buffer that can be used as a
008: * character-input stream.
009: *
010: * Modified from the JDK source in that it gets rid of the
011: * ensureOpen() method, so we get unexpected behaviour if the
012: * reader is closed.
013: * <p>
014: * The second modification is that since this class is used
015: * internally by FastPageParser in a single thread, we don't
016: * need any locking or synchronization. Using this class
017: * instead of the standard CharArrayReader improves
018: * FastPageParser performance by 15-20%.
019: *
020: * @author Hani Suleiman
021: */
022: public class CharArrayReader extends Reader {
023: /** The character buffer. */
024: protected char buf[];
025:
026: /** The current buffer position. */
027: protected int pos;
028:
029: /** The position of mark in buffer. */
030: protected int markedPos = 0;
031:
032: /**
033: * The index of the end of this buffer. There is not valid
034: * data at or beyond this index.
035: */
036: protected int count;
037:
038: /**
039: * Create an CharArrayReader from the specified array of chars.
040: *
041: * @param buf Input buffer (not copied)
042: */
043: public CharArrayReader(char buf[]) {
044: this .buf = buf;
045: this .pos = 0;
046: this .count = buf.length;
047: }
048:
049: /**
050: * Create an CharArrayReader from the specified array of chars.
051: *
052: * @param buf Input buffer (not copied)
053: * @param offset Offset of the first char to read
054: * @param length Number of chars to read
055: */
056: public CharArrayReader(char buf[], int offset, int length) {
057: if ((offset < 0) || (offset > buf.length) || (length < 0)
058: || ((offset + length) < 0)) {
059: throw new IllegalArgumentException();
060: }
061: this .buf = buf;
062: this .pos = offset;
063: this .count = Math.min(offset + length, buf.length);
064: this .markedPos = offset;
065: }
066:
067: /**
068: * Read a single character.
069: *
070: * @throws IOException If an I/O error occurs
071: */
072: public int read() throws IOException {
073: if (pos >= count)
074: return -1;
075: else
076: return buf[pos++];
077: }
078:
079: /**
080: * Read characters into a portion of an array.
081: *
082: * @param b Destination buffer
083: * @param off Offset at which to start storing characters
084: * @param len Maximum number of characters to read
085: * @return The actual number of characters read, or -1 if
086: * the end of the stream has been reached
087: * @throws IOException If an I/O error occurs
088: */
089: public int read(char b[], int off, int len) throws IOException {
090: if ((off < 0) || (off > b.length) || (len < 0)
091: || ((off + len) > b.length) || ((off + len) < 0)) {
092: throw new IndexOutOfBoundsException();
093: } else if (len == 0) {
094: return 0;
095: }
096:
097: if (pos >= count) {
098: return -1;
099: }
100: if (pos + len > count) {
101: len = count - pos;
102: }
103: if (len <= 0) {
104: return 0;
105: }
106: System.arraycopy(buf, pos, b, off, len);
107: pos += len;
108: return len;
109: }
110:
111: /**
112: * Skip characters.
113: *
114: * @param n The number of characters to skip
115: * @throws IOException If an I/O error occurs
116: * @return The number of characters actually skipped
117: */
118: public long skip(long n) throws IOException {
119: if (pos + n > count) {
120: n = count - pos;
121: }
122: if (n < 0) {
123: return 0;
124: }
125: pos += n;
126: return n;
127: }
128:
129: /**
130: * Tell whether this stream is ready to be read. Character-array readers
131: * are always ready to be read.
132: *
133: * @throws IOException If an I/O error occurs
134: */
135: public boolean ready() throws IOException {
136: return (count - pos) > 0;
137: }
138:
139: /**
140: * Tell whether this stream supports the mark() operation, which it does.
141: */
142: public boolean markSupported() {
143: return true;
144: }
145:
146: /**
147: * Mark the present position in the stream. Subsequent calls to reset()
148: * will reposition the stream to this point.
149: *
150: * @param readAheadLimit Limit on the number of characters that may be
151: * read while still preserving the mark. Because
152: * the stream's input comes from a character array,
153: * there is no actual limit; hence this argument is
154: * ignored.
155: * @throws IOException If an I/O error occurs
156: */
157: public void mark(int readAheadLimit) throws IOException {
158: markedPos = pos;
159: }
160:
161: /**
162: * Reset the stream to the most recent mark, or to the beginning if it has
163: * never been marked.
164: *
165: * @throws IOException If an I/O error occurs
166: */
167: public void reset() throws IOException {
168: pos = markedPos;
169: }
170:
171: /**
172: * Close the stream.
173: */
174: public void close() {
175: buf = null;
176: }
177: }
|