1 package com.opensymphony.module.sitemesh.util;
2
3 import java.io.Reader;
4 import java.io.IOException;
5
6 /**
7 * This class implements a character buffer that can be used as a
8 * character-input stream.
9 *
10 * Modified from the JDK source in that it gets rid of the
11 * ensureOpen() method, so we get unexpected behaviour if the
12 * reader is closed.
13 * <p>
14 * The second modification is that since this class is used
15 * internally by FastPageParser in a single thread, we don't
16 * need any locking or synchronization. Using this class
17 * instead of the standard CharArrayReader improves
18 * FastPageParser performance by 15-20%.
19 *
20 * @author Hani Suleiman
21 */
22 public class CharArrayReader extends Reader
23 {
24 /** The character buffer. */
25 protected char buf[];
26
27 /** The current buffer position. */
28 protected int pos;
29
30 /** The position of mark in buffer. */
31 protected int markedPos = 0;
32
33 /**
34 * The index of the end of this buffer. There is not valid
35 * data at or beyond this index.
36 */
37 protected int count;
38
39 /**
40 * Create an CharArrayReader from the specified array of chars.
41 *
42 * @param buf Input buffer (not copied)
43 */
44 public CharArrayReader(char buf[])
45 {
46 this.buf = buf;
47 this.pos = 0;
48 this.count = buf.length;
49 }
50
51 /**
52 * Create an CharArrayReader from the specified array of chars.
53 *
54 * @param buf Input buffer (not copied)
55 * @param offset Offset of the first char to read
56 * @param length Number of chars to read
57 */
58 public CharArrayReader(char buf[], int offset, int length)
59 {
60 if((offset < 0) || (offset > buf.length) || (length < 0) || ((offset + length) < 0))
61 {
62 throw new IllegalArgumentException();
63 }
64 this.buf = buf;
65 this.pos = offset;
66 this.count = Math.min(offset + length, buf.length);
67 this.markedPos = offset;
68 }
69
70 /**
71 * Read a single character.
72 *
73 * @throws IOException If an I/O error occurs
74 */
75 public int read() throws IOException
76 {
77 if(pos >= count)
78 return -1;
79 else
80 return buf[pos++];
81 }
82
83 /**
84 * Read characters into a portion of an array.
85 *
86 * @param b Destination buffer
87 * @param off Offset at which to start storing characters
88 * @param len Maximum number of characters to read
89 * @return The actual number of characters read, or -1 if
90 * the end of the stream has been reached
91 * @throws IOException If an I/O error occurs
92 */
93 public int read(char b[], int off, int len) throws IOException
94 {
95 if((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0))
96 {
97 throw new IndexOutOfBoundsException();
98 }
99 else if(len == 0)
100 {
101 return 0;
102 }
103
104 if(pos >= count)
105 {
106 return -1;
107 }
108 if(pos + len > count)
109 {
110 len = count - pos;
111 }
112 if(len <= 0)
113 {
114 return 0;
115 }
116 System.arraycopy(buf, pos, b, off, len);
117 pos += len;
118 return len;
119 }
120
121 /**
122 * Skip characters.
123 *
124 * @param n The number of characters to skip
125 * @throws IOException If an I/O error occurs
126 * @return The number of characters actually skipped
127 */
128 public long skip(long n) throws IOException
129 {
130 if(pos + n > count)
131 {
132 n = count - pos;
133 }
134 if(n < 0)
135 {
136 return 0;
137 }
138 pos += n;
139 return n;
140 }
141
142 /**
143 * Tell whether this stream is ready to be read. Character-array readers
144 * are always ready to be read.
145 *
146 * @throws IOException If an I/O error occurs
147 */
148 public boolean ready() throws IOException
149 {
150 return (count - pos) > 0;
151 }
152
153 /**
154 * Tell whether this stream supports the mark() operation, which it does.
155 */
156 public boolean markSupported()
157 {
158 return true;
159 }
160
161 /**
162 * Mark the present position in the stream. Subsequent calls to reset()
163 * will reposition the stream to this point.
164 *
165 * @param readAheadLimit Limit on the number of characters that may be
166 * read while still preserving the mark. Because
167 * the stream's input comes from a character array,
168 * there is no actual limit; hence this argument is
169 * ignored.
170 * @throws IOException If an I/O error occurs
171 */
172 public void mark(int readAheadLimit) throws IOException
173 {
174 markedPos = pos;
175 }
176
177 /**
178 * Reset the stream to the most recent mark, or to the beginning if it has
179 * never been marked.
180 *
181 * @throws IOException If an I/O error occurs
182 */
183 public void reset() throws IOException
184 {
185 pos = markedPos;
186 }
187
188 /**
189 * Close the stream.
190 */
191 public void close()
192 {
193 buf = null;
194 }
195 }