001: /*
002:
003: Derby - Class org.apache.derbyTesting.functionTests.util.streams.LoopingAlphabetReader
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derbyTesting.functionTests.util.streams;
023:
024: import java.io.IOException;
025: import java.io.Reader;
026:
027: /**
028: * A stream returning characters by looping over an alphabet.
029: */
030: public class LoopingAlphabetReader extends Reader {
031:
032: /**
033: * Maximum size of buffer.
034: * Balance between size and memory usage.
035: */
036: private static final int MAX_BUF_SIZE = 32 * 1024;
037: /** The character used for blanks (SPACE). */
038: private static final int SPACE = ' ';
039:
040: /** Number of characters in the reader. */
041: private final long length;
042: /** Number of blanks at the end of stream. */
043: private final int trailingBlanks;
044: /** Remaining non-blank characters. */
045: private long remainingNonBlanks;
046: /** Remaining blanks. */
047: private long remainingBlanks;
048: /**
049: * Internal buffer of characters.
050: * Used by the read-methods with a char[] argument.
051: */
052: private char[] buffer = new char[0];
053: /** The alphabet to draw letters from. */
054: private final CharAlphabet alphabet;
055: /** Tell if the reader is closed or not. */
056: private boolean closed = false;
057:
058: /**
059: * Create a looping modern latin alphabet reader of the specified length.
060: *
061: * @param length the number of characters
062: */
063: public LoopingAlphabetReader(long length) {
064: this (length, 0);
065: }
066:
067: /**
068: * Create a looping modern latin alphabet of the specified length, with
069: * the specified number of trailing blanks.
070: *
071: * The number of non-blank characters is
072: * <code>length - trailingBlanks</code>.
073: *
074: * @param length total number of characters
075: * @param trailingBlanks number of blank characters at the end
076: */
077: public LoopingAlphabetReader(long length, int trailingBlanks) {
078: this .length = length;
079: this .trailingBlanks = trailingBlanks;
080: this .remainingNonBlanks = length - trailingBlanks;
081: this .remainingBlanks = trailingBlanks;
082: this .alphabet = CharAlphabet.modernLatinLowercase();
083: fillBuffer(alphabet.charCount());
084: }
085:
086: /**
087: * Create a looping alphabet of the specified type and length.
088: *
089: * @param length the number of chars in the reader
090: * @param alphabet the alphabet to loop over
091: */
092: public LoopingAlphabetReader(long length, CharAlphabet alphabet) {
093: this (length, alphabet, 0);
094: }
095:
096: /**
097: * Create a looping alphabet of the specified type and length, with
098: * the specified number of trailing blanks.
099: *
100: * The number of non-blank characters is
101: * <code>length - trailingBlanks</code>.
102: *
103: * @param length total number of characters
104: * @param alphabet the alphabet to draw characters from
105: * @param trailingBlanks number of blank characters at the end
106: */
107: public LoopingAlphabetReader(long length, CharAlphabet alphabet,
108: int trailingBlanks) {
109: this .length = length;
110: this .trailingBlanks = trailingBlanks;
111: this .remainingNonBlanks = length - trailingBlanks;
112: this .remainingBlanks = trailingBlanks;
113: this .alphabet = alphabet;
114: fillBuffer(alphabet.charCount());
115: }
116:
117: public int read() throws IOException {
118: ensureOpen();
119: if (remainingBlanks <= 0 && remainingNonBlanks <= 0) {
120: return -1;
121: }
122: if (remainingNonBlanks <= 0) {
123: remainingBlanks--;
124: return SPACE;
125: }
126: remainingNonBlanks--;
127: return alphabet.nextCharAsInt();
128: }
129:
130: public int read(char[] buf, int off, int length) throws IOException {
131: ensureOpen();
132: if (remainingBlanks <= 0 && remainingNonBlanks <= 0) {
133: return -1;
134: }
135: // We can only read as many chars as there are in the stream.
136: int nonBlankLength = Math.min((int) remainingNonBlanks, length);
137: fillBuffer(nonBlankLength);
138: int read = 0;
139: // Find position of next char in the buffer.
140: int cOff = alphabet.nextCharToRead(0);
141: if (nonBlankLength <= (buffer.length - cOff)) {
142: System.arraycopy(buffer, cOff, buf, off, nonBlankLength);
143: remainingNonBlanks -= nonBlankLength;
144: read = nonBlankLength;
145: alphabet.nextCharToRead(nonBlankLength);
146: } else {
147: // Must read several times from the buffer.
148: int toRead = 0;
149: while (remainingNonBlanks > 0 && read < nonBlankLength) {
150: cOff = alphabet.nextCharToRead(toRead);
151: toRead = Math.min(buffer.length - cOff, nonBlankLength
152: - read);
153: System.arraycopy(buffer, cOff, buf, off + read, toRead);
154: remainingNonBlanks -= toRead;
155: read += toRead;
156: }
157: cOff = alphabet.nextCharToRead(toRead);
158: }
159: if (read < length && remainingBlanks > 0) {
160: read += fillBlanks(buf, off + read, length - read);
161: }
162: return read;
163: }
164:
165: /**
166: * Reset the stream.
167: */
168: public void reset() throws IOException {
169: ensureOpen();
170: remainingNonBlanks = length - trailingBlanks;
171: remainingBlanks = trailingBlanks;
172: alphabet.reset();
173: }
174:
175: /**
176: * Return remaining characters in the stream.
177: */
178: public int available() {
179: return (int) (remainingNonBlanks + remainingBlanks);
180: }
181:
182: /**
183: * Close the reader.
184: */
185: public void close() {
186: this .closed = true;
187: }
188:
189: /**
190: * Fill internal buffer of character sequence.
191: *
192: * @param bufSize the wanted size, might be ignored if too big
193: */
194: private void fillBuffer(int bufSize) {
195: if (bufSize > MAX_BUF_SIZE) {
196: bufSize = MAX_BUF_SIZE;
197: }
198: if (bufSize <= buffer.length) {
199: return;
200: }
201: int curOff = alphabet.nextCharToRead(0);
202: // First letter in buffer is always the first letter in the alphabet.
203: alphabet.reset();
204: buffer = new char[bufSize];
205: for (int i = 0; i < bufSize; i++) {
206: buffer[i] = alphabet.nextChar();
207: }
208: // Must reset internal state of the alphabet, as we have not yet
209: // delivered any bytes.
210: alphabet.reset();
211: alphabet.nextCharToRead(curOff);
212: }
213:
214: /**
215: * Fill array with blanks (SPACE).
216: *
217: * @param buf array to fill
218: * @param off starting offset
219: * @param length maximum number of blanks to fill in
220: */
221: private int fillBlanks(char[] buf, int off, int length) {
222: int i = 0;
223: for (; i < length; i++) {
224: if (remainingBlanks > 0) {
225: buf[off + i] = SPACE;
226: remainingBlanks--;
227: } else {
228: break;
229: }
230: }
231: return i;
232: }
233:
234: /**
235: * Ensure reader is open.
236: *
237: * @throws IOException if reader is closed
238: */
239: private final void ensureOpen() throws IOException {
240: if (closed) {
241: throw new IOException("Reader closed");
242: }
243: }
244: } // End class LoopingAlphabetReader
|