001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.io; 031 032import java.io.BufferedInputStream; 033import java.io.BufferedOutputStream; 034import java.io.BufferedReader; 035import java.io.BufferedWriter; 036import java.io.ByteArrayInputStream; 037import java.io.ByteArrayOutputStream; 038import java.io.DataInput; 039import java.io.DataInputStream; 040import java.io.DataOutput; 041import java.io.DataOutputStream; 042import java.io.File; 043import java.io.FileInputStream; 044import java.io.FileOutputStream; 045import java.io.IOException; 046import java.io.InputStream; 047import java.io.InputStreamReader; 048import java.io.OutputStream; 049import java.io.OutputStreamWriter; 050import java.io.PrintWriter; 051import java.io.Reader; 052import java.io.Writer; 053import java.lang.reflect.Constructor; 054import java.util.Arrays; 055import java.util.Scanner; 056 057import org.objenesis.strategy.StdInstantiatorStrategy; 058 059import com.esotericsoftware.kryo.Kryo; 060import com.esotericsoftware.kryo.KryoException; 061import com.esotericsoftware.kryo.io.Input; 062import com.esotericsoftware.kryo.io.Output; 063 064/** 065 * Methods for reading Readable objects and writing Writeable objects. 066 * 067 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 068 * @author Sina Samangooei (ss@ecs.soton.ac.uk) 069 */ 070public class IOUtils { 071 /** 072 * Create a new instance of the given class. The class must have a no-args 073 * constructor. The constructor doesn't have to be public. 074 * 075 * @param <T> 076 * The type of object. 077 * @param cls 078 * The class. 079 * @return a new instance. 080 */ 081 public static <T extends InternalReadable> T newInstance(Class<T> cls) { 082 try { 083 return cls.newInstance(); 084 } catch (final Exception e) { 085 try { 086 final Constructor<T> constr = cls.getDeclaredConstructor(); 087 088 if (constr != null) { 089 constr.setAccessible(true); 090 return constr.newInstance(); 091 } 092 } catch (final Exception e1) { 093 throw new RuntimeException(e); 094 } 095 096 throw new RuntimeException(e); 097 } 098 } 099 100 /** 101 * Create a new instance of the given class. The class must have a no-args 102 * constructor. The constructor doesn't have to be public. 103 * 104 * @param <T> 105 * The type of object. 106 * @param className 107 * The class name. 108 * @return a new instance. 109 */ 110 @SuppressWarnings("unchecked") 111 public static <T extends InternalReadable> T newInstance(String className) { 112 try { 113 return newInstance(((Class<T>) Class.forName(className))); 114 } catch (final Exception e) { 115 throw new RuntimeException(e); 116 } 117 } 118 119 /** 120 * Read an object from a file. 121 * 122 * @param <T> 123 * instance type expected 124 * @param f 125 * the file 126 * @return object read from file 127 * @throws IOException 128 * problem reading file 129 */ 130 @SuppressWarnings("unchecked") 131 public static <T extends InternalReadable> T read(File f) throws IOException { 132 final ObjectWrapper ow = IOUtils.read(f, ObjectWrapper.class); 133 134 return (T) ow.object; 135 } 136 137 /** 138 * Read an object from a file. 139 * 140 * @param <T> 141 * instance type expected 142 * @param f 143 * the file 144 * @param charset 145 * the charsetName sent to the reader which reads the file IFF 146 * the file is not binary 147 * @return object read from file 148 * @throws IOException 149 * problem reading file 150 */ 151 @SuppressWarnings("unchecked") 152 public static <T extends InternalReadable> T read(File f, String charset) throws IOException { 153 final ObjectWrapper ow = IOUtils.read(f, ObjectWrapper.class, charset); 154 155 return (T) ow.object; 156 } 157 158 /** 159 * Write an object to a file fully. The object will be saved with class 160 * information so that it can be automatically re-instantiated using 161 * {@link #read(File)} without needing to know the actual type. 162 * 163 * @param <T> 164 * instance type expected 165 * @param f 166 * the file 167 * @param object 168 * the object to write 169 * @throws IOException 170 * problem reading file 171 */ 172 public static <T extends WriteableBinary> void writeBinaryFull(File f, T object) throws IOException { 173 IOUtils.writeBinary(f, new ObjectWrapper(object)); 174 } 175 176 /** 177 * Write an object to a file fully. The object will be saved with class 178 * information so that it can be automatically re-instantiated using 179 * {@link #read(File)} without needing to know the actual type. 180 * 181 * @param <T> 182 * instance type expected 183 * @param f 184 * the file 185 * @param object 186 * the object to write 187 * @throws IOException 188 * problem reading file 189 */ 190 public static <T extends WriteableASCII> void writeASCIIFull(File f, T object) throws IOException { 191 IOUtils.writeASCII(f, new ObjectWrapper(object)); 192 } 193 194 /** 195 * Write an object to a file fully. The object will be saved with class 196 * information so that it can be automatically re-instantiated using 197 * {@link #read(File)} without needing to know the actual type. 198 * 199 * @param <T> 200 * instance type expected 201 * @param f 202 * the file 203 * @param object 204 * the object to write 205 * @param charset 206 * the charsetName sent to the internal writer 207 * @throws IOException 208 * problem reading file 209 */ 210 public static <T extends WriteableASCII> void writeASCIIFull(File f, T object, String charset) throws IOException { 211 IOUtils.writeASCII(f, new ObjectWrapper(object), charset); 212 } 213 214 /** 215 * Read a new instance of type class from a file. 216 * 217 * @param <T> 218 * instance type expected 219 * @param f 220 * the file 221 * @param cls 222 * the class 223 * @return new instance of class instantiated from the file 224 * @throws IOException 225 * problem reading file 226 */ 227 public static <T extends InternalReadable> T read(File f, Class<T> cls) throws IOException { 228 return read(f, newInstance(cls)); 229 } 230 231 /** 232 * Read a new instance of type class from a file. 233 * 234 * @param <T> 235 * instance type expected 236 * @param f 237 * the file 238 * @param cls 239 * the class 240 * @param charset 241 * the charsetName sent to the reader which reads the file IFF 242 * the file is not binary 243 * @return new instance of class instantiated from the file 244 * @throws IOException 245 * problem reading file 246 */ 247 public static <T extends InternalReadable> T read(File f, Class<T> cls, String charset) throws IOException { 248 return read(f, newInstance(cls), charset); 249 } 250 251 /** 252 * Read a new instance of type class from an input stream. 253 * 254 * @param <T> 255 * instance type expected 256 * @param ios 257 * the input stream 258 * @param cls 259 * the class 260 * @return new instance of class instantiated from the stream 261 * @throws IOException 262 * problem reading stream 263 */ 264 public static <T extends InternalReadable> T read(InputStream ios, Class<T> cls) throws IOException { 265 return read(ios, newInstance(cls)); 266 } 267 268 /** 269 * Read a new instance of type class from an input stream. 270 * 271 * @param <T> 272 * instance type expected 273 * @param ios 274 * the input stream 275 * @param cls 276 * the class 277 * @param charset 278 * the charsetName sent to the internal inputstreamreader 279 * @return new instance of class instantiated from the stream 280 * @throws IOException 281 * problem reading stream 282 */ 283 public static <T extends InternalReadable> T read(InputStream ios, Class<T> cls, String charset) throws IOException { 284 return read(ios, newInstance(cls), charset); 285 } 286 287 /** 288 * Open file input stream and call Readable#read(InputStream,T) 289 * 290 * @param <T> 291 * instance type expected 292 * @param f 293 * the file 294 * @param obj 295 * the object of type T 296 * @return A new instance of type T 297 * @throws IOException 298 * an error reading the file 299 */ 300 public static <T extends InternalReadable> T read(File f, T obj) throws IOException { 301 final FileInputStream fos = new FileInputStream(f); 302 try { 303 return read(fos, obj); 304 } finally { 305 if (fos != null) 306 fos.close(); 307 } 308 } 309 310 /** 311 * Open file input stream and call Readable#read(InputStream,T) 312 * 313 * @param <T> 314 * instance type expected 315 * @param f 316 * the file 317 * @param obj 318 * the object of type T 319 * @param charset 320 * the charsetName sent to the reader which reads the file IFF 321 * the file is not binary 322 * @return A new instance of type T 323 * @throws IOException 324 * an error reading the file 325 */ 326 public static <T extends InternalReadable> T read(File f, T obj, String charset) throws IOException { 327 final FileInputStream fos = new FileInputStream(f); 328 try { 329 return read(fos, obj, charset); 330 } finally { 331 if (fos != null) 332 fos.close(); 333 } 334 } 335 336 /** 337 * Read an instance of an object from an input stream. The stream is tested 338 * to contain the ASCII or binary header and the appropriate read instance 339 * is called. 340 * 341 * @see Readable#binaryHeader 342 * @see Readable#readBinary 343 * @see Readable#readASCII 344 * @param <T> 345 * instance type expected 346 * @param fis 347 * the input stream 348 * @param obj 349 * the object to instantiate 350 * @return the object 351 * 352 * @throws IOException 353 * if there is a problem reading the stream from the file 354 */ 355 public static <T extends InternalReadable> T read(InputStream fis, T obj) throws IOException { 356 final BufferedInputStream bis = new BufferedInputStream(fis); 357 if (obj instanceof ReadableBinary && isBinary(bis, ((ReadableBinary) obj).binaryHeader())) { 358 final byte[] header = new byte[((ReadableBinary) obj).binaryHeader().length]; 359 bis.read(header, 0, header.length); 360 ((ReadableBinary) obj).readBinary(new DataInputStream(bis)); 361 return obj; 362 } else { 363 final BufferedReader br = new BufferedReader(new InputStreamReader(bis)); 364 final char[] holder = new char[((ReadableASCII) obj).asciiHeader().length()]; 365 br.read(holder); 366 ((ReadableASCII) obj).readASCII(new Scanner(br)); 367 return obj; 368 } 369 } 370 371 /** 372 * Read an instance of an object from an input stream. The stream is tested 373 * to contain the ASCII or binary header and the appropriate read instance 374 * is called. 375 * 376 * @see Readable#binaryHeader 377 * @see Readable#readBinary 378 * @see Readable#readASCII 379 * @param <T> 380 * instance type expected 381 * @param fis 382 * the input stream 383 * @param obj 384 * the object to instantiate 385 * @param charset 386 * the charsetName sent the to the inputstreamreader 387 * @return the object 388 * 389 * @throws IOException 390 * if there is a problem reading the stream from the file 391 */ 392 public static <T extends InternalReadable> T read(InputStream fis, T obj, String charset) throws IOException { 393 final BufferedInputStream bis = new BufferedInputStream(fis); 394 if (obj instanceof ReadableBinary && isBinary(bis, ((ReadableBinary) obj).binaryHeader())) { 395 final byte[] header = new byte[((ReadableBinary) obj).binaryHeader().length]; 396 bis.read(header, 0, header.length); 397 ((ReadableBinary) obj).readBinary(new DataInputStream(bis)); 398 return obj; 399 } else { 400 final BufferedReader br = new BufferedReader(new InputStreamReader(bis, charset)); 401 final char[] holder = new char[((ReadableASCII) obj).asciiHeader().length()]; 402 br.read(holder); 403 ((ReadableASCII) obj).readASCII(new Scanner(br)); 404 return obj; 405 } 406 } 407 408 /** 409 * Read an instance of an object from a reader. The stream is assumed to be 410 * ascii and the appropriate read instance is called. 411 * 412 * @see Readable#readASCII 413 * @param <T> 414 * instance type expected 415 * @param fis 416 * the input stream 417 * @param obj 418 * the object to instantiate 419 * @return the object 420 * 421 * @throws IOException 422 * if there is a problem reading the stream from the file 423 */ 424 public static <T extends InternalReadable> T read(Reader fis, T obj) throws IOException { 425 final BufferedReader br = new BufferedReader(fis); 426 final char[] holder = new char[((ReadableASCII) obj).asciiHeader().length()]; 427 br.read(holder); 428 ((ReadableASCII) obj).readASCII(new Scanner(br)); 429 return obj; 430 } 431 432 /** 433 * Read an instance of an object from a reader. The stream is assumed to be 434 * ascii and the appropriate read instance is called. 435 * 436 * @see Readable#readASCII 437 * @param <T> 438 * instance type expected 439 * @param fis 440 * the input stream 441 * @param cls 442 * the object to instantiate 443 * @return the object 444 * 445 * @throws IOException 446 * if there is a problem reading the stream from the file 447 */ 448 public static <T extends InternalReadable> T read(Reader fis, Class<T> cls) throws IOException { 449 return read(fis, newInstance(cls)); 450 } 451 452 /** 453 * Opens an input stream and calls input stream version 454 * 455 * @see IOUtils#isBinary(BufferedInputStream,byte[]) 456 * 457 * @param f 458 * file containing data 459 * @param header 460 * expected header in binary format 461 * @return is the file in the binary format 462 * @throws IOException 463 * if there was a problem reading the input stream 464 */ 465 public static boolean isBinary(File f, byte[] header) throws IOException { 466 FileInputStream fis = null; 467 BufferedInputStream bis = null; 468 try { 469 fis = new FileInputStream(f); 470 bis = new BufferedInputStream(fis); 471 472 return isBinary(bis, header); 473 } finally { 474 if (fis != null) 475 try { 476 fis.close(); 477 } catch (final IOException e) { 478 } 479 if (bis != null) 480 try { 481 bis.close(); 482 } catch (final IOException e) { 483 } 484 } 485 } 486 487 /** 488 * Extracts the binary header from object and calls the byte[] version 489 * 490 * @see IOUtils#isBinary(BufferedInputStream,byte[]) 491 * 492 * @param <T> 493 * expected data type 494 * @param bis 495 * stream containing data 496 * @param obj 497 * instance of expected data type 498 * @return does the stream contain binary information 499 * @throws IOException 500 * problem reading input stream 501 */ 502 public static <T extends ReadableBinary> boolean isBinary(BufferedInputStream bis, T obj) throws IOException { 503 return isBinary(bis, obj.binaryHeader()); 504 } 505 506 /** 507 * Checks whether a given input stream contains readable binary information 508 * by checking for the first header.length bytes == header. The stream is 509 * reset to the beginning of the header once checked. 510 * 511 * @param bis 512 * stream containing data 513 * @param header 514 * expected binary header 515 * @return does the stream contain binary information 516 * @throws IOException 517 * problem reading or reseting the stream 518 */ 519 public static boolean isBinary(BufferedInputStream bis, byte[] header) throws IOException { 520 bis.mark(header.length + 10); 521 final byte[] aheader = new byte[header.length]; 522 bis.read(aheader, 0, aheader.length); 523 bis.reset(); 524 525 return Arrays.equals(aheader, header); 526 } 527 528 /** 529 * Write a Writeable T object instance. Opens a file stream and calls output 530 * stream version 531 * 532 * @see IOUtils#writeBinary(OutputStream,Writeable) 533 * 534 * @param <T> 535 * data type to be written 536 * @param f 537 * file to write instance to 538 * @param obj 539 * instance to be written 540 * @throws IOException 541 * error reading file 542 */ 543 public static <T extends WriteableBinary> void writeBinary(File f, T obj) throws IOException { 544 final FileOutputStream fos = new FileOutputStream(f); 545 try { 546 writeBinary(fos, obj); 547 fos.flush(); 548 } finally { 549 if (fos != null) 550 fos.close(); 551 } 552 } 553 554 /** 555 * Write a Writeable T object instance to the provided output stream, calls 556 * BufferedOutputStream version 557 * 558 * @see IOUtils#writeBinary(BufferedOutputStream,Writeable) 559 * 560 * @param <T> 561 * Expected data type 562 * @param fos 563 * output stream 564 * @param obj 565 * the object to write 566 * @throws IOException 567 * error writing to stream 568 */ 569 public static <T extends WriteableBinary> void writeBinary(OutputStream fos, T obj) throws IOException { 570 BufferedOutputStream bos = null; 571 try { 572 bos = new BufferedOutputStream(fos); 573 writeBinary(bos, obj); 574 } finally { 575 if (bos != null) { 576 bos.flush(); 577 } 578 } 579 580 } 581 582 /** 583 * Writeable object is written to the output stream in binary format. 584 * Firstly the binaryHeader is written then the object is handed the output 585 * stream to write it's content. 586 * 587 * @see Writeable#writeBinary(java.io.DataOutput) 588 * @see Writeable#binaryHeader() 589 * 590 * @param <T> 591 * instance type expected 592 * @param bos 593 * the output stream 594 * @param obj 595 * the object to write 596 * @throws IOException 597 * error writing to stream 598 */ 599 public static <T extends WriteableBinary> void writeBinary(BufferedOutputStream bos, T obj) throws IOException { 600 final DataOutputStream dos = new DataOutputStream(bos); 601 dos.write(obj.binaryHeader()); 602 obj.writeBinary(dos); 603 } 604 605 /** 606 * Writeable object is written to the a file in ASCII format. File stream is 607 * opened and stream version is called 608 * 609 * @see IOUtils#writeASCII(OutputStream, Writeable) 610 * 611 * @param <T> 612 * instance type expected 613 * @param f 614 * the file to write to 615 * @param obj 616 * the object to write 617 * @throws IOException 618 * error writing to file 619 */ 620 public static <T extends WriteableASCII> void writeASCII(File f, T obj) throws IOException { 621 final FileOutputStream fos = new FileOutputStream(f); 622 try { 623 writeASCII(fos, obj); 624 fos.flush(); 625 } finally { 626 if (fos != null) 627 fos.close(); 628 } 629 } 630 631 /** 632 * Writeable object is written to the a file in ASCII format. File stream is 633 * opened and stream version is called 634 * 635 * @see IOUtils#writeASCII(OutputStream, Writeable) 636 * 637 * @param <T> 638 * instance type expected 639 * @param f 640 * the file to write to 641 * @param obj 642 * the object to write 643 * @param charset 644 * the charsetName sent to the internal writer 645 * @throws IOException 646 * error writing to file 647 */ 648 public static <T extends WriteableASCII> void writeASCII(File f, T obj, String charset) throws IOException { 649 final FileOutputStream fos = new FileOutputStream(f); 650 try { 651 writeASCII(fos, obj, charset); 652 fos.flush(); 653 } finally { 654 if (fos != null) 655 fos.close(); 656 } 657 } 658 659 /** 660 * Write the object in ASCII format to the output stream. Construct a 661 * PrintWriter using the outputstream, write the object's ASCII header then 662 * write the object in ASCII format. 663 * 664 * @see PrintWriter 665 * @see Writeable#asciiHeader() 666 * @see Writeable#writeASCII(PrintWriter) 667 * 668 * @param <T> 669 * instance type expected 670 * @param fos 671 * the output stream 672 * @param obj 673 * the object 674 * @throws IOException 675 * error writing to stream 676 */ 677 public static <T extends WriteableASCII> void writeASCII(OutputStream fos, T obj) throws IOException { 678 PrintWriter pw = null; 679 try { 680 pw = new PrintWriter(fos); 681 pw.print(obj.asciiHeader()); 682 obj.writeASCII(pw); 683 } finally { 684 if (pw != null) { 685 pw.flush(); 686 } 687 } 688 } 689 690 /** 691 * Write the object in ASCII format to the output stream. Construct a 692 * PrintWriter using the outputstream, write the object's ASCII header then 693 * write the object in ASCII format. 694 * 695 * @see PrintWriter 696 * @see Writeable#asciiHeader() 697 * @see Writeable#writeASCII(PrintWriter) 698 * 699 * @param <T> 700 * instance type expected 701 * @param fos 702 * the output stream 703 * @param obj 704 * the object 705 * @param charset 706 * the charsetName sent to the internal outputstreamwriter 707 * @throws IOException 708 * error writing to stream 709 */ 710 public static <T extends WriteableASCII> void writeASCII(OutputStream fos, T obj, String charset) throws IOException { 711 PrintWriter pw = null; 712 try { 713 pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(fos, charset))); 714 pw.print(obj.asciiHeader()); 715 obj.writeASCII(pw); 716 } finally { 717 if (pw != null) { 718 pw.flush(); 719 } 720 } 721 } 722 723 /** 724 * Write the object in ASCII format to the output stream. Construct a 725 * PrintWriter using the outputstream, write the object's ASCII header then 726 * write the object in ASCII format. 727 * 728 * @see PrintWriter 729 * @see Writeable#asciiHeader() 730 * @see Writeable#writeASCII(PrintWriter) 731 * 732 * @param <T> 733 * instance type expected 734 * @param writer 735 * the output stream 736 * @param obj 737 * the object 738 * @throws IOException 739 * error writing to stream 740 */ 741 public static <T extends WriteableASCII> void writeASCII(Writer writer, T obj) throws IOException { 742 PrintWriter pw = null; 743 try { 744 pw = new PrintWriter(writer); 745 pw.print(obj.asciiHeader()); 746 obj.writeASCII(pw); 747 } finally { 748 if (pw != null) { 749 pw.flush(); 750 } 751 } 752 } 753 754 /** 755 * Check whether a given file is readable by a given Writeable class. 756 * Instantiates the class to get it's binary and ascii header which is then 757 * passed to the byte[] version of this method 758 * 759 * @see Readable#asciiHeader() 760 * 761 * @param <T> 762 * instance type expected 763 * @param f 764 * the file to check 765 * @param cls 766 * the class to instantiate the Readable object 767 * @return is file readable by a given class 768 * @throws IOException 769 * error reading file 770 */ 771 public static <T extends InternalReadable> boolean readable(File f, Class<T> cls) throws IOException { 772 final InternalReadable obj = newInstance(cls); 773 774 return (obj instanceof ReadableBinary && readable(f, ((ReadableBinary) obj).binaryHeader())) || 775 (obj instanceof ReadableASCII && readable(f, ((ReadableASCII) obj).asciiHeader())); 776 } 777 778 /** 779 * Check whether a file is readable by checking it's first bytes contains 780 * the header. Converts the header to a byte[] and calls the byte[] version 781 * of this method 782 * 783 * @see IOUtils#readable(File, byte[]) 784 * 785 * @param f 786 * the file to check 787 * @param header 788 * the header to check with 789 * @return does this file contain this header 790 * @throws IOException 791 * error reading file 792 */ 793 public static boolean readable(File f, String header) throws IOException { 794 return readable(f, header.getBytes()); 795 } 796 797 /** 798 * Check readability by checking whether a file starts with a given header. 799 * Instantiate an input stream and check whether the stream isBinary (i.e. 800 * starts with the header) 801 * 802 * @see IOUtils#isBinary(BufferedInputStream, byte[]) 803 * 804 * @param f 805 * file to check 806 * @param header 807 * the expected header 808 * @return does the file start with the header 809 * @throws IOException 810 * error reading file 811 */ 812 public static boolean readable(File f, byte[] header) throws IOException { 813 FileInputStream fos = null; 814 try { 815 fos = new FileInputStream(f); 816 final BufferedInputStream bis = new BufferedInputStream(fos); 817 return isBinary(bis, header); 818 } finally { 819 try { 820 if (fos != null) 821 fos.close(); 822 } catch (final IOException e) { 823 } 824 } 825 } 826 827 /** 828 * Check whether an InputStream can be read by an instantiated class based 829 * on it's binary and ascii headers. Buffered so the stream can be reset 830 * after the check. 831 * 832 * @param <T> 833 * instance type expected 834 * @param bis 835 * the stream 836 * @param cls 837 * the class to instantiate and check 838 * @return can an object be read from this stream of the class type 839 * @throws IOException 840 * error reading stream 841 */ 842 public static <T extends InternalReadable> boolean readable(BufferedInputStream bis, Class<T> cls) throws IOException 843 { 844 final InternalReadable obj = newInstance(cls); 845 846 return (obj instanceof ReadableBinary && readable(bis, ((ReadableBinary) obj).binaryHeader())) || 847 (obj instanceof ReadableASCII && readable(bis, ((ReadableASCII) obj).asciiHeader())); 848 } 849 850 /** 851 * Check whether an input stream starts with a header string. Calls the 852 * byte[] version of this function 853 * 854 * @see IOUtils#isBinary(BufferedInputStream, byte[]) 855 * 856 * @param bis 857 * the input stream 858 * @param header 859 * the header 860 * @return whether the stream starts with the string 861 * @throws IOException 862 * error reading stream 863 */ 864 public static boolean readable(BufferedInputStream bis, String header) throws IOException { 865 return readable(bis, header.getBytes()); 866 } 867 868 /** 869 * Check whether a stream starts with a header. Uses isBinary (therefore 870 * resetting the stream after the check) 871 * 872 * @param bis 873 * the input stream 874 * @param header 875 * the byte[] header 876 * @return whether the stream starts with the header 877 * @throws IOException 878 * error reading stream 879 */ 880 public static boolean readable(BufferedInputStream bis, byte[] header) throws IOException { 881 return isBinary(bis, header); 882 } 883 884 /** 885 * Convenience function for serializing a writeable object as a byte array. 886 * Calls {@link IOUtils#writeBinary(OutputStream, WriteableBinary)} on a 887 * {@link ByteArrayOutputStream} then calls 888 * {@link ByteArrayOutputStream#toByteArray()} 889 * 890 * @param object 891 * @return serialised object 892 * @throws IOException 893 */ 894 public static byte[] serialize(WriteableBinary object) throws IOException { 895 final ByteArrayOutputStream stream = new ByteArrayOutputStream(); 896 IOUtils.writeBinary(stream, object); 897 return stream.toByteArray(); 898 } 899 900 /** 901 * Convenience function for deserializing an object from a String. Calls 902 * {@link ReadableASCII#readASCII(Scanner)} with a Scanner initialized from 903 * a StringReader 904 * 905 * @param source 906 * where to read from 907 * @param clazz 908 * the class of the output 909 * @param <T> 910 * the type to output 911 * @return a new instance of T 912 * @throws IOException 913 */ 914 public static <T extends ReadableASCII> T fromString(String source, Class<T> clazz) throws IOException { 915 final T out = IOUtils.read(new ByteArrayInputStream(source.getBytes()), clazz); 916 return out; 917 } 918 919 /** 920 * Convenience function for deserializing an object from a byte array. Calls 921 * {@link IOUtils#read(InputStream, Class)} on a 922 * {@link ByteArrayInputStream}. 923 * 924 * @param source 925 * where to read from 926 * @param clazz 927 * the class of the output 928 * @param <T> 929 * the type to output 930 * @return a new instance of T 931 * @throws IOException 932 */ 933 public static <T extends ReadableBinary> T deserialize(byte[] source, Class<T> clazz) throws IOException { 934 final ByteArrayInputStream stream = new ByteArrayInputStream(source); 935 final T out = IOUtils.read(stream, clazz); 936 return out; 937 } 938 939 /** 940 * Convenience function for deserializing an object from a byte array. Calls 941 * {@link IOUtils#read(InputStream, Class)} on a 942 * {@link ByteArrayInputStream}. 943 * 944 * @param source 945 * where to read from 946 * @param clazz 947 * the class of the output 948 * @param <T> 949 * the type to output 950 * @param skip 951 * number of bytes to skip 952 * @return a new instance of T 953 * @throws IOException 954 */ 955 public static <T extends ReadableBinary> T deserialize(byte[] source, long skip, Class<T> clazz) throws IOException { 956 final ByteArrayInputStream stream = new ByteArrayInputStream(source); 957 stream.skip(skip); 958 final T out = IOUtils.read(stream, clazz); 959 return out; 960 } 961 962 /** 963 * Convenience function for deserializing an object from a byte array. Calls 964 * {@link IOUtils#read(InputStream, Class)} on a 965 * {@link ByteArrayInputStream}. 966 * 967 * @param source 968 * where to read from 969 * @param instance 970 * a T instance 971 * @param <T> 972 * the type to output 973 * @return a new instance of T 974 * @throws IOException 975 */ 976 public static <T extends InternalReadable> T deserialize(byte[] source, T instance) throws IOException { 977 final ByteArrayInputStream stream = new ByteArrayInputStream(source); 978 final T out = IOUtils.read(stream, instance); 979 return out; 980 } 981 982 /** 983 * Writes an object to a file using the Kryo serialisation library. The 984 * object doesn't need to have any special serialisation attributes. 985 * 986 * @param obj 987 * the object to write 988 * @param out 989 * the output sink 990 * @throws IOException 991 */ 992 public static void writeToFile(Object obj, File out) throws IOException { 993 DataOutputStream dos = null; 994 try { 995 dos = new DataOutputStream(new FileOutputStream(out)); 996 997 write(obj, dos); 998 } finally { 999 if (dos != null) 1000 try { 1001 dos.close(); 1002 } catch (final IOException e) { 1003 } 1004 } 1005 } 1006 1007 /** 1008 * Writes an object using the Kryo serialisation library. The object doesn't 1009 * need to have any special serialisation attributes. 1010 * 1011 * @param obj 1012 * the object to write 1013 * @param out 1014 * the output sink 1015 * @throws IOException 1016 */ 1017 public static void write(Object obj, DataOutput out) throws IOException { 1018 final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 1019 final Output output = new Output(bos); 1020 1021 final Kryo kryo = new Kryo(); 1022 kryo.writeClassAndObject(output, obj); 1023 output.flush(); 1024 1025 final byte[] array = bos.toByteArray(); 1026 1027 out.writeInt(array.length); 1028 out.write(array); 1029 } 1030 1031 /** 1032 * Utility method to read any object written with 1033 * {@link #write(Object, DataOutput)}. 1034 * 1035 * @param <T> 1036 * type of object 1037 * @param in 1038 * input 1039 * @return the object 1040 * @throws IOException 1041 */ 1042 @SuppressWarnings("unchecked") 1043 public static <T> T read(DataInput in) throws IOException { 1044 final int length = in.readInt(); 1045 1046 final byte[] bytes = new byte[length]; 1047 in.readFully(bytes); 1048 1049 final Kryo kryo = new Kryo(); 1050 Object obj; 1051 try { 1052 obj = kryo.readClassAndObject(new Input(bytes)); 1053 } catch (final KryoException e) { 1054 kryo.setInstantiatorStrategy(new StdInstantiatorStrategy()); 1055 obj = kryo.readClassAndObject(new Input(bytes)); 1056 } 1057 return (T) obj; 1058 } 1059 1060 /** 1061 * Utility method to read any object written with 1062 * {@link #writeToFile(Object, File)}. 1063 * 1064 * @param <T> 1065 * type of object 1066 * @param in 1067 * input file 1068 * @return the object 1069 * @throws IOException 1070 */ 1071 public static <T> T readFromFile(File in) throws IOException { 1072 DataInputStream din = null; 1073 try { 1074 din = new DataInputStream(new FileInputStream(in)); 1075 1076 // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954 1077 return IOUtils.<T> read(din); 1078 } finally { 1079 if (din != null) 1080 try { 1081 din.close(); 1082 } catch (final IOException e) { 1083 } 1084 } 1085 } 1086 1087 /** 1088 * Test whether the data in the given {@link InputStream} can be read by the 1089 * given {@link InputStreamObjectReader}. This method tries to ensure that 1090 * the stream is reset to its initial condition. 1091 * 1092 * @param reader 1093 * the {@link InputStreamObjectReader}. 1094 * @param is 1095 * the stream 1096 * @param name 1097 * the name of the file/object behind the stream (can be null) 1098 * @return true if the {@link InputStreamObjectReader} can read from this 1099 * stream; false otherwise. 1100 * @throws IOException 1101 * if an error occurs resetting the stream. 1102 */ 1103 public static boolean canRead(InputStreamObjectReader<?> reader, BufferedInputStream is, String name) 1104 throws IOException 1105 { 1106 try { 1107 is.mark(1024 * 1024); 1108 return reader.canRead(is, name); 1109 } finally { 1110 is.reset(); 1111 } 1112 } 1113 1114 /** 1115 * Test whether the data in the given source can be read by the given 1116 * {@link ObjectReader}. 1117 * 1118 * @param reader 1119 * the {@link ObjectReader}. 1120 * @param source 1121 * the source 1122 * @param name 1123 * the name of the file/object behind the stream (can be null) 1124 * @return true if the {@link ObjectReader} can read from this stream; false 1125 * otherwise. 1126 * @throws IOException 1127 * if an error occurs resetting the stream. 1128 */ 1129 public static <SRC> boolean canRead(ObjectReader<?, SRC> reader, SRC source, String name) throws IOException { 1130 return reader.canRead(source, name); 1131 } 1132}