001 /*
002 * Copyright (c) Andrey Kuznetsov. All Rights Reserved.
003 *
004 * http://uio.imagero.com
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 *
009 * o Redistributions of source code must retain the above copyright notice,
010 * this list of conditions and the following disclaimer.
011 *
012 * o Redistributions in binary form must reproduce the above copyright notice,
013 * this list of conditions and the following disclaimer in the documentation
014 * and/or other materials provided with the distribution.
015 *
016 * o Neither the name of Andrey Kuznetsov nor the names of
017 * its contributors may be used to endorse or promote products derived
018 * from this software without specific prior written permission.
019 *
020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
022 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
023 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
024 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
025 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
026 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
027 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
028 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
029 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
030 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
031 */
032
033 package com.imagero.uio;
034
035 import com.imagero.uio.bio.BIOFactory;
036 import com.imagero.uio.bio.BufferedRandomAccessIO;
037 import com.imagero.uio.bio.ByteArrayRandomAccessIO;
038 import com.imagero.uio.bio.IOController;
039 import com.imagero.uio.bio.content.ByteArrayContent;
040 import com.imagero.uio.bio.content.CharArrayContent;
041 import com.imagero.uio.bio.content.Content;
042 import com.imagero.uio.bio.content.DoubleArrayContent;
043 import com.imagero.uio.bio.content.FloatArrayContent;
044 import com.imagero.uio.bio.content.IntArrayContent;
045 import com.imagero.uio.bio.content.LongArrayContent;
046 import com.imagero.uio.bio.content.RandomAccessFileContent;
047 import com.imagero.uio.bio.content.ShortArrayContent;
048 import com.imagero.uio.impl.RandomAccessFileWrapper;
049 import com.imagero.uio.impl.RandomAccessFileX;
050
051 import java.io.ByteArrayInputStream;
052 import java.io.File;
053 import java.io.IOException;
054 import java.io.InputStream;
055 import java.io.OutputStream;
056 import java.io.RandomAccessFile;
057 import java.net.URL;
058
059 /**
060 * <pre>
061 * UIOStreamBuilder is a builder pattern implementation and replacement for RandomAccessFactory.
062 * Usual process looks like
063 * File f = ...;
064 * RandomAccessIO ro = new UIOStreamBuilder(f).setByteOrder(RandomAccessIO.LITTLE_ENDIAN).setBuffered(true).create();
065 * or
066 * RandomAccessIO ra = (RandomAccessIO)new UIOStreamBuilder(f).setMode(UIOStreamBuilder.READ_WRITE).create();
067 *
068 * Defaul values are:
069 * mode - UIOStreamBuilder.READ_ONLY
070 * byte order - RandomAccessIO.BIG_ENDIAN
071 * buffered - false (however some streams are always buffered)
072 *
073 * </pre>
074 * @see #create
075 * @see #setBuffered
076 * @see #setByteOrder
077 * @see #setBufferSize
078 * @see #setCacheFile
079 * @see #setMaxBufferCount
080 * @see #setMode
081 * @see #setStart
082 * @see #setLength
083 *
084 * @author Andrey Kuznetsov
085 */
086 public class UIOStreamBuilder {
087
088 public static final String READ_ONLY = "r";
089 public static final String READ_WRITE = "rw";
090
091 String mode = READ_ONLY;
092
093 int byteOrder = ISeekable.BIG_ENDIAN;
094
095 Long start;
096 Long length;
097
098 private boolean buffered = true;
099
100 private Integer maxBufferCount;
101 private Integer bufferSize;
102
103 Creator creator;
104
105 File cache;
106
107 public static int DEFAULT_CHUNK_SIZE = 256*1024;
108 public static int DEFAULT_CHUNK_COUNT = 8;
109
110 public boolean isReadOnly() {
111 if (READ_WRITE.equals(mode)) {
112 return false;
113 }
114 return true;
115 }
116
117 public UIOStreamBuilder(String filename) {
118 this(new File(filename));
119 }
120
121 public UIOStreamBuilder(File file) {
122 this.creator = new FileCreator(file);
123 }
124
125 public UIOStreamBuilder(RandomAccessFile rafSource) {
126 this.creator = new RAFCreator(rafSource);
127 }
128
129 public UIOStreamBuilder(RandomAccessIO ra) {
130 this.creator = new RAIOCreator(ra);
131 }
132
133 public UIOStreamBuilder(RandomAccessInput ro) {
134 this.creator = new RAICreator(ro);
135 }
136
137 public UIOStreamBuilder(byte[] byteSource) {
138 this.creator = new ByteCreator(byteSource);
139 }
140
141 public UIOStreamBuilder(byte[][] byteSource) {
142 this.creator = new Byte2DCreator(byteSource);
143 }
144
145 public UIOStreamBuilder(short[] shortSource) {
146 creator = new ShortCreator(shortSource);
147 }
148
149 public UIOStreamBuilder(short[][] shortSource) {
150 creator = new ShortCreator(shortSource);
151 }
152
153 public UIOStreamBuilder(char[] charSource) {
154 creator = new CharCreator(charSource);
155 }
156
157 public UIOStreamBuilder(char[][] charSource) {
158 creator = new CharCreator(charSource);
159 }
160
161 public UIOStreamBuilder(int[] intSource) {
162 creator = new IntCreator(intSource);
163 }
164
165 public UIOStreamBuilder(int[][] intSource) {
166 creator = new IntCreator(intSource);
167 }
168
169 public UIOStreamBuilder(long[] longSource) {
170 creator = new LongCreator(longSource);
171 }
172
173 public UIOStreamBuilder(long[][] longSource) {
174 creator = new LongCreator(longSource);
175 }
176
177 public UIOStreamBuilder(float[] floatSource) {
178 creator = new FloatCreator(floatSource);
179 }
180
181 public UIOStreamBuilder(float[][] floatSource) {
182 creator = new FloatCreator(floatSource);
183 }
184
185 public UIOStreamBuilder(double[] doubleSource) {
186 creator = new DoubleCreator(doubleSource);
187 }
188
189 public UIOStreamBuilder(double[][] doubleSource) {
190 creator = new DoubleCreator(doubleSource);
191 }
192
193 static private File getTmpDir() {
194 String name = System.getProperty("uio.temp.dir");
195 if(name != null && name.length() > 0) {
196 File f = new File(name);
197 if(!f.exists()) {
198 f.mkdirs();
199 }
200 if(f.isDirectory()) {
201 return f;
202 }
203 }
204 return null;
205 }
206
207 static File createTempFile(String prefix) {
208 File dir = getTmpDir();
209 if(dir != null) {
210 return new File(dir, prefix + Integer.toHexString(dir.hashCode()));
211 }
212 return null;
213 }
214
215 /**
216 * always buffered
217 * @param url
218 */
219 public UIOStreamBuilder(URL url) {
220 creator = new URLCreator(url);
221 }
222
223 /**
224 * always buffered
225 * @param in
226 */
227 public UIOStreamBuilder(InputStream in) {
228 creator = new ISCreator(in);
229 }
230
231 /**
232 * Always buffered.
233 * Two things are very important:
234 * 1. closing RandomAccessOutput created by this method does not close OutputStream
235 * 2. To write data to OutputStream RandomAccessOutput must be closed or flushed.
236 *
237 * @param out OutputStream
238 */
239 public UIOStreamBuilder(OutputStream out) {
240 creator = new OSCreator(out);
241 }
242
243 /**
244 * set mode (writeable or read only)
245 * @param mode READ_ONLY or READ_WRITE
246 * @return UIOStreamBuilder
247 */
248 public UIOStreamBuilder setMode(String mode) {
249 if (READ_ONLY.equals(mode) || READ_WRITE.equals(mode)) {
250 this.mode = mode;
251 return this;
252 } else {
253 throw new IllegalArgumentException(mode);
254 }
255 }
256
257 /**
258 * set byte order (big endian or little endian)
259 * @param byteOrder LITTLE_ENDIAN or BIG_ENDIAN (default value - BIG_ENDIAN)
260 * @return UIOStreamBuilder
261 */
262 public UIOStreamBuilder setByteOrder(int byteOrder) {
263 switch (byteOrder) {
264 case RandomAccessIO.LITTLE_ENDIAN:
265 case RandomAccessIO.BIG_ENDIAN:
266 this.byteOrder = byteOrder;
267 return this;
268 default:
269 throw new IllegalArgumentException("" + byteOrder);
270 }
271 }
272
273 /**
274 * set start offset
275 * @param start start offset of stream (default value - 0L)
276 * @return UIOStreamBuilder
277 */
278 public UIOStreamBuilder setStart(long start) {
279 if (start < 0) {
280 throw new IllegalArgumentException(" " + start);
281 }
282 this.start = new Long(start);
283 return this;
284 }
285
286 /**
287 * set stream length
288 * @param length stream length
289 * @return UIOStreamBuilder
290 */
291 public UIOStreamBuilder setLength(long length) {
292 if (length < 0) {
293 throw new IllegalArgumentException(" " + length);
294 }
295 this.length = new Long(length);
296 if (start == null) {
297 start = new Long(0L);
298 }
299 return this;
300 }
301
302 /**
303 * set if stream should be buffered or not (rather a hint because some streams are always buffered)
304 * @param buffered true or false (default value - false)
305 * @return UIOStreamBuilder
306 */
307 public UIOStreamBuilder setBuffered(boolean buffered) {
308 this.buffered = buffered;
309 return this;
310 }
311
312 /**
313 * set maxBufferCount for MemoryAccessManager - for unbuffered streams this parameter is ignored.
314 * @param max
315 * @return UIOStreamBuilder
316 */
317 public UIOStreamBuilder setMaxBufferCount(int max) {
318 this.maxBufferCount = new Integer(max);
319 return this;
320 }
321
322 /**
323 * set size for memory chunks used by MemoryAccessManager - for unbuffered streams this parameter is ignored.
324 * @param bufferSize
325 * @return UIOStreamBuilder
326 */
327 public UIOStreamBuilder setBufferSize(int bufferSize) {
328 this.bufferSize = new Integer(bufferSize);
329 return this;
330 }
331
332 /**
333 * Set file which can be used to cache data (only for Streams)
334 * @param f File
335 * @return UIOStreamBuilder
336 */
337 public UIOStreamBuilder setCacheFile(File f) {
338 this.cache = f;
339 return this;
340 }
341
342 /**
343 * finally create desired stream
344 * @return RandomAccessIinput
345 * @throws java.io.IOException
346 */
347 public RandomAccessInput create() throws IOException {
348 if (length != null && length.longValue() == 0) {
349 Sys.err.println("Warning: stream length is 0");
350 }
351 if (buffered) {
352 return creator.createBuffered();
353 } else {
354 return creator.create();
355 }
356 }
357
358 abstract class Creator {
359 abstract RandomAccessInput create() throws IOException;
360
361 abstract RandomAccessInput createBuffered() throws IOException;
362
363 protected int getByteOrder() {
364 return byteOrder != 0 ? byteOrder : RandomAccessIO.BIG_ENDIAN;
365 }
366
367 protected int getBufferCount() {
368 if (maxBufferCount != null) {
369 return maxBufferCount.intValue();
370 }
371 return DEFAULT_CHUNK_COUNT;
372 }
373
374 protected int getBufferSize() {
375 if (bufferSize != null) {
376 return bufferSize.intValue();
377 }
378 return DEFAULT_CHUNK_SIZE;
379 }
380 }
381
382 class FileCreator extends Creator {
383
384 File fileSource;
385
386 public FileCreator(File fileSource) {
387 this.fileSource = fileSource;
388 }
389
390 public RandomAccessInput create() throws IOException {
391 final RandomAccessFileX rafx = new RandomAccessFileX(fileSource, mode);
392 if (start == null && length == null) {
393 return new RandomAccessFileWrapper(rafx, getByteOrder());
394 } else if (length == null) {
395 return new RandomAccessFileWrapper(rafx, start.longValue(), getByteOrder());
396 } else {
397 return new RandomAccessFileWrapper(rafx, start.longValue(), length.longValue(), getByteOrder());
398 }
399 }
400
401 RandomAccessInput createBuffered() throws IOException {
402 Content bc = new RandomAccessFileContent(fileSource, mode);
403 IOController controller = new IOController(DEFAULT_CHUNK_SIZE, bc);
404 BufferedRandomAccessIO bio = new BufferedRandomAccessIO(controller);
405 bio.setByteOrder(byteOrder);
406 return bio;
407 }
408 }
409
410 class RAFCreator extends Creator {
411 RandomAccessFile rafSource;
412
413 public RAFCreator(RandomAccessFile rafSource) {
414 this.rafSource = rafSource;
415 }
416
417 public RandomAccessInput create() throws IOException {
418 if (start == null && length == null) {
419 return new RandomAccessFileWrapper(rafSource, getByteOrder());
420 } else if (length == null) {
421 return new RandomAccessFileWrapper(rafSource, start.longValue(), getByteOrder());
422 } else {
423 return new RandomAccessFileWrapper(rafSource, start.longValue(), length.longValue(), getByteOrder());
424 }
425 }
426
427 RandomAccessInput createBuffered() throws IOException {
428 Content bc = new RandomAccessFileContent(rafSource);
429 IOController controller = new IOController(DEFAULT_CHUNK_SIZE, bc);
430 return new BufferedRandomAccessIO(controller);
431 }
432 }
433
434 class RAICreator extends Creator {
435 RandomAccessInput roSource;
436
437 public RAICreator(RandomAccessInput roSource) {
438 this.roSource = roSource;
439 }
440
441 protected int getByteOrder() {
442 return byteOrder != 0 ? byteOrder : roSource.getByteOrder();
443 }
444
445 public RandomAccessInput create() throws IOException {
446 return roSource.createInputChild(start != null ? start.longValue() : 0L, byteOrder, true);
447 }
448
449 RandomAccessInput createBuffered() throws IOException {
450 return create();
451 }
452 }
453
454 class RAIOCreator extends Creator {
455 RandomAccessIO raSource;
456
457 public RAIOCreator(RandomAccessIO raSource) {
458 this.raSource = raSource;
459 }
460
461 protected int getByteOrder() {
462 return byteOrder != 0 ? byteOrder : raSource.getByteOrder();
463 }
464
465 public RandomAccessInput create() throws IOException {
466 return raSource.createIOChild(start != null ? start.longValue() : 0L, byteOrder, true);
467 }
468
469 RandomAccessInput createBuffered() throws IOException {
470 return create();
471 }
472 }
473
474 class Byte2DCreator extends Creator {
475
476 byte[][] byteSource;
477
478 public Byte2DCreator(byte[][] byteSource) {
479 this.byteSource = byteSource;
480 }
481
482 RandomAccessInput create() throws IOException {
483 return createBuffered();
484 }
485
486 RandomAccessInput createBuffered() throws IOException {
487 Content content = new ByteArrayContent(byteSource);
488 IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
489 return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
490 }
491 }
492
493 class ByteCreator extends Creator {
494
495 byte[] byteSource;
496
497 public ByteCreator(byte[] byteSource) {
498 this.byteSource = byteSource;
499 }
500
501 RandomAccessInput create() throws IOException {
502 return new ByteArrayRandomAccessIO(byteSource);
503 }
504
505 RandomAccessInput createBuffered() throws IOException {
506 final byte[][] bytes = new byte[][]{byteSource};
507 Content content = new ByteArrayContent(bytes);
508 IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
509 return new BufferedRandomAccessIO(controller);
510 }
511 }
512
513 class ShortCreator extends Creator {
514 short[][] shortSource;
515
516 public ShortCreator(short[] shortSource) {
517 this(new short[][]{shortSource});
518 }
519
520 public ShortCreator(short[][] shortSource) {
521 this.shortSource = shortSource;
522 }
523
524 RandomAccessInput create() throws IOException {
525 Content content = new ShortArrayContent(shortSource);
526 IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
527 return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
528 }
529
530 RandomAccessInput createBuffered() throws IOException {
531 return create();
532 }
533 }
534
535 class CharCreator extends Creator {
536 char[][] charSource;
537
538 public CharCreator(char[] charSource) {
539 this(new char[][]{charSource});
540 }
541
542 public CharCreator(char[][] charSource) {
543 this.charSource = charSource;
544 }
545
546 RandomAccessInput create() throws IOException {
547 Content content = new CharArrayContent(charSource);
548 IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
549 return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
550 }
551
552 RandomAccessInput createBuffered() throws IOException {
553 return create();
554 }
555 }
556
557 class IntCreator extends Creator {
558 int[][] intSource;
559
560 public IntCreator(int[] intSource) {
561 this(new int[][]{intSource});
562 }
563
564 public IntCreator(int[][] intSource) {
565 this.intSource = intSource;
566 }
567
568 RandomAccessInput create() throws IOException {
569 Content content = new IntArrayContent(intSource);
570 IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
571 return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
572 }
573
574 RandomAccessInput createBuffered() throws IOException {
575 return create();
576 }
577 }
578
579 class LongCreator extends Creator {
580 long[][] longSource;
581
582 public LongCreator(long[] longSource) {
583 this(new long[][]{longSource});
584 }
585
586 public LongCreator(long[][] longSource) {
587 this.longSource = longSource;
588 }
589
590 RandomAccessInput create() throws IOException {
591 Content content = new LongArrayContent(longSource);
592 IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
593 return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
594 }
595
596 RandomAccessInput createBuffered() throws IOException {
597 return create();
598 }
599 }
600
601 class FloatCreator extends Creator {
602 float[][] floatSource;
603
604 public FloatCreator(float[] floatSource) {
605 this(new float[][]{floatSource});
606 }
607
608 public FloatCreator(float[][] floatSource) {
609 this.floatSource = floatSource;
610 }
611
612 RandomAccessInput create() throws IOException {
613 Content content = new FloatArrayContent(floatSource);
614 IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
615 return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
616 }
617
618 RandomAccessInput createBuffered() throws IOException {
619 return create();
620 }
621 }
622
623 class DoubleCreator extends Creator {
624 double[][] doubleSource;
625
626 public DoubleCreator(double[] doubleSource) {
627 this(new double[][]{doubleSource});
628 }
629
630 public DoubleCreator(double[][] doubleSource) {
631 this.doubleSource = doubleSource;
632 }
633
634 RandomAccessInput create() throws IOException {
635 Content content = new DoubleArrayContent(doubleSource);
636 IOController controller = new IOController(DEFAULT_CHUNK_SIZE, content);
637 return new BufferedRandomAccessIO(controller, start != null ? start.longValue() : 0L);
638 }
639
640 RandomAccessInput createBuffered() throws IOException {
641 return create();
642 }
643 }
644
645 class URLCreator extends Creator {
646 URL url;
647 FileCreator fileCreator;
648
649 public URLCreator(URL url) {
650 this.url = url;
651 final String protocol = url.getProtocol();
652 if ("file".equalsIgnoreCase(protocol)) {
653 File f = new File(url.getFile());
654 fileCreator = new FileCreator(f);
655 }
656 }
657
658 RandomAccessInput create() throws IOException {
659 if (fileCreator == null) {
660 return create0();
661 } else {
662 return fileCreator.create();
663 }
664 }
665
666 RandomAccessInput createBuffered() throws IOException {
667 if (fileCreator == null) {
668 return create0();
669 } else {
670 return fileCreator.createBuffered();
671 }
672 }
673
674 private RandomAccessInput create0() {
675 if(cache == null) {
676 cache = createTempFile("urc");
677 }
678 IOController controller = BIOFactory.createIOController(url, cache, getBufferSize());
679 BufferedRandomAccessIO rio = new BufferedRandomAccessIO(controller);
680 rio.setByteOrder(byteOrder);
681 return rio;
682 }
683 }
684
685 class ISCreator extends Creator {
686 InputStream inputStreamSource;
687
688 public ISCreator(InputStream inputStreamSource) {
689 this.inputStreamSource = inputStreamSource;
690 }
691
692 RandomAccessInput create() throws IOException {
693 if (inputStreamSource instanceof ByteArrayInputStream) {
694 return new BaisWrapper((ByteArrayInputStream) inputStreamSource);
695 } else {
696 if (cache == null) {
697 cache = createTempFile("isc");
698 }
699 IOController controller = BIOFactory.createIOController(inputStreamSource, cache, getBufferSize());
700 BufferedRandomAccessIO bio = new BufferedRandomAccessIO(controller);
701 bio.setByteOrder(byteOrder);
702 return bio;
703 }
704 }
705
706 RandomAccessInput createBuffered() throws IOException {
707 return create();
708 }
709 }
710
711 class OSCreator extends Creator {
712 OutputStream outputStreamSource;
713
714 public OSCreator(OutputStream outputStreamSource) {
715 this.outputStreamSource = outputStreamSource;
716 setMode(READ_WRITE);
717 }
718
719 RandomAccessInput create() throws IOException {
720 final BufferedRandomAccessIO bio = BIOFactory.create(outputStreamSource);
721 bio.setByteOrder(byteOrder);
722 return bio;
723 }
724
725 RandomAccessInput createBuffered() throws IOException {
726 return create();
727 }
728 }
729 }