View Javadoc

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 8644 2012-09-27 06:46:11Z 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 = buf;
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 ( 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 }