1 | /* |
2 | * jDTAUS Core Utilities |
3 | * Copyright (C) 2005 Christian Schulte |
4 | * <cs@schulte.it> |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2.1 of the License, or any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with this library; if not, write to the Free Software |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
19 | * |
20 | */ |
21 | package org.jdtaus.core.io.util; |
22 | |
23 | import java.io.IOException; |
24 | import java.io.InputStream; |
25 | import java.io.OutputStream; |
26 | import java.io.Serializable; |
27 | import java.util.Arrays; |
28 | import java.util.Locale; |
29 | import org.jdtaus.core.container.ContainerFactory; |
30 | import org.jdtaus.core.io.FileOperations; |
31 | import org.jdtaus.core.lang.spi.MemoryManager; |
32 | import org.jdtaus.core.logging.spi.Logger; |
33 | |
34 | /** |
35 | * Implementation of elementary I/O operations in heap memory. |
36 | * <p>This implementation performs I/O in memory. The value of property |
37 | * {@code length} is limited to a maximum of {@code Integer.MAX_VALUE} (4 GB). |
38 | * </p> |
39 | * |
40 | * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> |
41 | * @version $JDTAUS: MemoryFileOperations.java 8853 2014-01-10 13:50:00Z schulte $ |
42 | */ |
43 | public final class MemoryFileOperations |
44 | implements FileOperations, Serializable, Cloneable |
45 | { |
46 | //--Fields------------------------------------------------------------------ |
47 | |
48 | /** |
49 | * Data to operate on. |
50 | * @serial |
51 | */ |
52 | private byte[] data; |
53 | |
54 | /** |
55 | * FilePointer. |
56 | * @serial |
57 | */ |
58 | private long filePointer; |
59 | |
60 | /** |
61 | * Actual length. |
62 | * @serial |
63 | */ |
64 | private int length; |
65 | |
66 | /** |
67 | * Default temporary buffer. |
68 | * @serial |
69 | */ |
70 | private byte[] defaultBuffer; |
71 | |
72 | //------------------------------------------------------------------Fields-- |
73 | //--Properties-------------------------------------------------------------- |
74 | |
75 | // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties |
76 | // This section is managed by jdtaus-container-mojo. |
77 | |
78 | /** |
79 | * Gets the value of property <code>streamBufferSize</code>. |
80 | * |
81 | * @return Size of the buffer for buffering streams. |
82 | */ |
83 | private int getStreamBufferSize() |
84 | { |
85 | return ( (java.lang.Integer) ContainerFactory.getContainer(). |
86 | getProperty( this, "streamBufferSize" ) ).intValue(); |
87 | |
88 | } |
89 | |
90 | // </editor-fold>//GEN-END:jdtausProperties |
91 | |
92 | //--------------------------------------------------------------Properties-- |
93 | //--Dependencies------------------------------------------------------------ |
94 | |
95 | // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies |
96 | // This section is managed by jdtaus-container-mojo. |
97 | |
98 | /** |
99 | * Gets the configured <code>Logger</code> implementation. |
100 | * |
101 | * @return The configured <code>Logger</code> implementation. |
102 | */ |
103 | private Logger getLogger() |
104 | { |
105 | return (Logger) ContainerFactory.getContainer(). |
106 | getDependency( this, "Logger" ); |
107 | |
108 | } |
109 | |
110 | /** |
111 | * Gets the configured <code>MemoryManager</code> implementation. |
112 | * |
113 | * @return The configured <code>MemoryManager</code> implementation. |
114 | */ |
115 | private MemoryManager getMemoryManager() |
116 | { |
117 | return (MemoryManager) ContainerFactory.getContainer(). |
118 | getDependency( this, "MemoryManager" ); |
119 | |
120 | } |
121 | |
122 | /** |
123 | * Gets the configured <code>Locale</code> implementation. |
124 | * |
125 | * @return The configured <code>Locale</code> implementation. |
126 | */ |
127 | private Locale getLocale() |
128 | { |
129 | return (Locale) ContainerFactory.getContainer(). |
130 | getDependency( this, "Locale" ); |
131 | |
132 | } |
133 | |
134 | // </editor-fold>//GEN-END:jdtausDependencies |
135 | |
136 | //------------------------------------------------------------Dependencies-- |
137 | //--FileOperations---------------------------------------------------------- |
138 | |
139 | public long getLength() |
140 | { |
141 | return this.length; |
142 | } |
143 | |
144 | /** |
145 | * {@inheritDoc} |
146 | * |
147 | * @throws IllegalArgumentException if {@code newLength} is negative or |
148 | * greater than {@code Integer.MAX_VALUE}. |
149 | */ |
150 | public void setLength( final long newLength ) |
151 | { |
152 | if ( newLength < 0L || newLength > Integer.MAX_VALUE ) |
153 | { |
154 | throw new IllegalArgumentException( Long.toString( newLength ) ); |
155 | } |
156 | |
157 | this.ensureCapacity( (int) newLength ); |
158 | this.length = (int) newLength; |
159 | if ( this.filePointer > this.length ) |
160 | { |
161 | this.filePointer = this.length; |
162 | } |
163 | } |
164 | |
165 | public long getFilePointer() |
166 | { |
167 | return this.filePointer; |
168 | } |
169 | |
170 | public void setFilePointer( final long pos ) |
171 | { |
172 | // Preconditions. |
173 | if ( pos < 0L || pos > Integer.MAX_VALUE ) |
174 | { |
175 | throw new IllegalArgumentException( Long.toString( pos ) ); |
176 | } |
177 | |
178 | this.filePointer = pos; |
179 | } |
180 | |
181 | public int read( final byte[] buf, final int off, final int len ) |
182 | { |
183 | final int ret; |
184 | |
185 | // Preconditions. |
186 | if ( buf == null ) |
187 | { |
188 | throw new NullPointerException( "buf" ); |
189 | } |
190 | if ( off < 0 ) |
191 | { |
192 | throw new ArrayIndexOutOfBoundsException( off ); |
193 | } |
194 | if ( len < 0 ) |
195 | { |
196 | throw new ArrayIndexOutOfBoundsException( len ); |
197 | } |
198 | if ( off + len > buf.length ) |
199 | { |
200 | throw new ArrayIndexOutOfBoundsException( off + len ); |
201 | } |
202 | if ( this.filePointer + len > Integer.MAX_VALUE ) |
203 | { |
204 | throw new ArrayIndexOutOfBoundsException( Integer.MAX_VALUE ); |
205 | } |
206 | |
207 | if ( len == 0 ) |
208 | { |
209 | ret = 0; |
210 | } |
211 | else if ( this.filePointer >= this.length ) |
212 | { |
213 | // EOF |
214 | ret = FileOperations.EOF; |
215 | } |
216 | else if ( this.filePointer + len > this.length ) |
217 | { |
218 | // less than len byte before EOF |
219 | final int remaining = (int) ( this.length - this.filePointer ); |
220 | System.arraycopy( this.data, (int) this.filePointer, buf, off, |
221 | remaining ); |
222 | |
223 | this.filePointer += remaining; |
224 | ret = remaining; |
225 | } |
226 | else |
227 | { |
228 | // copy len byte into buf. |
229 | System.arraycopy( |
230 | this.data, (int) this.filePointer, buf, off, len ); |
231 | |
232 | this.filePointer += len; |
233 | ret = len; |
234 | } |
235 | |
236 | return ret; |
237 | } |
238 | |
239 | public void write( final byte[] buf, final int off, final int len ) |
240 | { |
241 | // Preconditions. |
242 | if ( buf == null ) |
243 | { |
244 | throw new NullPointerException( "buf" ); |
245 | } |
246 | if ( off < 0 ) |
247 | { |
248 | throw new ArrayIndexOutOfBoundsException( off ); |
249 | } |
250 | if ( len < 0 ) |
251 | { |
252 | throw new ArrayIndexOutOfBoundsException( len ); |
253 | } |
254 | if ( off + len > buf.length ) |
255 | { |
256 | throw new ArrayIndexOutOfBoundsException( off + len ); |
257 | } |
258 | |
259 | final long newLen = this.filePointer + len; |
260 | if ( newLen > Integer.MAX_VALUE ) |
261 | { |
262 | throw new ArrayIndexOutOfBoundsException( Integer.MAX_VALUE ); |
263 | } |
264 | |
265 | if ( newLen > this.length ) |
266 | { |
267 | this.setLength( newLen ); |
268 | } |
269 | |
270 | System.arraycopy( buf, off, this.data, (int) this.filePointer, len ); |
271 | this.filePointer += len; |
272 | } |
273 | |
274 | public void read( final OutputStream out ) throws IOException |
275 | { |
276 | if ( out == null ) |
277 | { |
278 | throw new NullPointerException( "out" ); |
279 | } |
280 | |
281 | out.write( this.data, 0, this.length ); |
282 | this.filePointer = this.length; |
283 | } |
284 | |
285 | public void write( final InputStream in ) throws IOException |
286 | { |
287 | if ( in == null ) |
288 | { |
289 | throw new NullPointerException( "in" ); |
290 | } |
291 | |
292 | int read; |
293 | final byte[] buf = this.getStreamBuffer(); |
294 | |
295 | while ( ( read = in.read( buf, 0, buf.length ) ) != FileOperations.EOF ) |
296 | { |
297 | this.write( buf, 0, read ); |
298 | } |
299 | } |
300 | |
301 | /** |
302 | * {@inheritDoc} |
303 | * <p>Since this implementation does not use any system resources to |
304 | * release, this method does nothing. In contrast to other |
305 | * {@code FileOperations} implementations the instance can still be used |
306 | * after calling this method. It would be a mistake to write an application |
307 | * which relies on this behaviour, however.</p> |
308 | */ |
309 | public void close() |
310 | { |
311 | } |
312 | |
313 | //----------------------------------------------------------FileOperations-- |
314 | //--MemoryFileOperations---------------------------------------------------- |
315 | |
316 | /** Creates a new {@code MemoryFileOperations} instance of no length. */ |
317 | public MemoryFileOperations() |
318 | { |
319 | this.filePointer = 0L; |
320 | this.data = new byte[ 0 ]; |
321 | this.length = 0; |
322 | } |
323 | |
324 | /** |
325 | * Creates a new {@code MemoryFileOperations} instance of no length |
326 | * taking an initial capacity. |
327 | * |
328 | * @param initialCapacity the number of bytes to pre-allocate when |
329 | * creating the new instance. |
330 | * |
331 | * @throws OutOfMemoryError if not enough memory is available to create a |
332 | * buffer with a length of {@code initialCapacity}. |
333 | * |
334 | * @see #ensureCapacity(int) |
335 | */ |
336 | public MemoryFileOperations( final int initialCapacity ) |
337 | { |
338 | this.filePointer = 0L; |
339 | this.length = 0; |
340 | this.data = this.getMemoryManager().allocateBytes( initialCapacity ); |
341 | } |
342 | |
343 | /** |
344 | * Creates a new {@code MemoryFileOperations} instance holding {@code buf}. |
345 | * |
346 | * @param buf bytes to initialize the instance with. |
347 | * |
348 | * @throws NullPointerException if {@code buf} is {@code null}. |
349 | */ |
350 | public MemoryFileOperations( final byte[] buf ) |
351 | { |
352 | this(); |
353 | |
354 | if ( buf == null ) |
355 | { |
356 | throw new NullPointerException( "buf" ); |
357 | } |
358 | |
359 | this.data = (byte[]) buf.clone(); |
360 | this.length = buf.length; |
361 | this.filePointer = 0L; |
362 | } |
363 | |
364 | /** |
365 | * Gets the capacity of the instance. |
366 | * |
367 | * @return the capacity of the instance in byte. |
368 | */ |
369 | public int getCapacity() |
370 | { |
371 | return this.data.length; |
372 | } |
373 | |
374 | /** |
375 | * Increases the capacity of the instance, if necessary, to ensure that it |
376 | * can hold at least the number of bytes specified by the minimum capacity |
377 | * argument. |
378 | * |
379 | * @param minimumCapacity the minimum capacity to ensure. |
380 | */ |
381 | public void ensureCapacity( final int minimumCapacity ) |
382 | { |
383 | final int oldLength = this.data.length; |
384 | |
385 | while ( this.data.length < minimumCapacity ) |
386 | { |
387 | final byte[] newData = this.getMemoryManager().allocateBytes( |
388 | this.data.length * 2 >= minimumCapacity |
389 | ? this.data.length * 2 |
390 | : minimumCapacity ); |
391 | |
392 | Arrays.fill( newData, (byte) 0 ); |
393 | System.arraycopy( this.data, 0, newData, 0, this.data.length ); |
394 | this.data = newData; |
395 | } |
396 | |
397 | if ( oldLength != this.data.length && |
398 | this.getLogger().isDebugEnabled() ) |
399 | { |
400 | this.getLogger().debug( |
401 | this.getLogResizeMessage( this.getLocale(), |
402 | new Long( this.data.length ) ) ); |
403 | |
404 | } |
405 | } |
406 | |
407 | /** |
408 | * Gets the file contents. |
409 | * |
410 | * @return the file contents of the instance. |
411 | * |
412 | * @throws OutOfMemoryError if not enough memory is available. |
413 | */ |
414 | public byte[] getData() |
415 | { |
416 | final int len = (int) this.getLength(); |
417 | final byte[] ret = this.getMemoryManager().allocateBytes( len ); |
418 | |
419 | System.arraycopy( this.data, 0, ret, 0, len ); |
420 | |
421 | return ret; |
422 | } |
423 | |
424 | /** |
425 | * Gets a buffer for buffering streams. |
426 | * |
427 | * @return a buffer for buffering streams. |
428 | */ |
429 | private byte[] getStreamBuffer() |
430 | { |
431 | if ( this.defaultBuffer == null ) |
432 | { |
433 | this.defaultBuffer = this.getMemoryManager(). |
434 | allocateBytes( this.getStreamBufferSize() < 0 |
435 | ? 0 |
436 | : this.getStreamBufferSize() ); |
437 | |
438 | } |
439 | |
440 | return this.defaultBuffer; |
441 | } |
442 | |
443 | //----------------------------------------------------MemoryFileOperations-- |
444 | //--Object------------------------------------------------------------------ |
445 | |
446 | /** |
447 | * Indicates whether some other object is equal to this one by comparing |
448 | * the contents from memory. |
449 | * |
450 | * @param o the reference object with which to compare. |
451 | * |
452 | * @return {@code true} if this object is the same as {@code o}; |
453 | * {@code false} otherwise. |
454 | */ |
455 | public boolean equals( final Object o ) |
456 | { |
457 | boolean equal = this == o; |
458 | |
459 | if ( !equal && o instanceof MemoryFileOperations ) |
460 | { |
461 | final MemoryFileOperations that = (MemoryFileOperations) o; |
462 | equal = Arrays.equals( getData(), that.getData() ); |
463 | } |
464 | |
465 | return equal; |
466 | } |
467 | |
468 | /** |
469 | * Returns a hash code value for this object. |
470 | * |
471 | * @return a hash code value for this object. |
472 | */ |
473 | public int hashCode() |
474 | { |
475 | // JDK: As of JDK 1.5, Arrays.hashCode( getData() ). |
476 | final byte[] bytes = getData(); |
477 | |
478 | int result = 1; |
479 | for ( int i = 0, s0 = bytes.length; i < s0; i++ ) |
480 | { |
481 | result = 31 * result + bytes[i]; |
482 | } |
483 | |
484 | return result; |
485 | } |
486 | |
487 | /** |
488 | * Creates and returns a deep copy of this object. |
489 | * |
490 | * @return a clone of this instance. |
491 | */ |
492 | public Object clone() |
493 | { |
494 | try |
495 | { |
496 | return super.clone(); |
497 | } |
498 | catch ( final CloneNotSupportedException e ) |
499 | { |
500 | throw new AssertionError( e ); |
501 | } |
502 | } |
503 | |
504 | //------------------------------------------------------------------Object-- |
505 | //--Messages---------------------------------------------------------------- |
506 | |
507 | // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages |
508 | // This section is managed by jdtaus-container-mojo. |
509 | |
510 | /** |
511 | * Gets the text of message <code>logResize</code>. |
512 | * <blockquote><pre>aktuelle Puffergröße: {0, number} Byte</pre></blockquote> |
513 | * <blockquote><pre>current buffer size: {0, number} byte</pre></blockquote> |
514 | * |
515 | * @param locale The locale of the message instance to return. |
516 | * @param bufferSize The current size of the internal buffer. |
517 | * |
518 | * @return Information about the size of the internal buffer. |
519 | */ |
520 | private String getLogResizeMessage( final Locale locale, |
521 | final java.lang.Number bufferSize ) |
522 | { |
523 | return ContainerFactory.getContainer(). |
524 | getMessage( this, "logResize", locale, |
525 | new Object[] |
526 | { |
527 | bufferSize |
528 | }); |
529 | |
530 | } |
531 | |
532 | // </editor-fold>//GEN-END:jdtausMessages |
533 | |
534 | //----------------------------------------------------------------Messages-- |
535 | } |