001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the file COPYING.                     *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * If you do not have access to this file, you may request a copy from       *
011 * help@hdfgroup.org.                                                        *
012 ****************************************************************************/
013
014package hdf.view;
015
016import java.io.BufferedInputStream;
017import java.io.BufferedOutputStream;
018import java.io.File;
019import java.io.FileInputStream;
020import java.io.FileOutputStream;
021import java.io.RandomAccessFile;
022import java.util.Enumeration;
023import java.util.Hashtable;
024import java.util.StringTokenizer;
025
026import javax.swing.filechooser.FileFilter;
027
028/**
029 * A convenience implementation of FileFilter that filters out all files except
030 * for those type extensions that it knows about.
031 *
032 * @author Peter X. Cao
033 * @version 2.4 9/6/2007
034 */
035public class DefaultFileFilter extends FileFilter {
036    private static FileFilter FILE_FILTER_HDF = null;
037    private static FileFilter FILE_FILTER_HDF4 = null;
038    private static FileFilter FILE_FILTER_HDF5 = null;
039    private static FileFilter FILE_FILTER_JPEG = null;
040    private static FileFilter FILE_FILTER_TIFF = null;
041    private static FileFilter FILE_FILTER_PNG = null;
042    private static FileFilter FILE_FILTER_GIF = null;
043    private static FileFilter FILE_FILTER_BMP = null;
044    private static FileFilter FILE_FILTER_IMG = null;
045    private static FileFilter FILE_FILTER_TEXT = null;
046    private static FileFilter FILE_FILTER_BINARY = null;
047
048    private Hashtable<String, DefaultFileFilter> filters = null;
049    private String description = null;
050    private String fullDescription = null;
051    private boolean useExtensionsInDescription = true;
052
053    /**
054     * Creates a file filter. If no filters are added, then all files are
055     * accepted.
056     *
057     * @see #addExtension
058     */
059    public DefaultFileFilter() {
060        this.filters = new Hashtable<String, DefaultFileFilter>();
061    }
062
063    /**
064     * Creates a file filter that accepts files with the given extension.
065     * Example: new DefaultFileFilter("jpg");
066     *
067     * @see #addExtension
068     *
069     * @param extension the file extension to filter on
070     */
071    public DefaultFileFilter(String extension) {
072        this(extension, null);
073    }
074
075    /**
076     * Creates a file filter that accepts the given file type. Example: new
077     * DefaultFileFilter("jpg", "JPEG Image Images");
078     *
079     * Note that the "." before the extension is not needed. If provided, it
080     * will be ignored.
081     *
082     * @see #addExtension
083     *
084     * @param extension the file extension to filter on
085     * @param description the file extension full description
086     */
087    public DefaultFileFilter(String extension, String description) {
088        this();
089        if (extension != null) {
090            addExtension(extension);
091        }
092        if (description != null) {
093            setDescription(description);
094        }
095    }
096
097    /**
098     * Creates a file filter from the given string array. Example: new
099     * DefaultFileFilter(String {"gif", "jpg"});
100     *
101     * Note that the "." before the extension is not needed and will be ignored.
102     *
103     * @see #addExtension
104     *
105     * @param filters
106     *          the list of filter names
107     */
108    public DefaultFileFilter(String[] filters) {
109        this(filters, null);
110    }
111
112    /**
113     * Creates a file filter from the given string array and description.
114     * Example: new DefaultFileFilter(String {"gif", "jpg"},
115     * "Gif and JPG Images");
116     *
117     * Note that the "." before the extension is not needed and will be ignored.
118     *
119     * @see #addExtension
120     *
121     * @param filters
122     *          the list of filter names
123     * @param description
124     *          the name of the filter list
125     */
126    public DefaultFileFilter(String[] filters, String description) {
127        this();
128        for (int i = 0; i < filters.length; i++) {
129            // add filters one by one
130            addExtension(filters[i]);
131        }
132        if (description != null) {
133            setDescription(description);
134        }
135    }
136
137    /**
138     * Return true if this file should be shown in the directory pane, false if
139     * it shouldn't.
140     *
141     * Files that begin with "." are ignored.
142     *
143     * @see #getExtension
144     */
145    @Override
146    public boolean accept(File f) {
147        if (f != null) {
148            if (f.isDirectory()) {
149                return true;
150            }
151            String extension = getExtension(f);
152            if ((extension != null) && (filters.get(getExtension(f)) != null)) {
153                return true;
154            }
155        }
156
157        return false;
158    }
159
160    /**
161     * Return the extension portion of the file's name.
162     *
163     * @param f The file to get the extension portion of the name for
164     *
165     * @return The extension portion of the given file's name
166     *
167     * @see #getExtension
168     * @see FileFilter#accept
169     */
170    public String getExtension(File f) {
171        if (f != null) {
172            String filename = f.getName();
173            int i = filename.lastIndexOf('.');
174            if ((i > 0) && (i < filename.length() - 1)) {
175                return filename.substring(i + 1).toLowerCase();
176            }
177        }
178        return null;
179    }
180
181    /**
182     * Adds a filetype "dot" extension to filter against.
183     *
184     * For example: the following code will create a filter that filters out all
185     * files except those that end in ".jpg" and ".tif":
186     *
187     * DefaultFileFilter filter = new DefaultFileFilter();
188     * filter.addExtension("jpg"); filter.addExtension("tif"); or
189     * filter.addExtension("jpg, tif");
190     *
191     * Note that the "." before the extension is not needed and will be ignored.
192     *
193     * @param extension the file extension to add to the file filter
194     */
195    public void addExtension(String extension) {
196        if (filters == null) {
197            filters = new Hashtable<String, DefaultFileFilter>(5);
198        }
199
200        String ext = null;
201        StringTokenizer st = new StringTokenizer(extension, ",");
202        while (st.hasMoreElements()) {
203            ext = st.nextToken().trim();
204            filters.put(ext.toLowerCase(), this);
205        }
206        fullDescription = null;
207    }
208
209    /**
210     * @return the human readable description of this filter. For example:
211     * "JPEG and GIF Image Files (*.jpg, *.gif)"
212     */
213    @Override
214    public String getDescription() {
215        if (fullDescription == null) {
216            if ((description == null) || isExtensionListInDescription()) {
217                fullDescription = description == null ? "(" : description
218                        + " (";
219                // build the description from the extension list
220                Enumeration<String> extensions = filters.keys();
221                if (extensions != null) {
222
223                    if (!extensions.hasMoreElements()) {
224                        fullDescription = null;
225                        return null;
226                    }
227
228                    fullDescription += "." + extensions.nextElement();
229                    while (extensions.hasMoreElements()) {
230                        fullDescription += ", "
231                                + extensions.nextElement();
232                    }
233                }
234                fullDescription += ")";
235            }
236            else {
237                fullDescription = description;
238            }
239        }
240        return fullDescription;
241    }
242
243    /**
244     * Sets the human readable description of this filter. For example:
245     * filter.setDescription("Gif and JPG Images");
246     *
247     * @param description the full description of the file filter
248     */
249    public void setDescription(String description) {
250        this.description = description;
251        fullDescription = null;
252    }
253
254    /**
255     * Determines whether the extension list (.jpg, .gif, etc) should show up in
256     * the human readable description.
257     *
258     * Only relevent if a description was provided in the constructor or using
259     * setDescription();
260     *
261     * @param b the show state of the extension list
262     */
263    public void setExtensionListInDescription(boolean b) {
264        useExtensionsInDescription = b;
265        fullDescription = null;
266    }
267
268    /**
269     * @return whether the extension list (.jpg, .gif, etc) should show up in
270     * the human readable description.
271     *
272     * Only relevent if a description was provided in the constructor or using
273     * setDescription();
274     */
275    public boolean isExtensionListInDescription() {
276        return useExtensionsInDescription;
277    }
278
279    /** @return a file filter for HDF4/5 file. */
280    public static FileFilter getFileFilter() {
281        // update extension
282        String fileExtension = ViewProperties.getFileExtension();
283
284        DefaultFileFilter filter = new DefaultFileFilter();
285        filter.setDescription("HDF & more");
286
287        filter.addExtension(fileExtension);
288        FILE_FILTER_HDF = filter;
289
290        return filter;
291    }
292
293    /** @return s file filter for HDF4 file. */
294    public static FileFilter getFileFilterHDF4() {
295        DefaultFileFilter filter = new DefaultFileFilter();
296        filter.addExtension("hdf");
297        filter.addExtension("h4");
298        filter.addExtension("hdf4");
299        filter.setDescription("HDF4 files");
300        FILE_FILTER_HDF4 = filter;
301
302        return filter;
303    }
304
305    /**@return a file filter for HDF5 file. */
306    public static FileFilter getFileFilterHDF5() {
307        DefaultFileFilter filter = new DefaultFileFilter();
308        filter.addExtension("h5");
309        filter.addExtension("hdf5");
310        filter.setDescription("HDF5 files");
311        FILE_FILTER_HDF5 = filter;
312
313        return filter;
314    }
315
316    /** @return a file filter for JPEG image files. */
317    public static FileFilter getFileFilterJPEG() {
318        DefaultFileFilter filter = new DefaultFileFilter();
319        filter.addExtension("jpg");
320        filter.addExtension("jpeg");
321        filter.addExtension("jpe");
322        filter.addExtension("jif");
323        filter.addExtension("jfif");
324        filter.addExtension("jfi");
325        filter.setDescription("JPEG images");
326        FILE_FILTER_JPEG = filter;
327
328        return filter;
329    }
330
331    /** @return a file filter for TIFF image files. */
332    public static FileFilter getFileFilterTIFF() {
333        DefaultFileFilter filter = new DefaultFileFilter();
334        filter.addExtension("tif");
335        filter.addExtension("tiff");
336        filter.setDescription("TIFF images");
337        FILE_FILTER_TIFF = filter;
338
339        return filter;
340    }
341
342    /** @return a file filter for PNG image files. */
343    public static FileFilter getFileFilterPNG() {
344        DefaultFileFilter filter = new DefaultFileFilter();
345        filter.addExtension("png");
346        filter.setDescription("PNG images");
347        FILE_FILTER_PNG = filter;
348
349        return filter;
350    }
351
352    /** @return a file filter for BMP image files. */
353    public static FileFilter getFileFilterBMP() {
354        DefaultFileFilter filter = new DefaultFileFilter();
355        filter.addExtension("bmp");
356        filter.addExtension("dib");
357        filter.setDescription("BMP images");
358        FILE_FILTER_BMP = filter;
359
360        return filter;
361    }
362
363    /** @return a file filter for GIF image files. */
364    public static FileFilter getFileFilterGIF() {
365        DefaultFileFilter filter = new DefaultFileFilter();
366        filter.addExtension("gif");
367        filter.setDescription("GIF images");
368        FILE_FILTER_GIF = filter;
369
370        return filter;
371    }
372
373    /** @return a file filter for GIF, JPEG, BMP, or PNG image files. */
374    public static FileFilter getImageFileFilter() {
375        DefaultFileFilter filter = new DefaultFileFilter();
376        filter.addExtension("jpg");
377        filter.addExtension("jpeg");
378        filter.addExtension("jpe");
379        filter.addExtension("jif");
380        filter.addExtension("jfif");
381        filter.addExtension("jfi");
382        filter.addExtension("png");
383        filter.addExtension("gif");
384        filter.addExtension("bmp");
385        filter.addExtension("dib");
386        filter.setDescription("GIF, JPEG, BMP, or PNG images");
387        FILE_FILTER_IMG = filter;
388
389        return filter;
390    }
391
392    /** @return a file filter for text file. */
393    public static FileFilter getFileFilterText() {
394        DefaultFileFilter filter = new DefaultFileFilter();
395        filter.addExtension("txt");
396        filter.addExtension("text");
397        filter.setDescription("Text");
398        FILE_FILTER_TEXT = filter;
399
400        return filter;
401    }
402
403    /** @return a file filter for binary file. */
404    public static FileFilter getFileFilterBinary() {
405        DefaultFileFilter filter = new DefaultFileFilter();
406        filter.addExtension("bin");
407        filter.setDescription("Binary");
408        FILE_FILTER_BINARY = filter;
409
410        return filter;
411    }
412
413    /**
414     * look at the first 4 bytes of the file to see if it is an HDF4 file.
415     * byte[0]=14, byte[1]=3, byte[2]=19, byte[3]=1 or if it is a netCDF file
416     * byte[0]=67, byte[1]=68, byte[2]=70, byte[3]=1 or
417     *
418     * @param filename The name of the file to check
419     *
420     * @return true if the specified file is an HDF4 file; false otherwise
421     */
422    public static boolean isHDF4(String filename) {
423        boolean ish4 = false;
424        RandomAccessFile raf = null;
425
426        try {
427            raf = new RandomAccessFile(filename, "r");
428        }
429        catch (Exception ex) {
430            raf = null;
431        }
432
433        if (raf == null) {
434            return false;
435        }
436
437        byte[] header = new byte[4];
438        try {
439            raf.read(header);
440        }
441        catch (Exception ex) {
442            header = null;
443        }
444
445        if (header != null) {
446            if (
447            // HDF4
448            ((header[0] == 14) && (header[1] == 3) && (header[2] == 19) && (header[3] == 1))
449            /*
450             * // netCDF || (header[0]==67 && header[1]==68 && header[2]==70 &&
451             * header[3]==1)
452             */
453            ) {
454                ish4 = true;
455            }
456            else {
457                ish4 = false;
458            }
459        }
460
461        try {
462            raf.close();
463        }
464        catch (Exception ex) {
465        }
466
467        return ish4;
468    }
469
470    /**
471     * look at the first 8 bytes of the file to see if it is an HDF5 file.
472     * byte[0]=-199 which is 137 in unsigned byte, byte[1]=72, byte[2]=68,
473     * byte[3]=70, byte[4]=13, byte[5]=10, byte[6]=26, byte[7]=10
474     *
475     * @param filename The name of the file to check
476     *
477     * @return true if the specified file is an HDF5 file; false otherwise
478     */
479    public static boolean isHDF5(String filename) {
480        boolean ish5 = false;
481        RandomAccessFile raf = null;
482
483        try {
484            raf = new RandomAccessFile(filename, "r");
485        }
486        catch (Exception ex) {
487            raf = null;
488        }
489
490        if (raf == null) {
491            return false;
492        }
493
494        byte[] header = new byte[8];
495        long fileSize = 0;
496        try {
497            fileSize = raf.length();
498        }
499        catch (Exception ex) {
500        }
501
502        // The super block is located by searching for the HDF5 file signature
503        // at byte offset 0, byte offset 512 and at successive locations in the
504        // file, each a multiple of two of the previous location, i.e. 0, 512,
505        // 1024, 2048, etc
506        long offset = 0;
507        while (!ish5 && (offset < fileSize)) {
508            try {
509                raf.seek(offset);
510                raf.read(header);
511            }
512            catch (Exception ex) {
513                header = null;
514            }
515
516            if ((header[0] == -119) && (header[1] == 72) && (header[2] == 68)
517                    && (header[3] == 70) && (header[4] == 13)
518                    && (header[5] == 10) && (header[6] == 26)
519                    && (header[7] == 10)) {
520                ish5 = true;
521            }
522            else {
523                ish5 = false;
524                if (offset == 0) {
525                    offset = 512;
526                }
527                else {
528                    offset *= 2;
529                }
530            }
531        }
532
533        try {
534            raf.close();
535        }
536        catch (Exception ex) {
537        }
538
539        return ish5;
540    }
541
542    /**
543     * look at the first 4 bytes of the file to see if it is a netCDF file
544     * byte[0]=67, byte[1]=68, byte[2]=70, byte[3]=1 or
545     *
546     * @param filename The name of the file to check
547     *
548     * @return true if the specified file is a NetCDF file; false otherwise
549     */
550    public static boolean isNetcdf(String filename) {
551        boolean isnc = false;
552        RandomAccessFile raf = null;
553
554        try {
555            raf = new RandomAccessFile(filename, "r");
556        }
557        catch (Exception ex) {
558            raf = null;
559        }
560
561        if (raf == null) {
562            return false;
563        }
564
565        byte[] header = new byte[4];
566        try {
567            raf.read(header);
568        }
569        catch (Exception ex) {
570            header = null;
571        }
572
573        if (header != null) {
574            if (
575            // netCDF
576            (header[0] == 67) && (header[1] == 68) && (header[2] == 70)
577                    && (header[3] == 1)) {
578                isnc = true;
579            }
580            else {
581                isnc = false;
582            }
583        }
584
585        try {
586            raf.close();
587        }
588        catch (Exception ex) {
589        }
590
591        return isnc;
592    }
593
594    /**
595     * Read HDF5 user block data into byte array.
596     *
597     * @param filename The name of the file to read the HDF5 user block from
598     *
599     * @return a byte array of user block, or null if there is user data.
600     */
601    public static byte[] getHDF5UserBlock(String filename) {
602        byte[] userBlock = null;
603        RandomAccessFile raf = null;
604
605        try {
606            raf = new RandomAccessFile(filename, "r");
607        }
608        catch (Exception ex) {
609            try {
610                raf.close();
611            }
612            catch (Throwable err) {
613                ;
614            }
615            raf = null;
616        }
617
618        if (raf == null) {
619            return null;
620        }
621
622        byte[] header = new byte[8];
623        long fileSize = 0;
624        try {
625            fileSize = raf.length();
626        }
627        catch (Exception ex) {
628            fileSize = 0;
629        }
630        if (fileSize <= 0) {
631            try {
632                raf.close();
633            }
634            catch (Throwable err) {
635                ;
636            }
637            return null;
638        }
639
640        // The super block is located by searching for the HDF5 file signature
641        // at byte offset 0, byte offset 512 and at successive locations in the
642        // file, each a multiple of two of the previous location, i.e. 0, 512,
643        // 1024, 2048, etc
644        long offset = 0;
645        boolean ish5 = false;
646        while (offset < fileSize) {
647            try {
648                raf.seek(offset);
649                raf.read(header);
650            }
651            catch (Exception ex) {
652                header = null;
653            }
654
655            if ((header[0] == -119) && (header[1] == 72) && (header[2] == 68)
656                    && (header[3] == 70) && (header[4] == 13)
657                    && (header[5] == 10) && (header[6] == 26)
658                    && (header[7] == 10)) {
659                ish5 = true;
660                break; // find the end of user block
661            }
662            else {
663                ish5 = false;
664                if (offset == 0) {
665                    offset = 512;
666                }
667                else {
668                    offset *= 2;
669                }
670            }
671        }
672
673        if (!ish5 || (offset == 0)) {
674            try {
675                raf.close();
676            }
677            catch (Throwable err) {
678                ;
679            }
680            return null;
681        }
682
683        int blockSize = (int) offset;
684        userBlock = new byte[blockSize];
685        try {
686            raf.seek(0);
687            raf.read(userBlock, 0, blockSize);
688        }
689        catch (Exception ex) {
690            userBlock = null;
691        }
692
693        try {
694            raf.close();
695        }
696        catch (Exception ex) {
697        }
698
699        return userBlock;
700    }
701
702    /**
703     * Write HDF5 user block data into byte array.
704     *
705     * @param fin The name of the file to copy to the output file
706     *
707     * @param fout The name of the file to write HDF5 user block data to
708     *
709     * @param buf The HDF5 user block data to write into the output file
710     *
711     * @return a byte array of user block, or null if there is user data.
712     */
713    public static boolean setHDF5UserBlock(String fin, String fout, byte[] buf) {
714        boolean ish5 = false;
715
716        if ((buf == null) || (buf.length <= 0)) {
717            return false;
718        }
719
720        File tmpFile = new File(fin);
721        if (!tmpFile.exists()) {
722            return false;
723        }
724
725        // find the end of user block for the input file;
726        RandomAccessFile raf = null;
727        try {
728            raf = new RandomAccessFile(fin, "r");
729        }
730        catch (Exception ex) {
731            raf = null;
732        }
733
734        if (raf == null) {
735            return false;
736        }
737
738        byte[] header = new byte[8];
739        long fileSize = 0;
740        try {
741            fileSize = raf.length();
742        }
743        catch (Exception ex) {
744            fileSize = 0;
745        }
746        try {
747            fileSize = raf.length();
748        }
749        catch (Exception ex) {
750            fileSize = 0;
751        }
752        if (fileSize <= 0) {
753            try {
754                raf.close();
755            }
756            catch (Throwable err) {
757                ;
758            }
759            return false;
760        }
761
762        // The super block is located by searching for the HDF5 file signature
763        // at byte offset 0, byte offset 512 and at successive locations in the
764        // file, each a multiple of two of the previous location, i.e. 0, 512,
765        // 1024, 2048, etc
766        long offset = 0;
767        while (offset < fileSize) {
768            try {
769                raf.seek(offset);
770                raf.read(header);
771            }
772            catch (Exception ex) {
773                header = null;
774            }
775
776            if ((header[0] == -119) && (header[1] == 72) && (header[2] == 68)
777                    && (header[3] == 70) && (header[4] == 13)
778                    && (header[5] == 10) && (header[6] == 26)
779                    && (header[7] == 10)) {
780                ish5 = true;
781                break;
782            }
783            else {
784                ish5 = false;
785                if (offset == 0) {
786                    offset = 512;
787                }
788                else {
789                    offset *= 2;
790                }
791            }
792        }
793        try {
794            raf.close();
795        }
796        catch (Throwable err) {
797            ;
798        }
799
800        if (!ish5) {
801            return false;
802        }
803
804        int length = 0;
805        int bsize = 1024;
806        byte[] buffer;
807        BufferedInputStream bi = null;
808        BufferedOutputStream bo = null;
809
810        try {
811            bi = new BufferedInputStream(new FileInputStream(fin));
812        }
813        catch (Exception ex) {
814            try {
815                bi.close();
816            }
817            catch (Exception ex2) {
818            }
819            return false;
820        }
821
822        try {
823            bo = new BufferedOutputStream(new FileOutputStream(fout));
824        }
825        catch (Exception ex) {
826            try {
827                bo.close();
828            }
829            catch (Exception ex2) {
830            }
831            try {
832                bi.close();
833            }
834            catch (Exception ex2) {
835            }
836            return false;
837        }
838
839        // skip the header of original file
840        try {
841            bi.skip(offset);
842        }
843        catch (Exception ex) {
844        }
845
846        // write the header into the new file
847        try {
848            bo.write(buf, 0, buf.length);
849        }
850        catch (Exception ex) {
851        }
852
853        // The super block space is allocated by offset 0, 512, 1024, 2048, etc
854        offset = 512;
855        while (offset < buf.length) {
856            offset *= 2;
857        }
858        int padSize = (int) (offset - buf.length);
859        if (padSize > 0) {
860            byte[] padBuf = new byte[padSize];
861            try {
862                bo.write(padBuf, 0, padSize);
863            }
864            catch (Exception ex) {
865            }
866        }
867
868        // copy the hdf5 file content from input file to the output file
869        buffer = new byte[bsize];
870        try {
871            length = bi.read(buffer, 0, bsize);
872        }
873        catch (Exception ex) {
874            length = 0;
875        }
876        while (length > 0) {
877            try {
878                bo.write(buffer, 0, length);
879                length = bi.read(buffer, 0, bsize);
880            }
881            catch (Exception ex) {
882                length = 0;
883            }
884        }
885
886        try {
887            bo.flush();
888        }
889        catch (Exception ex) {
890        }
891        try {
892            bi.close();
893        }
894        catch (Exception ex) {
895        }
896        try {
897            bo.close();
898        }
899        catch (Exception ex) {
900        }
901        return true;
902    }
903}