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