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.math.BigDecimal; |
25 | import java.util.Locale; |
26 | import javax.swing.event.EventListenerList; |
27 | import org.jdtaus.core.container.ContainerFactory; |
28 | import org.jdtaus.core.io.FileOperations; |
29 | import org.jdtaus.core.io.StructuredFile; |
30 | import org.jdtaus.core.io.StructuredFileListener; |
31 | import org.jdtaus.core.lang.spi.MemoryManager; |
32 | import org.jdtaus.core.messages.DeletesBlocksMessage; |
33 | import org.jdtaus.core.messages.InsertsBlocksMessage; |
34 | import org.jdtaus.core.monitor.spi.Task; |
35 | import org.jdtaus.core.monitor.spi.TaskMonitor; |
36 | |
37 | /** |
38 | * {@code StructuredFile} implementation based on {@code FileOperations}. |
39 | * <p>Pre {@code FlushableFileOperations} and its implementations this |
40 | * implementation performed read-ahead caching. This behaviour got changed |
41 | * in favour of {@code ReadAheadFileOperations} and |
42 | * {@code CoalescingFileOperations} which are generalized replacements for any |
43 | * cacheing formerly performed by this implementation. Since this class does |
44 | * not implement any cacheing anymore, the {@link #flush()} method will write |
45 | * out pending changes of an underlying {@code FlushableFileOperations} |
46 | * implementation, if any, by calling the corresponding {@code flush()} method |
47 | * of that {@code FlushableFileOperations} instance.</p> |
48 | * <p>This implementation uses task monitoring for the {@code deleteBlocks()} |
49 | * and {@code insertBlocks()} methods. Task monitoring is controlled by |
50 | * property {@code monitoringThreshold} holding the number of bytes which |
51 | * need to minimally be copied to enable any task monitoring during the |
52 | * copy operation (defaults to 5242880 - 5MB).</p> |
53 | * |
54 | * <p><b>Note:</b><br> |
55 | * This implementation is not thread-safe and concurrent changes to the |
56 | * underlying {@code FileOperations} implementation are not supported.</p> |
57 | * |
58 | * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> |
59 | * @version $JDTAUS: StructuredFileOperations.java 8641 2012-09-27 06:45:17Z schulte $ |
60 | * |
61 | * @see CoalescingFileOperations |
62 | * @see ReadAheadFileOperations |
63 | */ |
64 | public final class StructuredFileOperations implements StructuredFile |
65 | { |
66 | //--Fields------------------------------------------------------------------ |
67 | |
68 | /** Pre-allocated temporary buffer. */ |
69 | private byte[] defaultBuffer; |
70 | |
71 | /** Caches the value of property blockCount. */ |
72 | private long cachedBlockCount = NO_CACHED_BLOCKCOUNT; |
73 | |
74 | private static final long NO_CACHED_BLOCKCOUNT = Long.MIN_VALUE; |
75 | |
76 | /** List for {@code StructuredFileListener}s. */ |
77 | private final EventListenerList fileListeners = new EventListenerList(); |
78 | |
79 | /** Value of property {@code blockSize} as a {@code BigDecimal}. */ |
80 | private final BigDecimal decimalBlockSize; |
81 | |
82 | //------------------------------------------------------------------Fields-- |
83 | //--Dependencies------------------------------------------------------------ |
84 | |
85 | // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies |
86 | // This section is managed by jdtaus-container-mojo. |
87 | |
88 | /** |
89 | * Gets the configured <code>MemoryManager</code> implementation. |
90 | * |
91 | * @return The configured <code>MemoryManager</code> implementation. |
92 | */ |
93 | private MemoryManager getMemoryManager() |
94 | { |
95 | return (MemoryManager) ContainerFactory.getContainer(). |
96 | getDependency( this, "MemoryManager" ); |
97 | |
98 | } |
99 | |
100 | /** |
101 | * Gets the configured <code>Locale</code> implementation. |
102 | * |
103 | * @return The configured <code>Locale</code> implementation. |
104 | */ |
105 | private Locale getLocale() |
106 | { |
107 | return (Locale) ContainerFactory.getContainer(). |
108 | getDependency( this, "Locale" ); |
109 | |
110 | } |
111 | |
112 | /** |
113 | * Gets the configured <code>TaskMonitor</code> implementation. |
114 | * |
115 | * @return The configured <code>TaskMonitor</code> implementation. |
116 | */ |
117 | private TaskMonitor getTaskMonitor() |
118 | { |
119 | return (TaskMonitor) ContainerFactory.getContainer(). |
120 | getDependency( this, "TaskMonitor" ); |
121 | |
122 | } |
123 | |
124 | // </editor-fold>//GEN-END:jdtausDependencies |
125 | |
126 | //------------------------------------------------------------Dependencies-- |
127 | //--Properties-------------------------------------------------------------- |
128 | |
129 | // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties |
130 | // This section is managed by jdtaus-container-mojo. |
131 | |
132 | /** |
133 | * Gets the value of property <code>defaultMonitoringThreshold</code>. |
134 | * |
135 | * @return Number of bytes which need to minimally be copied to enable any task monitoring during copy operations. |
136 | */ |
137 | private java.lang.Integer getDefaultMonitoringThreshold() |
138 | { |
139 | return (java.lang.Integer) ContainerFactory.getContainer(). |
140 | getProperty( this, "defaultMonitoringThreshold" ); |
141 | |
142 | } |
143 | |
144 | /** |
145 | * Gets the value of property <code>defaultBufferSize</code>. |
146 | * |
147 | * @return Size of the pre-alocated default buffer in byte. |
148 | */ |
149 | private int getDefaultBufferSize() |
150 | { |
151 | return ( (java.lang.Integer) ContainerFactory.getContainer(). |
152 | getProperty( this, "defaultBufferSize" ) ).intValue(); |
153 | |
154 | } |
155 | |
156 | // </editor-fold>//GEN-END:jdtausProperties |
157 | |
158 | //--------------------------------------------------------------Properties-- |
159 | //--StructuredFile---------------------------------------------------------- |
160 | |
161 | public int getBlockSize() |
162 | { |
163 | return this.blockSize; |
164 | } |
165 | |
166 | public long getBlockCount() throws IOException |
167 | { |
168 | this.assertNotClosed(); |
169 | |
170 | if ( this.cachedBlockCount == NO_CACHED_BLOCKCOUNT ) |
171 | { |
172 | this.cachedBlockCount = BigDecimal.valueOf( |
173 | this.getFileOperations().getLength() ).divide( |
174 | this.decimalBlockSize, BigDecimal.ROUND_UNNECESSARY ). |
175 | longValue(); |
176 | |
177 | // TODO JDK 1.5 longValueExact() |
178 | } |
179 | |
180 | return this.cachedBlockCount; |
181 | } |
182 | |
183 | public void deleteBlocks( final long index, |
184 | final long count ) throws IOException |
185 | { |
186 | final long blockCount = this.getBlockCount(); |
187 | |
188 | // Preconditions. |
189 | if ( index < 0L || index > blockCount - count ) |
190 | { |
191 | throw new ArrayIndexOutOfBoundsException( (int) index ); |
192 | } |
193 | if ( count <= 0 || count > blockCount - index ) |
194 | { |
195 | throw new ArrayIndexOutOfBoundsException( (int) count ); |
196 | } |
197 | |
198 | this.assertNotClosed(); |
199 | |
200 | this.deleteBlocksImpl( index, count, blockCount ); |
201 | } |
202 | |
203 | private void deleteBlocksImpl( final long index, final long count, |
204 | final long blockCount ) throws IOException |
205 | { |
206 | final long block = index + count; |
207 | final Task task = new Task(); |
208 | long toMoveByte = ( blockCount - block ) * this.getBlockSize(); |
209 | long readPos = block * this.getBlockSize(); |
210 | long writePos = index * this.getBlockSize(); |
211 | long progress = 0L; |
212 | long progressDivisor = 1L; |
213 | long maxProgress = toMoveByte; |
214 | |
215 | // Clear the cached block count. |
216 | this.cachedBlockCount = NO_CACHED_BLOCKCOUNT; |
217 | |
218 | // No blocks are following the ones to remove. |
219 | if ( toMoveByte == 0L ) |
220 | { |
221 | this.getFileOperations().setLength( |
222 | this.getFileOperations().getLength() - count * |
223 | this.getBlockSize() ); |
224 | |
225 | this.fireBlocksDeleted( index, count ); |
226 | return; |
227 | } |
228 | |
229 | final byte[] buf = this.getBuffer( toMoveByte > Integer.MAX_VALUE |
230 | ? Integer.MAX_VALUE |
231 | : (int) toMoveByte ); |
232 | |
233 | while ( maxProgress > Integer.MAX_VALUE ) |
234 | { |
235 | maxProgress /= 2L; |
236 | progressDivisor *= 2L; |
237 | } |
238 | |
239 | task.setIndeterminate( false ); |
240 | task.setCancelable( false ); |
241 | task.setMinimum( 0 ); |
242 | task.setMaximum( (int) maxProgress ); |
243 | task.setProgress( (int) progress ); |
244 | task.setDescription( new DeletesBlocksMessage() ); |
245 | |
246 | final boolean monitoring = toMoveByte > this.getMonitoringThreshold(); |
247 | if ( monitoring ) |
248 | { |
249 | this.getTaskMonitor().monitor( task ); |
250 | } |
251 | |
252 | try |
253 | { |
254 | // Move following blocks to the position of the first block to |
255 | // remove. |
256 | while ( toMoveByte > 0L ) |
257 | { |
258 | this.getFileOperations().setFilePointer( readPos ); |
259 | final int len = toMoveByte <= buf.length |
260 | ? (int) toMoveByte |
261 | : buf.length; |
262 | |
263 | int read = 0; |
264 | int total = 0; |
265 | do |
266 | { |
267 | read = this.getFileOperations(). |
268 | read( buf, total, len - total ); |
269 | |
270 | assert read != FileOperations.EOF : |
271 | "Unexpected end of file."; |
272 | |
273 | total += read; |
274 | |
275 | } |
276 | while ( total < len ); |
277 | |
278 | // Move the block count blocks to the beginning. |
279 | this.getFileOperations().setFilePointer( writePos ); |
280 | this.getFileOperations().write( buf, 0, len ); |
281 | |
282 | readPos += len; |
283 | writePos += len; |
284 | toMoveByte -= len; |
285 | progress += len; |
286 | |
287 | task.setProgress( (int) ( progress / progressDivisor ) ); |
288 | } |
289 | |
290 | // Truncate the file. |
291 | this.getFileOperations().setLength( this.getFileOperations(). |
292 | getLength() - count * |
293 | this.getBlockSize() ); |
294 | |
295 | this.fireBlocksDeleted( index, count ); |
296 | } |
297 | finally |
298 | { |
299 | if ( monitoring ) |
300 | { |
301 | this.getTaskMonitor().finish( task ); |
302 | } |
303 | } |
304 | } |
305 | |
306 | public void insertBlocks( final long index, |
307 | final long count ) throws IOException |
308 | { |
309 | final long blockCount = this.getBlockCount(); |
310 | |
311 | // Preconditions. |
312 | if ( index < 0L || index > blockCount ) |
313 | { |
314 | throw new ArrayIndexOutOfBoundsException( (int) index ); |
315 | } |
316 | if ( count <= 0L || count > Long.MAX_VALUE - blockCount ) |
317 | { |
318 | throw new ArrayIndexOutOfBoundsException( (int) count ); |
319 | } |
320 | |
321 | this.assertNotClosed(); |
322 | |
323 | this.insertBlocksImpl( index, count, blockCount ); |
324 | } |
325 | |
326 | private void insertBlocksImpl( final long index, final long count, |
327 | final long blockCount ) throws IOException |
328 | { |
329 | final Task task = new Task(); |
330 | long toMoveByte = ( blockCount - index ) * this.getBlockSize(); |
331 | long readPos = blockCount * this.getBlockSize(); |
332 | long writePos = readPos + count * this.getBlockSize(); |
333 | long progress = 0L; |
334 | long progressDivisor = 1L; |
335 | long maxProgress = toMoveByte; |
336 | |
337 | // Clear the cached block count. |
338 | this.cachedBlockCount = NO_CACHED_BLOCKCOUNT; |
339 | |
340 | // Increase the length of the file. |
341 | this.getFileOperations().setLength( this.getFileOperations(). |
342 | getLength() + this.getBlockSize() * |
343 | count ); |
344 | |
345 | // New blocks are inserted at the end of the file. |
346 | if ( toMoveByte <= 0L ) |
347 | { |
348 | this.fireBlocksInserted( index, count ); |
349 | return; |
350 | } |
351 | |
352 | final byte[] buf = this.getBuffer( toMoveByte > Integer.MAX_VALUE |
353 | ? Integer.MAX_VALUE |
354 | : (int) toMoveByte ); |
355 | |
356 | while ( maxProgress > Integer.MAX_VALUE ) |
357 | { |
358 | maxProgress /= 2L; |
359 | progressDivisor *= 2L; |
360 | } |
361 | |
362 | task.setIndeterminate( false ); |
363 | task.setCancelable( false ); |
364 | task.setMinimum( 0 ); |
365 | task.setMaximum( (int) maxProgress ); |
366 | task.setProgress( (int) progress ); |
367 | task.setDescription( new InsertsBlocksMessage() ); |
368 | |
369 | final boolean monitoring = toMoveByte > this.getMonitoringThreshold(); |
370 | if ( monitoring ) |
371 | { |
372 | this.getTaskMonitor().monitor( task ); |
373 | } |
374 | |
375 | try |
376 | { |
377 | // Move all blocks from index inclusive count blocks to the end of |
378 | // the file. |
379 | while ( toMoveByte > 0L ) |
380 | { |
381 | final int moveLen = buf.length >= toMoveByte |
382 | ? (int) toMoveByte |
383 | : buf.length; |
384 | |
385 | readPos -= moveLen; |
386 | writePos -= moveLen; |
387 | this.getFileOperations().setFilePointer( readPos ); |
388 | int read = 0; |
389 | int total = 0; |
390 | |
391 | do |
392 | { |
393 | read = this.getFileOperations(). |
394 | read( buf, total, moveLen - total ); |
395 | |
396 | assert read != FileOperations.EOF : |
397 | "Unexpected end of file."; |
398 | |
399 | total += read; |
400 | |
401 | } |
402 | while ( total < moveLen ); |
403 | |
404 | // Move the block count blocks to the end. |
405 | this.getFileOperations().setFilePointer( writePos ); |
406 | this.getFileOperations().write( buf, 0, moveLen ); |
407 | |
408 | toMoveByte -= moveLen; |
409 | progress += moveLen; |
410 | |
411 | task.setProgress( (int) ( progress / progressDivisor ) ); |
412 | } |
413 | |
414 | this.fireBlocksInserted( index, count ); |
415 | } |
416 | finally |
417 | { |
418 | if ( monitoring ) |
419 | { |
420 | this.getTaskMonitor().finish( task ); |
421 | } |
422 | } |
423 | } |
424 | |
425 | public void readBlock( final long block, final int off, |
426 | final byte[] buf ) throws IOException |
427 | { |
428 | this.readBlock( block, off, buf, 0, buf.length ); |
429 | } |
430 | |
431 | public void readBlock( final long block, final int off, final byte[] buf, |
432 | final int index, final int length ) |
433 | throws IOException |
434 | { |
435 | this.assertValidArguments( block, off, buf, index, length ); |
436 | this.assertNotClosed(); |
437 | |
438 | int totalRead = 0; |
439 | int toRead = length; |
440 | |
441 | this.getFileOperations().setFilePointer( |
442 | block * this.getBlockSize() + off ); |
443 | |
444 | do |
445 | { |
446 | final int read = this.getFileOperations(). |
447 | read( buf, index + totalRead, toRead ); |
448 | |
449 | assert read != FileOperations.EOF : |
450 | "Unexpected end of file."; |
451 | |
452 | totalRead += read; |
453 | toRead -= read; |
454 | |
455 | } |
456 | while ( totalRead < length ); |
457 | } |
458 | |
459 | public void writeBlock( final long block, final int off, |
460 | final byte[] buf ) throws IOException |
461 | { |
462 | this.writeBlock( block, off, buf, 0, buf.length ); |
463 | } |
464 | |
465 | public void writeBlock( final long block, final int off, |
466 | final byte[] buf, |
467 | final int index, final int length ) |
468 | throws IOException |
469 | { |
470 | this.assertValidArguments( block, off, buf, index, length ); |
471 | this.assertNotClosed(); |
472 | |
473 | this.getFileOperations().setFilePointer( |
474 | block * this.getBlockSize() + off ); |
475 | |
476 | this.getFileOperations().write( buf, index, length ); |
477 | } |
478 | |
479 | /** |
480 | * {@inheritDoc} |
481 | * Flushes the instance and closes the {@code FileOperations} implementation |
482 | * backing the instance. |
483 | * |
484 | * @throws IOException if closing the {@code FileOperations} implementation |
485 | * backing the instance fails, or if the instance already is closed. |
486 | */ |
487 | public void close() throws IOException |
488 | { |
489 | this.assertNotClosed(); |
490 | |
491 | this.flush(); |
492 | this.getFileOperations().close(); |
493 | this.closed = true; |
494 | } |
495 | |
496 | public void addStructuredFileListener( |
497 | final StructuredFileListener listener ) |
498 | { |
499 | this.fileListeners.add( StructuredFileListener.class, listener ); |
500 | } |
501 | |
502 | public void removeStructuredFileListener( |
503 | final StructuredFileListener listener ) |
504 | { |
505 | this.fileListeners.remove( StructuredFileListener.class, listener ); |
506 | } |
507 | |
508 | public StructuredFileListener[] getStructuredFileListeners() |
509 | { |
510 | return (StructuredFileListener[]) this.fileListeners.getListeners( |
511 | StructuredFileListener.class ); |
512 | |
513 | } |
514 | |
515 | //----------------------------------------------------------StructuredFile-- |
516 | //--StructuredFileOperations------------------------------------------------ |
517 | |
518 | /** Number of bytes per block. */ |
519 | private int blockSize; |
520 | |
521 | /** Mininum number of bytes to copy to start any task monitoring. */ |
522 | private Integer monitoringThreshold; |
523 | |
524 | /** {@code FileOperations} backing the instance. */ |
525 | private FileOperations fileOperations; |
526 | |
527 | /** Flags the instance as beeing closed. */ |
528 | private boolean closed; |
529 | |
530 | /** |
531 | * Creates a new {@code StructuredFileOperations} instance taking the size |
532 | * of one block in byte and the {@code FileOperations} operations are to be |
533 | * performed with. |
534 | * |
535 | * @param blockSize Number of bytes per block. |
536 | * @param fileOperations {@code FileOperations} implementation to operate |
537 | * on. |
538 | * |
539 | * @throws NullPointerException if {@code fileOperations} is {@code null}. |
540 | * @throws IllegalArgumentException if {@code blockSize} is incompatible |
541 | * with the length of {@code fileOperations}. |
542 | * @throws IOException if getting the length from the {@code fileOperations} |
543 | * fails. |
544 | */ |
545 | public StructuredFileOperations( final int blockSize, |
546 | final FileOperations fileOperations ) |
547 | throws IOException |
548 | { |
549 | super(); |
550 | |
551 | if ( fileOperations == null ) |
552 | { |
553 | throw new NullPointerException( "fileOperations" ); |
554 | } |
555 | if ( blockSize <= 0 ) |
556 | { |
557 | throw new IllegalArgumentException( Integer.toString( blockSize ) ); |
558 | } |
559 | |
560 | this.blockSize = blockSize; |
561 | this.decimalBlockSize = BigDecimal.valueOf( blockSize ); |
562 | this.fileOperations = fileOperations; |
563 | this.assertValidFileLength(); |
564 | } |
565 | |
566 | /** |
567 | * Creates a new {@code StructuredFileOperations} instance taking the size |
568 | * of one block in byte, task monitoring configuration and the |
569 | * {@code FileOperations} operations are to be performed with. |
570 | * |
571 | * @param blockSize Number of bytes per block. |
572 | * @param monitoringThreshold the mininum number of bytes to copy to start |
573 | * any task monitoring. |
574 | * @param fileOperations {@code FileOperations} implementation to operate |
575 | * on. |
576 | * |
577 | * @throws NullPointerException if {@code fileOperations} is {@code null}. |
578 | * @throws IllegalArgumentException if {@code blockSize} is incompatible |
579 | * with the length of {@code fileOperations}. |
580 | * @throws IOException if getting the length from the {@code fileOperations} |
581 | * fails. |
582 | */ |
583 | public StructuredFileOperations( final int blockSize, |
584 | final int monitoringThreshold, |
585 | final FileOperations fileOperations ) |
586 | throws IOException |
587 | { |
588 | super(); |
589 | |
590 | if ( fileOperations == null ) |
591 | { |
592 | throw new NullPointerException( "fileOperations" ); |
593 | } |
594 | if ( blockSize <= 0 ) |
595 | { |
596 | throw new IllegalArgumentException( Integer.toString( blockSize ) ); |
597 | } |
598 | |
599 | this.blockSize = blockSize; |
600 | this.decimalBlockSize = BigDecimal.valueOf( blockSize ); |
601 | this.fileOperations = fileOperations; |
602 | |
603 | if ( monitoringThreshold > 0 ) |
604 | { |
605 | this.monitoringThreshold = new Integer( monitoringThreshold ); |
606 | } |
607 | |
608 | this.assertValidFileLength(); |
609 | } |
610 | |
611 | /** |
612 | * Gets the {@code FileOperations} implementation operations are performed |
613 | * with. |
614 | * |
615 | * @return the {@code FileOperations} implementation operations are |
616 | * performed with. |
617 | */ |
618 | public FileOperations getFileOperations() |
619 | { |
620 | return this.fileOperations; |
621 | } |
622 | |
623 | /** |
624 | * Calls the {@code flush()} method of an underlying |
625 | * {@code FlushableFileOperations} instance, if any. |
626 | * |
627 | * @throws IOException if reading or writing fails. |
628 | */ |
629 | public void flush() throws IOException |
630 | { |
631 | this.assertNotClosed(); |
632 | |
633 | if ( this.getFileOperations() instanceof FlushableFileOperations ) |
634 | { |
635 | ( (FlushableFileOperations) this.getFileOperations() ).flush(); |
636 | } |
637 | } |
638 | |
639 | /** |
640 | * Checks arguments provided to the {@code readBlock} and {@code writeBlock} |
641 | * methods. |
642 | * |
643 | * @throws NullPointerException if {@code buf} is {@code null}. |
644 | * @throws IndexOutOfBoundsException if {@code block} is negative, |
645 | * greater than or equal to {@code getBlockCount()}, or {@code off} is |
646 | * negative, greater than or equal to {@code getBlockSize()}, or |
647 | * {@code index} is negative, greater than or equal to the length of |
648 | * {@code buf}, or {@code length} is negative or greater than the |
649 | * length of {@code buf} minus {@code index} or greater than |
650 | * {@code getBlockSize() minus {@code off}. |
651 | */ |
652 | private void assertValidArguments( final long block, final int off, |
653 | final byte[] buf, final int index, |
654 | final int length ) throws |
655 | NullPointerException, IndexOutOfBoundsException, IOException |
656 | { |
657 | final long blockCount = this.getBlockCount(); |
658 | |
659 | if ( buf == null ) |
660 | { |
661 | throw new NullPointerException( "buf" ); |
662 | } |
663 | if ( block < 0 || block >= blockCount ) |
664 | { |
665 | throw new ArrayIndexOutOfBoundsException( (int) block ); |
666 | } |
667 | if ( off < 0 || off >= this.getBlockSize() ) |
668 | { |
669 | throw new ArrayIndexOutOfBoundsException( off ); |
670 | } |
671 | if ( index < 0 || index >= buf.length ) |
672 | { |
673 | throw new ArrayIndexOutOfBoundsException( index ); |
674 | } |
675 | if ( length < 0L || length > buf.length - index || |
676 | length > this.getBlockSize() - off ) |
677 | { |
678 | throw new ArrayIndexOutOfBoundsException( length ); |
679 | } |
680 | } |
681 | |
682 | /** |
683 | * Checks the length of the provided {@code FileOperations} implementation |
684 | * against property {@code blockSize}. |
685 | * |
686 | * @throws IllegalArgumentException if the combination of property |
687 | * {@code blockSize} and {@code getFileOperations().getLength()} is invalid. |
688 | * @throws IOException if the getting the length fails. |
689 | */ |
690 | private void assertValidFileLength() throws IOException |
691 | { |
692 | if ( this.getFileOperations() != null && |
693 | this.getFileOperations().getLength() % this.getBlockSize() != 0L ) |
694 | { |
695 | throw new IllegalArgumentException( |
696 | Long.toString( this.getFileOperations().getLength() % |
697 | this.getBlockSize() ) ); |
698 | |
699 | } |
700 | } |
701 | |
702 | /** |
703 | * Checks that the instance is not closed. |
704 | * |
705 | * @throws IOException if the instance is closed. |
706 | */ |
707 | private void assertNotClosed() throws IOException |
708 | { |
709 | if ( this.closed ) |
710 | { |
711 | throw new IOException( this.getAlreadyClosedMessage( |
712 | this.getLocale() ) ); |
713 | |
714 | } |
715 | } |
716 | |
717 | /** |
718 | * Gets the value of property {@code monitoringThreshold}. |
719 | * |
720 | * @return the mininum number of bytes to copy to start any task monitoring. |
721 | */ |
722 | public int getMonitoringThreshold() |
723 | { |
724 | if ( this.monitoringThreshold == null ) |
725 | { |
726 | this.monitoringThreshold = this.getDefaultMonitoringThreshold(); |
727 | } |
728 | |
729 | return this.monitoringThreshold.intValue(); |
730 | } |
731 | |
732 | /** |
733 | * Notifies all registered {@code StructuredFileListener}s about inserted |
734 | * blocks. |
735 | * |
736 | * @param index the index new blocks were inserted. |
737 | * @param insertedBlocks the number of blocks which were inserted at |
738 | * {@code index}. |
739 | * |
740 | * @throws IOException if reading or writing fails. |
741 | */ |
742 | private void fireBlocksInserted( |
743 | final long index, final long insertedBlocks ) throws IOException |
744 | { |
745 | final Object[] listeners = this.fileListeners.getListenerList(); |
746 | for ( int i = listeners.length - 2; i >= 0; i -= 2 ) |
747 | { |
748 | if ( listeners[i] == StructuredFileListener.class ) |
749 | { |
750 | ( (StructuredFileListener) listeners[i + 1] ).blocksInserted( |
751 | index, insertedBlocks ); |
752 | |
753 | } |
754 | } |
755 | } |
756 | |
757 | /** |
758 | * Notifies all registered {@code StructuredFileListener}s about deleted |
759 | * blocks. |
760 | * |
761 | * @param index the index blocks were deleted at. |
762 | * @param deletedBlocks the number of blocks which were deleted starting at |
763 | * {@code index}. |
764 | * |
765 | * @throws IOException if reading or writing fails. |
766 | */ |
767 | private void fireBlocksDeleted( |
768 | final long index, final long deletedBlocks ) throws IOException |
769 | { |
770 | final Object[] listeners = this.fileListeners.getListenerList(); |
771 | for ( int i = listeners.length - 2; i >= 0; i -= 2 ) |
772 | { |
773 | if ( listeners[i] == StructuredFileListener.class ) |
774 | { |
775 | ( (StructuredFileListener) listeners[i + 1] ).blocksDeleted( |
776 | index, deletedBlocks ); |
777 | |
778 | } |
779 | } |
780 | } |
781 | |
782 | private byte[] getBuffer( final int requested ) throws IOException |
783 | { |
784 | final long length = this.getFileOperations().getLength(); |
785 | |
786 | if ( requested <= 0 || requested > length ) |
787 | { |
788 | throw new IllegalArgumentException( Integer.toString( requested ) ); |
789 | } |
790 | |
791 | if ( this.defaultBuffer == null ) |
792 | { |
793 | this.defaultBuffer = this.getMemoryManager(). |
794 | allocateBytes( this.getDefaultBufferSize() ); |
795 | |
796 | } |
797 | |
798 | return requested <= this.defaultBuffer.length || |
799 | this.getMemoryManager().getAvailableBytes() < requested |
800 | ? this.defaultBuffer |
801 | : this.getMemoryManager().allocateBytes( requested ); |
802 | |
803 | } |
804 | |
805 | //------------------------------------------------StructuredFileOperations-- |
806 | //--Messages---------------------------------------------------------------- |
807 | |
808 | // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages |
809 | // This section is managed by jdtaus-container-mojo. |
810 | |
811 | /** |
812 | * Gets the text of message <code>alreadyClosed</code>. |
813 | * <blockquote><pre>Instanz geschlossen - keine E/A-Operationen möglich.</pre></blockquote> |
814 | * <blockquote><pre>Instance closed - cannot perform I/O.</pre></blockquote> |
815 | * |
816 | * @param locale The locale of the message instance to return. |
817 | * |
818 | * @return Message stating that an instance is already closed. |
819 | */ |
820 | private String getAlreadyClosedMessage( final Locale locale ) |
821 | { |
822 | return ContainerFactory.getContainer(). |
823 | getMessage( this, "alreadyClosed", locale, null ); |
824 | |
825 | } |
826 | |
827 | // </editor-fold>//GEN-END:jdtausMessages |
828 | |
829 | //----------------------------------------------------------------Messages-- |
830 | } |