001 /*
002 * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
003 *
004 * http://uio.imagero.com
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 *
009 * o Redistributions of source code must retain the above copyright notice,
010 * this list of conditions and the following disclaimer.
011 *
012 * o Redistributions in binary form must reproduce the above copyright notice,
013 * this list of conditions and the following disclaimer in the documentation
014 * and/or other materials provided with the distribution.
015 *
016 * o Neither the name of Andrey Kuznetsov nor the names of
017 * its contributors may be used to endorse or promote products derived
018 * from this software without specific prior written permission.
019 *
020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
022 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
023 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
027 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
028 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
029 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
030 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031 */
032 package com.imagero.uio.io;
033
034 import java.awt.event.ActionEvent;
035 import java.awt.event.ActionListener;
036 import java.io.ByteArrayOutputStream;
037 import java.io.DataOutput;
038 import java.io.IOException;
039
040 /**
041 * ByteArrayOutputStreamExt extends ByteArrayOutputStream with posibility to drain off data into user specified buffer.
042 * ActionEvent is fired if buffer is full and going to grow.
043 * @author Andrey Kuznetsov
044 */
045 public class ByteArrayOutputStreamExt extends ByteArrayOutputStream {
046
047 public static final String BUFFER_FULL = "buffer full";
048
049 ActionListener bufferListener;
050
051 public ByteArrayOutputStreamExt() {
052 this(1024);
053 }
054
055 public ByteArrayOutputStreamExt(int size) {
056 super(size);
057 }
058
059 public ByteArrayOutputStreamExt(ActionListener l) {
060 this.bufferListener = l;
061 }
062
063 public ByteArrayOutputStreamExt(int size, ActionListener l) {
064 super(size);
065 this.bufferListener = l;
066 }
067
068 protected void fireBufferFullEvent() {
069 drained = false;
070 if (bufferListener != null) {
071 ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, BUFFER_FULL);
072 bufferListener.actionPerformed(e);
073 }
074 }
075
076 /**
077 * Fill destination buffer with data which is removed from start of this buffer.
078 * @param dest destination buffer
079 * @return how much bytes was moved from this buffer into destination buffer.
080 */
081 public synchronized int drain(byte[] dest) {
082 int length = Math.min(dest.length, count);
083 if (length > 0) {
084 System.arraycopy(buf, 0, dest, 0, length);
085 int len = count - length;
086 if (len > 0) {
087 System.arraycopy(buf, length, buf, 0, len);
088 }
089 count -= length;
090 }
091 drained = true;
092 return length;
093 }
094
095 boolean drained;
096
097 public synchronized void write(int b) {
098 if (bufferListener != null) {
099 int newcount = count + 1;
100 if (newcount > buf.length) {
101 fireBufferFullEvent();
102 }
103 }
104 super.write(b);
105 }
106
107 /**
108 * Writes len bytes from given byte array starting at offset off to this buffer.
109 * If capacity of buffer is not enough for incoming data,
110 * then, at-first, buffer filled with data,
111 * then fired "buffer is full" event,
112 * thus giving the user possibility to "drain" buffer.
113 * If buffer was drained, then rest of data is written to buffer,
114 * otherwise buffer capacity is increased before writing.
115 * @param b
116 * @param off
117 * @param len
118 */
119 public synchronized void write(byte b[], int off, int len) {
120 int max = buf.length - count;
121 if (max > len || bufferListener == null) {
122 super.write(b, off, len);
123 } else {
124 super.write(b, off, max);
125 fireBufferFullEvent();
126 write2(b, off + max, len - max);
127 }
128 }
129
130 private void write2(byte b[], int off, int len) {
131 if (!drained) {
132 super.write(b, off, len);
133 } else {
134 drained = false;
135 int max = buf.length - count;
136 if (max > len) {
137 super.write(b, off, len);
138 } else {
139 super.write(b, off, max);
140 fireBufferFullEvent();
141 write2(b, off + max, len - max);
142 }
143 }
144 }
145
146 public void close() throws IOException {
147 fireBufferFullEvent();
148 super.close();
149 }
150
151 public void setBufferListener(ActionListener bufferListener) {
152 this.bufferListener = bufferListener;
153 }
154
155 /**
156 * Retrieve internal buffer.
157 * Recommended use - immediately after receiving "buffer full" event.
158 * Internal buffer is returned and replaced with new empty buffer.
159 */
160 public synchronized byte[] drain() {
161 byte[] tmp = buf;
162 buf = new byte[0];
163 count = 0;
164 drained = true;
165 return tmp;
166 }
167
168 public synchronized void writeTo(DataOutput out) throws IOException {
169 out.write(buf, 0, count);
170 }
171
172 public byte [] closeStream() {
173 byte [] tmp = buf;
174 buf = null;
175 return tmp;
176 }
177 }