001: /*
002: * Circular Character Buffer
003: * Copyright (C) 2002 Stephen Ostermiller
004: * http://ostermiller.org/contact.pl?regarding=Java+Utilities
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * See COPYING.TXT for details.
017: */
018: package com.Ostermiller.util;
019:
020: import java.io.*;
021:
022: /**
023: * Implements the Circular Buffer producer/consumer model for characters.
024: * More information about this class is available from <a target="_top" href=
025: * "http://ostermiller.org/utils/CircularCharBuffer.html">ostermiller.org</a>.
026: * <p>
027: * Using this class is a simpler alternative to using a PipedReader
028: * and a PipedWriter. PipedReaders and PipedWriters don't support the
029: * mark operation, don't allow you to control buffer sizes that they use,
030: * and have a more complicated API that requires instantiating two
031: * classes and connecting them.
032: * <p>
033: * This class is thread safe.
034: *
035: * @see CircularByteBuffer
036: * @see CircularObjectBuffer
037: *
038: * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
039: * @since ostermillerutils 1.00.00
040: */
041: public class CircularCharBuffer {
042:
043: /**
044: * The default size for a circular character buffer.
045: *
046: * @since ostermillerutils 1.00.00
047: */
048: private final static int DEFAULT_SIZE = 1024;
049:
050: /**
051: * A buffer that will grow as things are added.
052: *
053: * @since ostermillerutils 1.00.00
054: */
055: public final static int INFINITE_SIZE = -1;
056:
057: /**
058: * The circular buffer.
059: * <p>
060: * The actual capacity of the buffer is one less than the actual length
061: * of the buffer so that an empty and a full buffer can be
062: * distinguished. An empty buffer will have the markPostion and the
063: * writePosition equal to each other. A full buffer will have
064: * the writePosition one less than the markPostion.
065: * <p>
066: * There are three important indexes into the buffer:
067: * The readPosition, the writePosition, and the markPosition.
068: * If the Reader has never been marked, the readPosition and
069: * the markPosition should always be the same. The characters
070: * available to be read go from the readPosition to the writePosition,
071: * wrapping around the end of the buffer. The space available for writing
072: * goes from the write position to one less than the markPosition,
073: * wrapping around the end of the buffer. The characters that have
074: * been saved to support a reset() of the Reader go from markPosition
075: * to readPosition, wrapping around the end of the buffer.
076: *
077: * @since ostermillerutils 1.00.00
078: */
079: protected char[] buffer;
080: /**
081: * Index of the first character available to be read.
082: *
083: * @since ostermillerutils 1.00.00
084: */
085: protected volatile int readPosition = 0;
086: /**
087: * Index of the first character available to be written.
088: *
089: * @since ostermillerutils 1.00.00
090: */
091: protected volatile int writePosition = 0;
092: /**
093: * Index of the first saved character. (To support stream marking.)
094: *
095: * @since ostermillerutils 1.00.00
096: */
097: protected volatile int markPosition = 0;
098: /**
099: * Number of characters that have to be saved
100: * to support mark() and reset() on the Reader.
101: *
102: * @since ostermillerutils 1.00.00
103: */
104: protected volatile int markSize = 0;
105: /**
106: * If this buffer is infinite (should resize itself when full)
107: *
108: * @since ostermillerutils 1.00.00
109: */
110: protected volatile boolean infinite = false;
111: /**
112: * True if a write to a full buffer should block until the buffer
113: * has room, false if the write method should throw an IOException
114: *
115: * @since ostermillerutils 1.00.00
116: */
117: protected boolean blockingWrite = true;
118: /**
119: * The Reader that can empty this buffer.
120: *
121: * @since ostermillerutils 1.00.00
122: */
123: protected Reader reader = new CircularCharBufferReader();
124: /**
125: * true if the close() method has been called on the Reader
126: *
127: * @since ostermillerutils 1.00.00
128: */
129: protected boolean readerClosed = false;
130: /**
131: * The Writer that can fill this buffer.
132: *
133: * @since ostermillerutils 1.00.00
134: */
135: protected Writer writer = new CircularCharBufferWriter();
136: /**
137: * true if the close() method has been called on the writer
138: *
139: * @since ostermillerutils 1.00.00
140: */
141: protected boolean writerClosed = false;
142:
143: /**
144: * Make this buffer ready for reuse. The contents of the buffer
145: * will be cleared and the streams associated with this buffer
146: * will be reopened if they had been closed.
147: *
148: * @since ostermillerutils 1.00.00
149: */
150: public void clear() {
151: synchronized (this ) {
152: readPosition = 0;
153: writePosition = 0;
154: markPosition = 0;
155: readerClosed = false;
156: writerClosed = false;
157: }
158: }
159:
160: /**
161: * Retrieve a Writer that can be used to fill
162: * this buffer.
163: * <p>
164: * Write methods may throw a BufferOverflowException if
165: * the buffer is not large enough. A large enough buffer
166: * size must be chosen so that this does not happen or
167: * the caller must be prepared to catch the exception and
168: * try again once part of the buffer has been consumed.
169: *
170: *
171: * @return the producer for this buffer.
172: *
173: * @since ostermillerutils 1.00.00
174: */
175: public Writer getWriter() {
176: return writer;
177: }
178:
179: /**
180: * Retrieve a Reader that can be used to empty
181: * this buffer.
182: * <p>
183: * This Reader supports marks at the expense
184: * of the buffer size.
185: *
186: * @return the consumer for this buffer.
187: *
188: * @since ostermillerutils 1.00.00
189: */
190: public Reader getReader() {
191: return reader;
192: }
193:
194: /**
195: * Get number of characters that are available to be read.
196: * <p>
197: * Note that the number of characters available plus
198: * the number of characters free may not add up to the
199: * capacity of this buffer, as the buffer may reserve some
200: * space for other purposes.
201: *
202: * @return the size in characters of this buffer
203: *
204: * @since ostermillerutils 1.00.00
205: */
206: public int getAvailable() {
207: synchronized (this ) {
208: return available();
209: }
210: }
211:
212: /**
213: * Get the number of characters this buffer has free for
214: * writing.
215: * <p>
216: * Note that the number of characters available plus
217: * the number of characters free may not add up to the
218: * capacity of this buffer, as the buffer may reserve some
219: * space for other purposes.
220: *
221: * @return the available space in characters of this buffer
222: *
223: * @since ostermillerutils 1.00.00
224: */
225: public int getSpaceLeft() {
226: synchronized (this ) {
227: return spaceLeft();
228: }
229: }
230:
231: /**
232: * Get the capacity of this buffer.
233: * <p>
234: * Note that the number of characters available plus
235: * the number of characters free may not add up to the
236: * capacity of this buffer, as the buffer may reserve some
237: * space for other purposes.
238: *
239: * @return the size in characters of this buffer
240: *
241: * @since ostermillerutils 1.00.00
242: */
243: public int getSize() {
244: synchronized (this ) {
245: return buffer.length;
246: }
247: }
248:
249: /**
250: * double the size of the buffer
251: *
252: * @since ostermillerutils 1.00.00
253: */
254: private void resize() {
255: char[] newBuffer = new char[buffer.length * 2];
256: int marked = marked();
257: int available = available();
258: if (markPosition <= writePosition) {
259: // any space between the mark and
260: // the first write needs to be saved.
261: // In this case it is all in one piece.
262: int length = writePosition - markPosition;
263: System
264: .arraycopy(buffer, markPosition, newBuffer, 0,
265: length);
266: } else {
267: int length1 = buffer.length - markPosition;
268: System.arraycopy(buffer, markPosition, newBuffer, 0,
269: length1);
270: int length2 = writePosition;
271: System.arraycopy(buffer, 0, newBuffer, length1, length2);
272: }
273: buffer = newBuffer;
274: markPosition = 0;
275: readPosition = marked;
276: writePosition = marked + available;
277: }
278:
279: /**
280: * Space available in the buffer which can be written.
281: *
282: * @since ostermillerutils 1.00.00
283: */
284: private int spaceLeft() {
285: if (writePosition < markPosition) {
286: // any space between the first write and
287: // the mark except one character is available.
288: // In this case it is all in one piece.
289: return (markPosition - writePosition - 1);
290: }
291: // space at the beginning and end.
292: return ((buffer.length - 1) - (writePosition - markPosition));
293: }
294:
295: /**
296: * Characters available for reading.
297: *
298: * @since ostermillerutils 1.00.00
299: */
300: private int available() {
301: if (readPosition <= writePosition) {
302: // any space between the first read and
303: // the first write is available. In this case i
304: // is all in one piece.
305: return (writePosition - readPosition);
306: }
307: // space at the beginning and end.
308: return (buffer.length - (readPosition - writePosition));
309: }
310:
311: /**
312: * Characters saved for supporting marks.
313: *
314: * @since ostermillerutils 1.00.00
315: */
316: private int marked() {
317: if (markPosition <= readPosition) {
318: // any space between the markPosition and
319: // the first write is marked. In this case i
320: // is all in one piece.
321: return (readPosition - markPosition);
322: }
323: // space at the beginning and end.
324: return (buffer.length - (markPosition - readPosition));
325: }
326:
327: /**
328: * If we have passed the markSize reset the
329: * mark so that the space can be used.
330: *
331: * @since ostermillerutils 1.00.00
332: */
333: private void ensureMark() {
334: if (marked() >= markSize) {
335: markPosition = readPosition;
336: markSize = 0;
337: }
338: }
339:
340: /**
341: * Create a new buffer with a default capacity.
342: * Writing to a full buffer will block until space
343: * is available rather than throw an exception.
344: *
345: * @since ostermillerutils 1.00.00
346: */
347: public CircularCharBuffer() {
348: this (DEFAULT_SIZE, true);
349: }
350:
351: /**
352: * Create a new buffer with given capacity.
353: * Writing to a full buffer will block until space
354: * is available rather than throw an exception.
355: * <p>
356: * Note that the buffer may reserve some characters for
357: * special purposes and capacity number of characters may
358: * not be able to be written to the buffer.
359: * <p>
360: * Note that if the buffer is of INFINITE_SIZE it will
361: * neither block or throw exceptions, but rather grow
362: * without bound.
363: *
364: * @param size desired capacity of the buffer in characters or CircularCharBuffer.INFINITE_SIZE
365: *
366: * @since ostermillerutils 1.00.00
367: */
368: public CircularCharBuffer(int size) {
369: this (size, true);
370: }
371:
372: /**
373: * Create a new buffer with a default capacity and
374: * given blocking behavior.
375: *
376: * @param blockingWrite true writing to a full buffer should block
377: * until space is available, false if an exception should
378: * be thrown instead.
379: *
380: * @since ostermillerutils 1.00.00
381: */
382: public CircularCharBuffer(boolean blockingWrite) {
383: this (DEFAULT_SIZE, blockingWrite);
384: }
385:
386: /**
387: * Create a new buffer with the given capacity and
388: * blocking behavior.
389: * <p>
390: * Note that the buffer may reserve some characters for
391: * special purposes and capacity number of characters may
392: * not be able to be written to the buffer.
393: * <p>
394: * Note that if the buffer is of CircularCharBuffer.INFINITE_SIZE it will
395: * neither block or throw exceptions, but rather grow
396: * without bound.
397: *
398: * @param size desired capacity of the buffer in characters or CircularCharBuffer.INFINITE_SIZE
399: * @param blockingWrite true writing to a full buffer should block
400: * until space is available, false if an exception should
401: * be thrown instead.
402: *
403: * @since ostermillerutils 1.00.00
404: */
405: public CircularCharBuffer(int size, boolean blockingWrite) {
406: if (size == INFINITE_SIZE) {
407: buffer = new char[DEFAULT_SIZE];
408: infinite = true;
409: } else {
410: buffer = new char[size];
411: infinite = false;
412: }
413: this .blockingWrite = blockingWrite;
414: }
415:
416: /**
417: * Class for reading from a circular character buffer.
418: *
419: * @since ostermillerutils 1.00.00
420: */
421: protected class CircularCharBufferReader extends Reader {
422:
423: /**
424: * Close the stream. Once a stream has been closed, further read(), ready(),
425: * mark(), or reset() invocations will throw an IOException. Closing a
426: * previously-closed stream, however, has no effect.
427: *
428: * @throws IOException never.
429: *
430: * @since ostermillerutils 1.00.00
431: */
432: @Override
433: public void close() throws IOException {
434: synchronized (CircularCharBuffer.this ) {
435: readerClosed = true;
436: }
437: }
438:
439: /**
440: * Mark the present position in the stream. Subsequent calls to reset() will
441: * attempt to reposition the stream to this point.
442: * <p>
443: * The readAheadLimit must be less than the size of circular buffer.
444: *
445: * @param readAheadLimit Limit on the number of characters that may be read while
446: * still preserving the mark. After reading this many characters, attempting to
447: * reset the stream will fail.
448: * @throws IOException if the stream is closed, or the buffer size is greater
449: * than or equal to the readAheadLimit.
450: *
451: * @since ostermillerutils 1.00.00
452: */
453: @Override
454: public void mark(int readAheadLimit) throws IOException {
455: synchronized (CircularCharBuffer.this ) {
456: if (readerClosed)
457: throw new IOException(
458: "Reader has been closed; cannot mark a closed Reader.");
459: if (buffer.length - 1 <= readAheadLimit)
460: throw new IOException(
461: "Cannot mark stream, readAheadLimit bigger than buffer size.");
462: markSize = readAheadLimit;
463: markPosition = readPosition;
464: }
465: }
466:
467: /**
468: * Tell whether this stream supports the mark() operation.
469: *
470: * @return true, mark is supported.
471: *
472: * @since ostermillerutils 1.00.00
473: */
474: @Override
475: public boolean markSupported() {
476: return true;
477: }
478:
479: /**
480: * Read a single character.
481: * This method will block until a character is available, an I/O error occurs,
482: * or the end of the stream is reached.
483: *
484: * @return The character read, as an integer in the range 0 to 65535 (0x00-0xffff),
485: * or -1 if the end of the stream has been reached
486: * @throws IOException if the stream is closed.
487: *
488: * @since ostermillerutils 1.00.00
489: */
490: @Override
491: public int read() throws IOException {
492: while (true) {
493: synchronized (CircularCharBuffer.this ) {
494: if (readerClosed)
495: throw new IOException(
496: "Reader has been closed; cannot read from a closed Reader.");
497: int available = available();
498: if (available > 0) {
499: int result = buffer[readPosition] & 0xffff;
500: readPosition++;
501: if (readPosition == buffer.length) {
502: readPosition = 0;
503: }
504: ensureMark();
505: return result;
506: } else if (writerClosed) {
507: return -1;
508: }
509: }
510: try {
511: Thread.sleep(100);
512: } catch (Exception x) {
513: throw new IOException(
514: "Blocking read operation interrupted.");
515: }
516: }
517: }
518:
519: /**
520: * Read characters into an array.
521: * This method will block until some input is available,
522: * an I/O error occurs, or the end of the stream is reached.
523: *
524: * @param cbuf Destination buffer.
525: * @return The number of characters read, or -1 if the end of
526: * the stream has been reached
527: * @throws IOException if the stream is closed.
528: *
529: * @since ostermillerutils 1.00.00
530: */
531: @Override
532: public int read(char[] cbuf) throws IOException {
533: return read(cbuf, 0, cbuf.length);
534: }
535:
536: /**
537: * Read characters into a portion of an array.
538: * This method will block until some input is available,
539: * an I/O error occurs, or the end of the stream is reached.
540: *
541: * @param cbuf Destination buffer.
542: * @param off Offset at which to start storing characters.
543: * @param len Maximum number of characters to read.
544: * @return The number of characters read, or -1 if the end of
545: * the stream has been reached
546: * @throws IOException if the stream is closed.
547: *
548: * @since ostermillerutils 1.00.00
549: */
550: @Override
551: public int read(char[] cbuf, int off, int len)
552: throws IOException {
553: while (true) {
554: synchronized (CircularCharBuffer.this ) {
555: if (readerClosed)
556: throw new IOException(
557: "Reader has been closed; cannot read from a closed Reader.");
558: int available = available();
559: if (available > 0) {
560: int length = Math.min(len, available);
561: int firstLen = Math.min(length, buffer.length
562: - readPosition);
563: int secondLen = length - firstLen;
564: System.arraycopy(buffer, readPosition, cbuf,
565: off, firstLen);
566: if (secondLen > 0) {
567: System.arraycopy(buffer, 0, cbuf, off
568: + firstLen, secondLen);
569: readPosition = secondLen;
570: } else {
571: readPosition += length;
572: }
573: if (readPosition == buffer.length) {
574: readPosition = 0;
575: }
576: ensureMark();
577: return length;
578: } else if (writerClosed) {
579: return -1;
580: }
581: }
582: try {
583: Thread.sleep(100);
584: } catch (Exception x) {
585: throw new IOException(
586: "Blocking read operation interrupted.");
587: }
588: }
589: }
590:
591: /**
592: * Tell whether this stream is ready to be read.
593: *
594: * @return True if the next read() is guaranteed not to block for input,
595: * false otherwise. Note that returning false does not guarantee that
596: * the next read will block.
597: * @throws IOException if the stream is closed.
598: *
599: * @since ostermillerutils 1.00.00
600: */
601: @Override
602: public boolean ready() throws IOException {
603: synchronized (CircularCharBuffer.this ) {
604: if (readerClosed)
605: throw new IOException(
606: "Reader has been closed, it is not ready.");
607: return (available() > 0);
608: }
609: }
610:
611: /**
612: * Reset the stream.
613: * If the stream has been marked, then attempt to reposition i
614: * at the mark. If the stream has not been marked, or more characters
615: * than the readAheadLimit have been read, this method has no effect.
616: *
617: * @throws IOException if the stream is closed.
618: *
619: * @since ostermillerutils 1.00.00
620: */
621: @Override
622: public void reset() throws IOException {
623: synchronized (CircularCharBuffer.this ) {
624: if (readerClosed)
625: throw new IOException(
626: "Reader has been closed; cannot reset a closed Reader.");
627: readPosition = markPosition;
628: }
629: }
630:
631: /**
632: * Skip characters.
633: * This method will block until some characters are available,
634: * an I/O error occurs, or the end of the stream is reached.
635: *
636: * @param n The number of characters to skip
637: * @return The number of characters actually skipped
638: * @throws IllegalArgumentException if n is negative.
639: * @throws IOException if the stream is closed.
640: *
641: * @since ostermillerutils 1.00.00
642: */
643: @Override
644: public long skip(long n) throws IOException,
645: IllegalArgumentException {
646: while (true) {
647: synchronized (CircularCharBuffer.this ) {
648: if (readerClosed)
649: throw new IOException(
650: "Reader has been closed; cannot skip characters on a closed Reader.");
651: int available = available();
652: if (available > 0) {
653: int length = Math.min((int) n, available);
654: int firstLen = Math.min(length, buffer.length
655: - readPosition);
656: int secondLen = length - firstLen;
657: if (secondLen > 0) {
658: readPosition = secondLen;
659: } else {
660: readPosition += length;
661: }
662: if (readPosition == buffer.length) {
663: readPosition = 0;
664: }
665: ensureMark();
666: return length;
667: } else if (writerClosed) {
668: return 0;
669: }
670: }
671: try {
672: Thread.sleep(100);
673: } catch (Exception x) {
674: throw new IOException(
675: "Blocking read operation interrupted.");
676: }
677: }
678: }
679: }
680:
681: /**
682: * Class for writing to a circular character buffer.
683: * If the buffer is full, the writes will either block
684: * until there is some space available or throw an IOException
685: * based on the CircularCharBuffer's preference.
686: *
687: * @since ostermillerutils 1.00.00
688: */
689: protected class CircularCharBufferWriter extends Writer {
690:
691: /**
692: * Close the stream, flushing it first.
693: * This will cause the reader associated with this circular buffer
694: * to read its last characters once it empties the buffer.
695: * Once a stream has been closed, further write() or flush() invocations
696: * will cause an IOException to be thrown. Closing a previously-closed stream,
697: * however, has no effect.
698: *
699: * @throws IOException never.
700: *
701: * @since ostermillerutils 1.00.00
702: */
703: @Override
704: public void close() throws IOException {
705: synchronized (CircularCharBuffer.this ) {
706: if (!writerClosed) {
707: flush();
708: }
709: writerClosed = true;
710: }
711: }
712:
713: /**
714: * Flush the stream.
715: *
716: * @throws IOException if the stream is closed.
717: *
718: * @since ostermillerutils 1.00.00
719: */
720: @Override
721: public void flush() throws IOException {
722: if (writerClosed)
723: throw new IOException(
724: "Writer has been closed; cannot flush a closed Writer.");
725: if (readerClosed)
726: throw new IOException(
727: "Buffer closed by Reader; cannot flush.");
728: // this method needs to do nothing
729: }
730:
731: /**
732: * Write an array of characters.
733: * If the buffer allows blocking writes, this method will block until
734: * all the data has been written rather than throw an IOException.
735: *
736: * @param cbuf Array of characters to be written
737: * @throws BufferOverflowException if buffer does not allow blocking writes
738: * and the buffer is full. If the exception is thrown, no data
739: * will have been written since the buffer was set to be non-blocking.
740: * @throws IOException if the stream is closed, or the write is interrupted.
741: *
742: * @since ostermillerutils 1.00.00
743: */
744: @Override
745: public void write(char[] cbuf) throws IOException {
746: write(cbuf, 0, cbuf.length);
747: }
748:
749: /**
750: * Write a portion of an array of characters.
751: * If the buffer allows blocking writes, this method will block until
752: * all the data has been written rather than throw an IOException.
753: *
754: * @param cbuf Array of characters
755: * @param off Offset from which to start writing characters
756: * @param len - Number of characters to write
757: * @throws BufferOverflowException if buffer does not allow blocking writes
758: * and the buffer is full. If the exception is thrown, no data
759: * will have been written since the buffer was set to be non-blocking.
760: * @throws IOException if the stream is closed, or the write is interrupted.
761: *
762: * @since ostermillerutils 1.00.00
763: */
764: @Override
765: public void write(char[] cbuf, int off, int len)
766: throws IOException {
767: while (len > 0) {
768: synchronized (CircularCharBuffer.this ) {
769: if (writerClosed)
770: throw new IOException(
771: "Writer has been closed; cannot write to a closed Writer.");
772: if (readerClosed)
773: throw new IOException(
774: "Buffer closed by Reader; cannot write to a closed buffer.");
775: int spaceLeft = spaceLeft();
776: while (infinite && spaceLeft < len) {
777: resize();
778: spaceLeft = spaceLeft();
779: }
780: if (!blockingWrite && spaceLeft < len)
781: throw new BufferOverflowException(
782: "CircularCharBuffer is full; cannot write "
783: + len + " characters");
784: int realLen = Math.min(len, spaceLeft);
785: int firstLen = Math.min(realLen, buffer.length
786: - writePosition);
787: int secondLen = Math.min(realLen - firstLen,
788: buffer.length - markPosition - 1);
789: int written = firstLen + secondLen;
790: if (firstLen > 0) {
791: System.arraycopy(cbuf, off, buffer,
792: writePosition, firstLen);
793: }
794: if (secondLen > 0) {
795: System.arraycopy(cbuf, off + firstLen, buffer,
796: 0, secondLen);
797: writePosition = secondLen;
798: } else {
799: writePosition += written;
800: }
801: if (writePosition == buffer.length) {
802: writePosition = 0;
803: }
804: off += written;
805: len -= written;
806: }
807: if (len > 0) {
808: try {
809: Thread.sleep(100);
810: } catch (Exception x) {
811: throw new IOException(
812: "Waiting for available space in buffer interrupted.");
813: }
814: }
815: }
816: }
817:
818: /**
819: * Write a single character.
820: * The character to be written is contained in the 16 low-order bits of the
821: * given integer value; the 16 high-order bits are ignored.
822: * If the buffer allows blocking writes, this method will block until
823: * all the data has been written rather than throw an IOException.
824: *
825: * @param c number of characters to be written
826: * @throws BufferOverflowException if buffer does not allow blocking writes
827: * and the buffer is full.
828: * @throws IOException if the stream is closed, or the write is interrupted.
829: *
830: * @since ostermillerutils 1.00.00
831: */
832: @Override
833: public void write(int c) throws IOException {
834: boolean written = false;
835: while (!written) {
836: synchronized (CircularCharBuffer.this ) {
837: if (writerClosed)
838: throw new IOException(
839: "Writer has been closed; cannot write to a closed Writer.");
840: if (readerClosed)
841: throw new IOException(
842: "Buffer closed by Reader; cannot write to a closed buffer.");
843: int spaceLeft = spaceLeft();
844: while (infinite && spaceLeft < 1) {
845: resize();
846: spaceLeft = spaceLeft();
847: }
848: if (!blockingWrite && spaceLeft < 1)
849: throw new BufferOverflowException(
850: "CircularCharBuffer is full; cannot write 1 character");
851: if (spaceLeft > 0) {
852: buffer[writePosition] = (char) (c & 0xffff);
853: writePosition++;
854: if (writePosition == buffer.length) {
855: writePosition = 0;
856: }
857: written = true;
858: }
859: }
860: if (!written) {
861: try {
862: Thread.sleep(100);
863: } catch (Exception x) {
864: throw new IOException(
865: "Waiting for available space in buffer interrupted.");
866: }
867: }
868: }
869: }
870:
871: /**
872: * Write a string.
873: * If the buffer allows blocking writes, this method will block until
874: * all the data has been written rather than throw an IOException.
875: *
876: * @param str String to be written
877: * @throws BufferOverflowException if buffer does not allow blocking writes
878: * and the buffer is full. If the exception is thrown, no data
879: * will have been written since the buffer was set to be non-blocking.
880: * @throws IOException if the stream is closed, or the write is interrupted.
881: *
882: * @since ostermillerutils 1.00.00
883: */
884: @Override
885: public void write(String str) throws IOException {
886: write(str, 0, str.length());
887: }
888:
889: /**
890: * Write a portion of a string.
891: * If the buffer allows blocking writes, this method will block until
892: * all the data has been written rather than throw an IOException.
893: *
894: * @param str A String
895: * @param off Offset from which to start writing characters
896: * @param len Number of characters to write
897: * @throws BufferOverflowException if buffer does not allow blocking writes
898: * and the buffer is full. If the exception is thrown, no data
899: * will have been written since the buffer was set to be non-blocking.
900: * @throws IOException if the stream is closed, or the write is interrupted.
901: *
902: * @since ostermillerutils 1.00.00
903: */
904: @Override
905: public void write(String str, int off, int len)
906: throws IOException {
907: while (len > 0) {
908: synchronized (CircularCharBuffer.this ) {
909: if (writerClosed)
910: throw new IOException(
911: "Writer has been closed; cannot write to a closed Writer.");
912: if (readerClosed)
913: throw new IOException(
914: "Buffer closed by Reader; cannot write to a closed buffer.");
915: int spaceLeft = spaceLeft();
916: while (infinite && spaceLeft < len) {
917: resize();
918: spaceLeft = spaceLeft();
919: }
920: if (!blockingWrite && spaceLeft < len)
921: throw new BufferOverflowException(
922: "CircularCharBuffer is full; cannot write "
923: + len + " characters");
924: int realLen = Math.min(len, spaceLeft);
925: int firstLen = Math.min(realLen, buffer.length
926: - writePosition);
927: int secondLen = Math.min(realLen - firstLen,
928: buffer.length - markPosition - 1);
929: int written = firstLen + secondLen;
930: for (int i = 0; i < firstLen; i++) {
931: buffer[writePosition + i] = str.charAt(off + i);
932: }
933: if (secondLen > 0) {
934: for (int i = 0; i < secondLen; i++) {
935: buffer[i] = str.charAt(off + firstLen + i);
936: }
937: writePosition = secondLen;
938: } else {
939: writePosition += written;
940: }
941: if (writePosition == buffer.length) {
942: writePosition = 0;
943: }
944: off += written;
945: len -= written;
946: }
947: if (len > 0) {
948: try {
949: Thread.sleep(100);
950: } catch (Exception x) {
951: throw new IOException(
952: "Waiting for available space in buffer interrupted.");
953: }
954: }
955: }
956: }
957: }
958: }
|