1 /** 2 * The utillib library. 3 * More information is available at http://www.jinchess.com/. 4 * Copyright (C) 2002 Alexander Maryanovsky. 5 * All rights reserved. 6 * 7 * The utillib library is free software; you can redistribute 8 * it and/or modify it under the terms of the GNU Lesser General Public License 9 * as published by the Free Software Foundation; either version 2 of the 10 * License, or (at your option) any later version. 11 * 12 * The utillib library is distributed in the hope that it will 13 * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public License 18 * along with utillib library; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA 20 */ 21 22 package free.util; 23 24 import java.io.*; 25 26 27 /** 28 * An implementation of a virtual file, whose contents are kept in memory. 29 */ 30 31 public class MemoryFile{ 32 33 34 /** 35 * The default buffer size. 36 */ 37 38 private static final int DEFAULT_BUFFER_SIZE = 2048; 39 40 41 42 /** 43 * The initial buffer size. 44 */ 45 46 private final int initBufSize; 47 48 49 50 /** 51 * The data byte array. 52 */ 53 54 private byte [] data = null; 55 56 57 58 /** 59 * The amount of bytes that have been written into the file. 60 */ 61 62 private volatile int size = 0; 63 64 65 66 /** 67 * The currently open <code>OutputStream</code> writing into this 68 * <code>MemoryFile</code>. <code>null</code> if none. 69 */ 70 71 private OutputStream out = null; 72 73 74 75 76 /** 77 * Creates a new, empty, <code>MemoryFile</code>. 78 */ 79 MemoryFile()80 public MemoryFile(){ 81 this(null, DEFAULT_BUFFER_SIZE); 82 } 83 84 85 86 87 /** 88 * Creates a new empty <code>MemoryFile</code> with the specified initial 89 * buffer size. 90 */ 91 MemoryFile(int bufSize)92 public MemoryFile(int bufSize){ 93 this(null, bufSize); 94 } 95 96 97 98 99 /** 100 * Creates a new <code>MemoryFile</code> initialized with the data from the 101 * specified byte array. The specified byte array is copied. 102 */ 103 MemoryFile(byte [] data)104 public MemoryFile(byte [] data){ 105 this(data, DEFAULT_BUFFER_SIZE); 106 } 107 108 109 110 111 /** 112 * Creates a new <code>MemoryFile</code> initialized with the data from the 113 * specified byte array and the initial buffer size. Note that if the 114 * specified initial buffer size is smaller than the length of the byte array, 115 * it will only be user when/if the contents of the file are cleared. 116 */ 117 MemoryFile(byte [] data, int bufSize)118 public MemoryFile(byte [] data, int bufSize){ 119 if (bufSize <= 0) 120 throw new IllegalArgumentException("The buffer size must be a positive integer"); 121 122 this.initBufSize = bufSize; 123 124 if (data != null){ 125 this.data = new byte[bufSize > data.length ? bufSize : data.length]; 126 System.arraycopy(data, 0, this.data, 0, data.length); 127 this.size = data.length; 128 } 129 } 130 131 132 133 134 /** 135 * Returns the size of the file. 136 */ 137 getSize()138 public int getSize(){ 139 return size; 140 } 141 142 143 144 /** 145 * Returns an <code>OutputStream</code> that will write into this 146 * <code>MemoryFile</code>. Only a single open <code>OutputStream</code> is 147 * allowed per <code>MemoryFile</code>. Note that invoking this method clears 148 * the current contents of the file. 149 * 150 * @throws IOException if an open OutputStream writing into this 151 * <code>MemoryFile</code> already exists. 152 */ 153 getOutputStream()154 public synchronized OutputStream getOutputStream() throws IOException{ 155 if (out != null) 156 throw new IOException("MemoryFile already open for writing"); 157 158 size = 0; 159 data = new byte[initBufSize]; 160 return out = new InternalOutputStream(); 161 } 162 163 164 165 166 /** 167 * Returns an <code>InputStream</code> that will read from this 168 * <code>MemoryFile</code>. Note that the data read from the returned 169 * <code>InputStream</code> will be the data in the file when this method is 170 * invoked. If more data is written into the file or the contents of the file 171 * are cleared, the returned <code>InputStream</code> will not be affected. 172 */ 173 getInputStream()174 public InputStream getInputStream(){ 175 return new ByteArrayInputStream(data, 0, getSize()); 176 } 177 178 179 180 181 /** 182 * Clears this file, resetting its size to 0. 183 * 184 * @throws IOException if the file is currently open for writing. 185 */ 186 clear()187 public synchronized void clear() throws IOException{ 188 if (out != null) 189 throw new IOException("MemoryFile open for writing"); 190 size = 0; 191 } 192 193 194 195 196 /** 197 * Writes the contents of this <code>MemoryFile</code> into the specified 198 * <code>OutputStream</code>. 199 */ 200 writeTo(OutputStream out)201 public synchronized void writeTo(OutputStream out) throws IOException{ 202 out.write(data, 0, getSize()); 203 } 204 205 206 207 208 /** 209 * Increases the size of the internal buffer by at least the specified amount 210 * of bytes. The caller must take care of proper synchronization. 211 */ 212 growBuf(int minGrowSize)213 private void growBuf(int minGrowSize){ 214 int growSize = minGrowSize < data.length ? data.length : minGrowSize; 215 byte [] newData = new byte[data.length + growSize]; 216 System.arraycopy(data, 0, newData, 0, data.length); 217 data = newData; 218 } 219 220 221 222 /** 223 * Writes a single byte into the file. 224 */ 225 226 private synchronized void write(int b){ 227 if (data.length - size == 0) 228 growBuf(1); 229 230 data[size++] = (byte)(b&0xff); 231 } 232 233 234 235 /** 236 * Writes the specified amount of bytes from the specified array starting with 237 * the specified index into the file. 238 */ 239 240 private synchronized void write(byte [] arr, int offset, int length){ 241 if (data.length - size < arr.length) 242 growBuf(arr.length + size - data.length); 243 244 System.arraycopy(arr, offset, data, size, length); 245 size += length; 246 } 247 248 249 250 /** 251 * Closes the <code>OutputStream</code> writing into this file. 252 * 253 * @throws IOException if the <code>OutputStream</code> is already closed. 254 */ 255 256 private synchronized void closeOutputStream() throws IOException{ 257 if (out == null) 258 throw new IOException("OutputStream already closed"); 259 260 out = null; 261 } 262 263 264 265 /** 266 * The <code>OutputStream</code> class that is responsible for writing into 267 * this <code>MemoryFile</code>. This class simply forwards the calls to the 268 * methods in its outer class. 269 */ 270 271 private class InternalOutputStream extends OutputStream{ 272 273 public void write(int b){ 274 MemoryFile.this.write(b); 275 } 276 277 public void write(byte [] buf, int offset, int length){ 278 MemoryFile.this.write(buf, offset, length); 279 } 280 281 public void close() throws IOException{ 282 MemoryFile.this.closeOutputStream(); 283 } 284 285 } 286 287 288 } 289