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}