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 files COPYING and Copyright.html. *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * Or, see https://support.hdfgroup.org/products/licenses.html               *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.view;
016
017import java.awt.Graphics;
018import java.awt.Image;
019import java.awt.Toolkit;
020import java.awt.image.BufferedImage;
021import java.awt.image.ColorModel;
022import java.awt.image.DataBufferInt;
023import java.awt.image.DirectColorModel;
024import java.awt.image.MemoryImageSource;
025import java.awt.image.PixelGrabber;
026import java.io.BufferedInputStream;
027import java.io.BufferedOutputStream;
028import java.io.BufferedReader;
029import java.io.DataOutputStream;
030import java.io.File;
031import java.io.FileInputStream;
032import java.io.FileOutputStream;
033import java.io.FileReader;
034import java.io.IOException;
035import java.io.RandomAccessFile;
036import java.lang.reflect.Array;
037import java.lang.reflect.Constructor;
038import java.lang.reflect.Method;
039import java.math.BigDecimal;
040import java.math.BigInteger;
041import java.nio.ByteBuffer;
042import java.nio.ByteOrder;
043import java.nio.DoubleBuffer;
044import java.nio.FloatBuffer;
045import java.nio.IntBuffer;
046import java.nio.LongBuffer;
047import java.nio.ShortBuffer;
048import java.util.BitSet;
049import java.util.Iterator;
050import java.util.List;
051import java.util.StringTokenizer;
052
053import javax.imageio.ImageIO;
054
055import org.eclipse.jface.dialogs.MessageDialog;
056import org.eclipse.swt.widgets.Display;
057import org.eclipse.swt.widgets.Shell;
058
059import hdf.object.Datatype;
060import hdf.object.FileFormat;
061import hdf.object.Group;
062import hdf.object.ScalarDS;
063import hdf.view.ViewProperties.BITMASK_OP;
064
065/**
066 * The "Tools" class contains various tools for HDF files such as jpeg to HDF
067 * converter.
068 *
069 * @author Peter X. Cao
070 * @version 2.4 9/6/2007
071 */
072public final class Tools {
073    private static final Display display = Display.getDefault();
074
075    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Tools.class);
076
077    public static final long       MAX_INT8        = 127;
078    public static final long       MAX_UINT8       = 255;
079    public static final long       MAX_INT16       = 32767;
080    public static final long       MAX_UINT16      = 65535;
081    public static final long       MAX_INT32       = 2147483647;
082    public static final long       MAX_UINT32      = 4294967295L;
083    public static final long       MAX_INT64       = 9223372036854775807L;
084    public static final BigInteger MAX_UINT64      = new BigInteger("18446744073709551615");
085
086    private static final int       FLOAT_BUFFER_SIZE = 524288;
087    private static final int       INT_BUFFER_SIZE = 524288;
088    private static final int       SHORT_BUFFER_SIZE = 1048576;
089    private static final int       LONG_BUFFER_SIZE = 262144;
090    private static final int       DOUBLE_BUFFER_SIZE = 262144;
091    private static final int       BYTE_BUFFER_SIZE = 2097152;
092
093    /** Key for JPEG image file type. */
094    public static final String     FILE_TYPE_JPEG  = "JPEG";
095
096    /** Key for TIFF image file type. */
097    public static final String     FILE_TYPE_TIFF  = "TIFF";
098
099    /** Key for PNG image file type. */
100    public static final String     FILE_TYPE_PNG   = "PNG";
101
102    /** Key for GIF image file type. */
103    public static final String     FILE_TYPE_GIF   = "GIF";
104
105    /** Key for BMP image file type. */
106    public static final String FILE_TYPE_BMP = "BMP";
107
108    /** Key for all image file type. */
109    public static final String FILE_TYPE_IMAGE = "IMG";
110
111    /**
112     * Converts unsigned 64-bit integer data to a BigInteger since Java does not
113     * have unsigned types.
114     *
115     * @param l
116     *        The long value to convert to a BigInteger
117     *
118     * @return A BigInteger representing the unsigned value of the given long.
119     */
120    public static BigInteger convertUINT64toBigInt(Long l) {
121        if (l < 0) {
122            l = (l << 1) >>> 1;
123            BigInteger big1 = new BigInteger("9223372036854775808"); // 2^65
124            BigInteger big2 = new BigInteger(l.toString());
125            return big1.add(big2);
126        }
127        else {
128            return new BigInteger(l.toString());
129        }
130    }
131
132    /**
133     * Converts an image file into HDF4/5 file.
134     *
135     * @param imgFileName
136     *            the input image file.
137     * @param hFileName
138     *            the name of the HDF4/5 file.
139     * @param fromType
140     *            the type of image.
141     * @param toType
142     *            the type of file converted to.
143     *
144     * @throws Exception if a failure occurred
145     */
146    public static void convertImageToHDF(String imgFileName, String hFileName, String fromType, String toType)
147            throws Exception {
148        File imgFile = null;
149
150        if (imgFileName == null) {
151            throw new NullPointerException("The source image file is null.");
152        }
153        imgFile = new File(imgFileName);
154        if (!imgFile.exists()) {
155            throw new NullPointerException("The source image file does not exist.");
156        }
157        if (hFileName == null) {
158            throw new NullPointerException("The target HDF file is null.");
159        }
160
161        if (!fromType.equals(FILE_TYPE_IMAGE)) {
162            throw new UnsupportedOperationException("Unsupported image type.");
163        }
164        else if (!(toType.equals(FileFormat.FILE_TYPE_HDF4) || toType.equals(FileFormat.FILE_TYPE_HDF5))) {
165            throw new UnsupportedOperationException("Unsupported destination file type.");
166        }
167
168        BufferedImage image = null;
169        try {
170            BufferedInputStream in = new BufferedInputStream(new FileInputStream(imgFileName));
171            image = ImageIO.read(in);
172            in.close();
173        }
174        catch (Exception err) {
175            image = null;
176        }
177
178        if (image == null) throw new UnsupportedOperationException("Failed to read image: " + imgFileName);
179
180        long h = image.getHeight();
181        long w = image.getWidth();
182        byte[] data = null;
183
184        try {
185            data = new byte[(int)(3 * h * w)];
186        }
187        catch (OutOfMemoryError err) {
188            err.printStackTrace();
189            throw err;
190        }
191
192        int idx = 0;
193        int rgb = 0;
194        for (int i = 0; i < h; i++) {
195            for (int j = 0; j < w; j++) {
196                rgb = image.getRGB(j, i);
197                data[idx++] = (byte) (rgb >> 16);
198                data[idx++] = (byte) (rgb >> 8);
199                data[idx++] = (byte) rgb;
200            }
201        }
202
203        long[] dims = null;
204        Datatype type = null;
205        Group pgroup = null;
206        String imgName = imgFile.getName();
207        FileFormat newfile = null;
208        FileFormat thefile = null;
209        if (toType.equals(FileFormat.FILE_TYPE_HDF5)) {
210            thefile = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5);
211            long[] h5dims = { h, w, 3 }; // RGB pixel interlace
212            dims = h5dims;
213        }
214        else if (toType.equals(FileFormat.FILE_TYPE_HDF4)) {
215            thefile = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4);
216            long[] h4dims = { w, h, 3 }; // RGB pixel interlace
217            dims = h4dims;
218        }
219        else {
220            thefile = null;
221        }
222
223        if (thefile != null) {
224            newfile = thefile.createInstance(hFileName, FileFormat.CREATE);
225            newfile.open();
226            pgroup = (Group) newfile.getRootObject();
227            type = newfile.createDatatype(Datatype.CLASS_CHAR, 1, Datatype.NATIVE, Datatype.SIGN_NONE);
228            newfile.createImage(imgName, pgroup, type, dims, null, null, -1, 3, ScalarDS.INTERLACE_PIXEL, data);
229            newfile.close();
230        }
231
232        Runtime.getRuntime().gc();
233    }
234
235    /**
236     * Save a BufferedImage into an image file.
237     *
238     * @param image
239     *            the BufferedImage to save.
240     * @param file
241     *            the image file.
242     * @param type
243     *            the image type.
244     *
245     * @throws IOException
246     *             if a failure occurred
247     */
248    public static void saveImageAs(BufferedImage image, File file, String type) throws IOException {
249        if (image == null) {
250            throw new NullPointerException("The source image is null.");
251        }
252
253        ImageIO.write(image, type, file);
254    }
255
256    /**
257     * Creates the gray palette of the indexed 256-color table.
258     * <p>
259     * The palette values are stored in a two-dimensional byte array and arrange
260     * by color components of red, green and blue. palette[][] = byte[3][256],
261     * where, palette[0][], palette[1][] and palette[2][] are the red, green and
262     * blue components respectively.
263     *
264     * @return the gray palette in the form of byte[3][256]
265     */
266    public static final byte[][] createGrayPalette() {
267        byte[][] p = new byte[3][256];
268
269        for (int i = 0; i < 256; i++) {
270            p[0][i] = p[1][i] = p[2][i] = (byte) (i);
271        }
272
273        return p;
274    }
275
276    /**
277     * Creates the reverse gray palette of the indexed 256-color table.
278     * <p>
279     * The palette values are stored in a two-dimensional byte array and arrange
280     * by color components of red, green and blue. palette[][] = byte[3][256],
281     * where, palette[0][], palette[1][] and palette[2][] are the red, green and
282     * blue components respectively.
283     *
284     * @return the gray palette in the form of byte[3][256]
285     */
286    public static final byte[][] createReverseGrayPalette() {
287        byte[][] p = new byte[3][256];
288
289        for (int i = 0; i < 256; i++) {
290            p[0][i] = p[1][i] = p[2][i] = (byte) (255 - i);
291        }
292
293        return p;
294    }
295
296    /**
297     * Creates the gray wave palette of the indexed 256-color table.
298     * <p>
299     * The palette values are stored in a two-dimensional byte array and arrange
300     * by color components of red, green and blue. palette[][] = byte[3][256],
301     * where, palette[0][], palette[1][] and palette[2][] are the red, green and
302     * blue components respectively.
303     *
304     * @return the gray palette in the form of byte[3][256]
305     */
306    public static final byte[][] createGrayWavePalette() {
307        byte[][] p = new byte[3][256];
308
309        for (int i = 0; i < 256; i++) {
310            p[0][i] = p[1][i] = p[2][i] = (byte) ((double) 255 / 2 + ((double) 255 / 2) * Math.sin((i - 32) / 20.3));
311        }
312
313        return p;
314    }
315
316    /**
317     * Creates the rainbow palette of the indexed 256-color table.
318     * <p>
319     * The palette values are stored in a two-dimensional byte array and arrange
320     * by color components of red, green and blue. palette[][] = byte[3][256],
321     * where, palette[0][], palette[1][] and palette[2][] are the red, green and
322     * blue components respectively.
323     *
324     * @return the rainbow palette in the form of byte[3][256]
325     */
326    public static final byte[][] createRainbowPalette() {
327        byte r;
328        byte g;
329        byte b;
330        byte[][] p = new byte[3][256];
331
332        for (int i = 1; i < 255; i++) {
333            if (i <= 29) {
334                r = (byte) (129.36 - i * 4.36);
335                g = 0;
336                b = (byte) 255;
337            }
338            else if (i <= 86) {
339                r = 0;
340                g = (byte) (-133.54 + i * 4.52);
341                b = (byte) 255;
342            }
343            else if (i <= 141) {
344                r = 0;
345                g = (byte) 255;
346                b = (byte) (665.83 - i * 4.72);
347            }
348            else if (i <= 199) {
349                r = (byte) (-635.26 + i * 4.47);
350                g = (byte) 255;
351                b = 0;
352            }
353            else {
354                r = (byte) 255;
355                g = (byte) (1166.81 - i * 4.57);
356                b = 0;
357            }
358
359            p[0][i] = r;
360            p[1][i] = g;
361            p[2][i] = b;
362        }
363
364        p[0][0] = p[1][0] = p[2][0] = 0;
365        p[0][255] = p[1][255] = p[2][255] = (byte) 255;
366
367        return p;
368    }
369
370    /**
371     * Creates the nature palette of the indexed 256-color table.
372     * <p>
373     * The palette values are stored in a two-dimensional byte array and arrange
374     * by color components of red, green and blue. palette[][] = byte[3][256],
375     * where, palette[0][], palette[1][] and palette[2][] are the red, green and
376     * blue components respectively.
377     *
378     * @return the nature palette in the form of byte[3][256]
379     */
380    public static final byte[][] createNaturePalette() {
381        byte[][] p = new byte[3][256];
382
383        for (int i = 1; i < 210; i++) {
384            p[0][i] = (byte) ((Math.sin((double) (i - 5) / 16) + 1) * 90);
385            p[1][i] = (byte) ((1 - Math.sin((double) (i - 30) / 12)) * 64 * (1 - (double) i / 255) + 128 - (double) i / 2);
386            p[2][i] = (byte) ((1 - Math.sin((double) (i - 8) / 9)) * 110 + 30);
387        }
388
389        for (int i = 210; i < 255; i++) {
390            p[0][i] = (byte) 80;
391            p[1][i] = (byte) 0;
392            p[2][i] = (byte) 200;
393        }
394
395        p[0][0] = p[1][0] = p[2][0] = 0;
396        p[0][255] = p[1][255] = p[2][255] = (byte) 255;
397
398        return p;
399    }
400
401    /**
402     * Creates the wave palette of the indexed 256-color table.
403     * <p>
404     * The palette values are stored in a two-dimensional byte array and arrange
405     * by color components of red, green and blue. palette[][] = byte[3][256],
406     * where, palette[0][], palette[1][] and palette[2][] are the red, green and
407     * blue components respectively.
408     *
409     * @return the wave palette in the form of byte[3][256]
410     */
411    public static final byte[][] createWavePalette() {
412        byte[][] p = new byte[3][256];
413
414        for (int i = 1; i < 255; i++) {
415            p[0][i] = (byte) ((Math.sin(((double) i / 40 - 3.2)) + 1) * 128);
416            p[1][i] = (byte) ((1 - Math.sin((i / 2.55 - 3.1))) * 70 + 30);
417            p[2][i] = (byte) ((1 - Math.sin(((double) i / 40 - 3.1))) * 128);
418        }
419
420        p[0][0] = p[1][0] = p[2][0] = 0;
421        p[0][255] = p[1][255] = p[2][255] = (byte) 255;
422
423        return p;
424    }
425
426    /**
427     * read an image palette from a file.
428     *
429     * A palette file has format of (value, red, green, blue). The color value
430     * in palette file can be either unsigned char [0..255] or float [0..1].
431     * Float value will be converted to [0..255].
432     *
433     * The color table in file can have any number of entries between 2 to 256.
434     * It will be converted to a color table of 256 entries. Any missing index
435     * will calculated by linear interpolation between the neighboring index
436     * values. For example, index 11 is missing in the following table 10 200 60
437     * 20 12 100 100 60 Index 11 will be calculated based on index 10 and index
438     * 12, i.e. 11 150 80 40
439     *
440     * @param filename
441     *            the name of the palette file.
442     *
443     * @return the wave palette in the form of byte[3][256]
444     */
445    public static final byte[][] readPalette(String filename) {
446        final int COLOR256 = 256;
447        int nentries = 0;
448        int i = 0;
449        int j = 0;
450        int idx = 0;
451        float v = 0;
452        float r = 0;
453        float g = 0;
454        float b = 0;
455        float ratio = 0;
456        float maxV = 0;
457        float minV = 0;
458        float maxColor = 0;
459        float minColor = 0;
460        float[][] tbl = new float[COLOR256][4]; /* value, red, green, blue */
461
462        if (filename == null) return null;
463
464        try (BufferedReader in = new BufferedReader(new FileReader(filename))) {
465            String line = null;
466            do {
467                try {
468                    line = in.readLine();
469                }
470                catch (Exception ex) {
471                    log.debug("input file:", ex);
472                    line = null;
473                }
474
475                if (line == null)
476                    continue;
477
478                StringTokenizer st = new StringTokenizer(line);
479
480                // invalid line
481                if (st.countTokens() != 4) {
482                    continue;
483                }
484
485                try {
486                    v = Float.valueOf(st.nextToken());
487                    r = Float.valueOf(st.nextToken());
488                    g = Float.valueOf(st.nextToken());
489                    b = Float.valueOf(st.nextToken());
490                }
491                catch (NumberFormatException ex) {
492                    log.debug("input file:", ex);
493                    continue;
494                }
495
496                tbl[idx][0] = v;
497                tbl[idx][1] = r;
498                tbl[idx][2] = g;
499                tbl[idx][3] = b;
500
501                if (idx == 0) {
502                    maxV = minV = v;
503                    maxColor = minColor = r;
504                }
505
506                maxV = Math.max(maxV, v);
507                maxColor = Math.max(maxColor, r);
508                maxColor = Math.max(maxColor, g);
509                maxColor = Math.max(maxColor, b);
510
511                minV = Math.min(minV, v);
512                minColor = Math.min(minColor, r);
513                minColor = Math.min(minColor, g);
514                minColor = Math.min(minColor, b);
515
516                idx++;
517                if (idx >= COLOR256)
518                    break; /* only support to 256 colors */
519            } while (line != null);
520        }
521        catch (Exception ex) {
522            log.debug("input file:", ex);
523        }
524
525        nentries = idx;
526        if (nentries <= 1) // must have more than one entries
527            return null;
528
529        // convert color table to byte
530        nentries = idx;
531        if (maxColor <= 1) {
532            ratio = (minColor == maxColor) ? 1.0f : ((COLOR256 - 1.0f) / (maxColor - minColor));
533
534            for (i = 0; i < nentries; i++) {
535                for (j = 1; j < 4; j++)
536                    tbl[i][j] = (tbl[i][j] - minColor) * ratio;
537            }
538        }
539
540        // convert table to 256 entries
541        idx = 0;
542        ratio = (minV == maxV) ? 1.0f : ((COLOR256 - 1.0f) / (maxV - minV));
543
544        int[][] p = new int[3][COLOR256];
545        for (i = 0; i < nentries; i++) {
546            idx = (int) ((tbl[i][0] - minV) * ratio);
547            for (j = 0; j < 3; j++)
548                p[j][idx] = (int) tbl[i][j + 1];
549        }
550
551        /* linear interpolating missing values in the color table */
552        for (i = 1; i < COLOR256; i++) {
553            if ((p[0][i] + p[1][i] + p[2][i]) == 0) {
554                j = i + 1;
555
556                // figure out number of missing points between two given points
557                while (j < COLOR256 && (p[0][j] + p[1][j] + p[2][j]) == 0)
558                    j++;
559
560                if (j >= COLOR256) break; // nothing in the table to interpolating
561
562                float d1 = (p[0][j] - p[0][i - 1]) / (float) (j - i);
563                float d2 = (p[1][j] - p[1][i - 1]) / (float) (j - i);
564                float d3 = (p[2][j] - p[2][i - 1]) / (float) (j - i);
565
566                for (int k = i; k <= j; k++) {
567                    p[0][k] = (int) (p[0][i - 1] + d1 * (k - i + 1));
568                    p[1][k] = (int) (p[1][i - 1] + d2 * (k - i + 1));
569                    p[2][k] = (int) (p[2][i - 1] + d3 * (k - i + 1));
570                }
571                i = j + 1;
572            } // ((p[0][i] + p[1][i] + p[2][i]) == 0)
573        } // (i = 1; i < COLOR256; i++)
574
575        byte[][] pal = new byte[3][COLOR256];
576        for (i = 1; i < COLOR256; i++) {
577            for (j = 0; j < 3; j++)
578                pal[j][i] = (byte) (p[j][i]);
579        }
580
581        return pal;
582    }
583
584    /**
585     * This method returns true if the specified image has transparent pixels.
586     *
587     * @param image
588     *            the image to be check if has alpha.
589     *
590     * @return true if the image has alpha setting.
591     */
592    public static boolean hasAlpha(Image image) {
593        if (image == null) {
594            return false;
595        }
596
597        // If buffered image, the color model is readily available
598        if (image instanceof BufferedImage) {
599            BufferedImage bimage = (BufferedImage) image;
600            return bimage.getColorModel().hasAlpha();
601        }
602
603        // Use a pixel grabber to retrieve the image's color model
604        // grabbing a single pixel is usually sufficient
605        PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);
606        try {
607            pg.grabPixels();
608        }
609        catch (InterruptedException e) {
610            log.debug("transparent pixels:", e);
611        }
612        ColorModel cm = pg.getColorModel();
613
614        return cm.hasAlpha();
615    }
616
617    /**
618     * Creates a RGB indexed image of 256 colors.
619     *
620     * @param bufferedImage
621     *            the target image.
622     * @param imageData
623     *            the byte array of the image data.
624     * @param palette
625     *            the color lookup table.
626     * @param w
627     *            the width of the image.
628     * @param h
629     *            the height of the image.
630     *
631     * @return the image.
632     */
633    public static Image createIndexedImage(BufferedImage bufferedImage, byte[] imageData, byte[][] palette, long w, long h)
634    {
635        if (imageData==null || w<=0 || h<=0)
636            return null;
637
638        if (palette==null)
639            palette = Tools.createGrayPalette();
640
641        if (bufferedImage == null)
642            bufferedImage = new BufferedImage((int)w, (int)h, BufferedImage.TYPE_INT_ARGB);
643
644        final int[] pixels = ( (DataBufferInt) bufferedImage.getRaster().getDataBuffer() ).getData();
645        int len = pixels.length;
646
647        for (int i=0; i<len; i++) {
648            int idx = imageData[i] & 0xff;
649            int r = (palette[0][idx] & 0xff)<<16;
650            int g = (palette[1][idx] & 0xff)<<8;
651            int b = palette[2][idx] & 0xff;
652
653            pixels[i] = 0xff000000 | r | g | b;
654        }
655
656        return bufferedImage;
657    }
658
659    /**
660     * Creates a true color image.
661     * <p>
662     * DirectColorModel is used to construct the image from raw data. The DirectColorModel model is
663     * similar to an X11 TrueColor visual, which has the following parameters: <br>
664     *
665     * <pre>
666     * Number of bits:        32
667     *             Red mask:              0x00ff0000
668     *             Green mask:            0x0000ff00
669     *             Blue mask:             0x000000ff
670     *             Alpha mask:            0xff000000
671     *             Color space:           sRGB
672     *             isAlphaPremultiplied:  False
673     *             Transparency:          Transparency.TRANSLUCENT
674     *             transferType:          DataBuffer.TYPE_INT
675     * </pre>
676     * <p>
677     * The data may be arranged in one of two ways: by pixel or by plane. In both cases, the dataset
678     * will have a dataspace with three dimensions, height, width, and components.
679     * <p>
680     * For HDF4, the interlace modes specify orders for the dimensions as:
681     *
682     * <pre>
683     * INTERLACE_PIXEL = [width][height][pixel components]
684     *            INTERLACE_PLANE = [pixel components][width][height]
685     * </pre>
686     * <p>
687     * For HDF5, the interlace modes specify orders for the dimensions as:
688     *
689     * <pre>
690     * INTERLACE_PIXEL = [height][width][pixel components]
691     *            INTERLACE_PLANE = [pixel components][height][width]
692     * </pre>
693     *
694     * @param imageData
695     *            the byte array of the image data.
696     * @param planeInterlace
697     *            flag if the image is plane interlace.
698     * @param w
699     *            the width of the image.
700     * @param h
701     *            the height of the image.
702     *
703     * @return the image.
704     */
705    public static Image createTrueColorImage(byte[] imageData, boolean planeInterlace, long w, long h) {
706        Image theImage = null;
707        long imgSize = w * h;
708        int[] packedImageData = new int[(int) imgSize];
709        int pixel = 0;
710        int idx = 0;
711        int r = 0;
712        int g = 0;
713        int b = 0;
714        for (int i = 0; i < h; i++) {
715            for (int j = 0; j < w; j++) {
716                if (planeInterlace) {
717                    r = imageData[idx];
718                    g = imageData[(int)imgSize + idx];
719                    b = imageData[(int)imgSize * 2 + idx];
720                }
721                else {
722                    r = imageData[idx * 3];
723                    g = imageData[idx * 3 + 1];
724                    b = imageData[idx * 3 + 2];
725                }
726
727                r = (r << 16) & 0x00ff0000;
728                g = (g << 8) & 0x0000ff00;
729                b = b & 0x000000ff;
730
731                // bits packed into alpha (1), red (r), green (g) and blue (b)
732                // as 11111111rrrrrrrrggggggggbbbbbbbb
733                pixel = 0xff000000 | r | g | b;
734                packedImageData[idx++] = pixel;
735            } // (int j=0; j<w; j++)
736        } // (int i=0; i<h; i++)
737
738        DirectColorModel dcm = (DirectColorModel) ColorModel.getRGBdefault();
739        theImage = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource((int)w, (int)h, dcm, packedImageData, 0, (int)w));
740
741        return theImage;
742    }
743
744    /**
745     * This method returns a buffered image with the contents of an image.
746     *
747     * @param image
748     *            the plain image object.
749     *
750     * @return buffered image for the given image.
751     */
752    public static BufferedImage toBufferedImage(Image image) {
753        if (image == null) {
754            return null;
755        }
756
757        if (image instanceof BufferedImage) {
758            return (BufferedImage) image;
759        }
760
761        // !!!!!!!!!!!!!!!!!! NOTICE !!!!!!!!!!!!!!!!!!!!!
762        // the following way of creating a buffered image is using
763        // Component.createImage(). This method can be used only if the
764        // component is visible on the screen. Also, this method returns
765        // buffered images that do not support transparent pixels.
766        // The buffered image created by this way works for package
767        // com.sun.image.codec.jpeg.*
768        // It does not work well with JavaTM Advanced Imaging
769        // com.sun.media.jai.codec.*
770        // if the screen setting is less than 32-bit color
771        int w = image.getWidth(null);
772        int h = image.getHeight(null);
773        BufferedImage bimage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
774        Graphics g = bimage.createGraphics();
775        g.drawImage(image, 0, 0, null);
776
777        g.dispose();
778        return bimage;
779    }
780
781    /**
782     * Convert an array of raw data into array of a byte data.
783     *
784     * @param rawData
785     *            The input raw data.
786     * @param minmax
787     *            the range of the raw data.
788     * @param w
789     *            the width of the raw data.
790     * @param h
791     *            the height of the raw data.
792     * @param isTransposed
793     *            if the data is transposed.
794     * @param byteData
795     *            the data in.
796     *
797     * @return the byte array of pixel data.
798     */
799    public static byte[] getBytes(Object rawData, double[] minmax, long w, long h, boolean isTransposed, byte[] byteData) {
800        return Tools.getBytes(rawData, minmax, w, h, isTransposed, null, false, byteData);
801    }
802
803    public static byte[] getBytes(Object rawData, double[] minmax, long w, long h, boolean isTransposed,
804            List<Number> invalidValues, byte[] byteData) {
805        return getBytes(rawData, minmax, w, h, isTransposed, invalidValues, false, byteData);
806    }
807
808    public static byte[] getBytes(Object rawData, double[] minmax, long w, long h, boolean isTransposed,
809            List<Number> invalidValues, boolean convertByteData, byte[] byteData) {
810        return getBytes(rawData, minmax, w, h, isTransposed,invalidValues, convertByteData, byteData, null);
811    }
812
813    /**
814     * Convert an array of raw data into array of a byte data.
815     *
816     * @param rawData
817     *            The input raw data.
818     * @param minmax
819     *            the range of the raw data.
820     * @param w
821     *            the width of the raw data.
822     * @param h
823     *            the height of the raw data.
824     * @param isTransposed
825     *            if the data is transposed.
826     * @param invalidValues
827     *            the list of invalid values.
828     * @param convertByteData
829     *            the converted data out.
830     * @param byteData
831     *            the data in.
832     * @param list
833     *            the list of integers.
834     *
835     * @return the byte array of pixel data.
836     */
837    public static byte[] getBytes(Object rawData, double[] minmax, long w, long h, boolean isTransposed,
838            List<Number> invalidValues, boolean convertByteData, byte[] byteData, List<Integer> list)
839    {
840        double[] fillValue = null;
841
842        // no input data
843        if (rawData == null || w<=0 || h<=0) {
844            return null;
845        }
846
847        // input data is not an array
848        if (!rawData.getClass().isArray()) {
849            return null;
850        }
851
852        String cname = rawData.getClass().getName();
853        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
854        int size = Array.getLength(rawData);
855
856        if (minmax == null) {
857            minmax = new double[2];
858            minmax[0] = minmax[1] = 0;
859        }
860
861        if (dname == 'B') {
862            return convertByteData((byte[]) rawData, minmax, w, h, isTransposed, fillValue, convertByteData, byteData, list);
863        }
864
865        if ((byteData == null) || (size != byteData.length)) {
866            byteData = new byte[size]; // reuse the old buffer
867        }
868
869        if (minmax[0] == minmax[1]) {
870            Tools.findMinMax(rawData, minmax, fillValue);
871        }
872
873        double min = minmax[0];
874        double max = minmax[1];
875
876        if (invalidValues != null && !invalidValues.isEmpty()) {
877            int n = invalidValues.size();
878            fillValue = new double[n];
879            for (int i=0; i<n; i++) {
880                fillValue[i] = invalidValues.get(i).doubleValue();
881            }
882        }
883        double ratio = (min == max) ? 1.00d : (double) (255.00 / (max - min));
884        long idxSrc = 0;
885        long idxDst = 0;
886        switch (dname) {
887            case 'S':
888                short[] s = (short[]) rawData;
889                for (long i = 0; i < h; i++) {
890                    for (long j = 0; j < w; j++) {
891                        idxSrc = idxDst =j * h + i;
892                        if (isTransposed) idxDst = i * w + j;
893                        byteData[(int)idxDst] = toByte(s[(int)idxSrc], ratio, min, max, fillValue, (int)idxSrc, list);
894                    }
895                }
896                break;
897
898            case 'I':
899                int[] ia = (int[]) rawData;
900                for (long i = 0; i < h; i++) {
901                    for (long j = 0; j < w; j++) {
902                        idxSrc = idxDst = (j * h + i);
903                        if (isTransposed) idxDst = i * w + j;
904                        byteData[(int)idxDst] = toByte(ia[(int)idxSrc], ratio, min, max, fillValue, (int)idxSrc, list);
905                    }
906                }
907                break;
908
909            case 'J':
910                long[] l = (long[]) rawData;
911                for (long i = 0; i < h; i++) {
912                    for (long j = 0; j < w; j++) {
913                        idxSrc = idxDst =j * h + i;
914                        if (isTransposed) idxDst = i * w + j;
915                        byteData[(int)idxDst] = toByte(l[(int)idxSrc], ratio, min, max, fillValue, (int)idxSrc, list);
916                    }
917                }
918                break;
919
920            case 'F':
921                float[] f = (float[]) rawData;
922                for (long i = 0; i < h; i++) {
923                    for (long j = 0; j < w; j++) {
924                        idxSrc = idxDst =j * h + i;
925                        if (isTransposed) idxDst = i * w + j;
926                        byteData[(int)idxDst] = toByte(f[(int)idxSrc], ratio, min, max, fillValue, (int)idxSrc, list);
927                    }
928                }
929                break;
930
931            case 'D':
932                double[] d = (double[]) rawData;
933                for (long i = 0; i < h; i++) {
934                    for (long j = 0; j < w; j++) {
935                        idxSrc = idxDst =j * h + i;
936                        if (isTransposed) idxDst = i * w + j;
937                        byteData[(int)idxDst] = toByte(d[(int)idxSrc], ratio, min, max, fillValue, (int)idxSrc, list);
938                    }
939                }
940                break;
941
942            default:
943                byteData = null;
944                break;
945        } // (dname)
946
947        return byteData;
948    }
949
950    private static byte toByte(double in, double ratio, double min, double max, double[] fill, int idx,  List<Integer> list)
951    {
952        byte out = 0;
953
954        if (in < min || in > max || isFillValue(in, fill) || isNaNINF(in)) {
955            out = 0;
956            if (list!=null)
957                list.add(idx);
958        }
959        else
960            out = (byte) ((in-min)*ratio);
961
962        return out;
963    }
964
965    private static boolean isFillValue(double in, double[] fill) {
966
967        if (fill==null)
968            return false;
969
970        for (int i=0; i<fill.length; i++) {
971            if (fill[i] == in)
972                return true;
973        }
974
975        return false;
976    }
977
978    private static byte[] convertByteData(byte[] rawData, double[] minmax, long w, long h, boolean isTransposed,
979            Object fillValue, boolean convertByteData, byte[] byteData, List<Integer> list) {
980        if (rawData == null) return null;
981
982        if (convertByteData) {
983            if (minmax[0] == minmax[1]) {
984                Tools.findMinMax(rawData, minmax, fillValue);
985            }
986        }
987
988        if (minmax[0] == 0 && minmax[1] == 255) convertByteData = false; // no need to convert data
989
990        // no conversion and no transpose
991        if (!convertByteData && !isTransposed) {
992            if (byteData != null && byteData.length == rawData.length) {
993                System.arraycopy(rawData, 0, byteData, 0, rawData.length);
994                return byteData;
995            }
996
997            return rawData;
998        }
999
1000        // don't want to change the original raw data
1001        if (byteData == null || rawData == byteData) byteData = new byte[rawData.length];
1002
1003        if (!convertByteData) {
1004            // do not convert data, just transpose the data
1005            minmax[0] = 0;
1006            minmax[1] = 255;
1007            if (isTransposed) {
1008                for (long i = 0; i < h; i++) {
1009                    for (long j = 0; j < w; j++) {
1010                        byteData[(int)(i * w + j)] = rawData[(int)(j * h + i)];
1011                    }
1012                }
1013            }
1014            return byteData;
1015        }
1016
1017        // special data range used, must convert the data
1018        double min = minmax[0];
1019        double max = minmax[1];
1020        double ratio = (min == max) ? 1.00d : (double) (255.00 / (max - min));
1021        long idxSrc = 0;
1022        long idxDst = 0;
1023        for (long i = 0; i < h; i++) {
1024            for (long j = 0; j < w; j++) {
1025                idxSrc = idxDst =j * h + i;
1026                if (isTransposed) idxDst = i * w + j;
1027
1028                if (rawData[(int) idxSrc] > max || rawData[(int) idxSrc] < min) {
1029                    byteData[(int)idxDst] = (byte) 0;
1030                    if (list!=null)
1031                        list.add((int)idxSrc);
1032                }
1033                else
1034                    byteData[(int)idxDst] = (byte) ((rawData[(int)idxSrc] - min) * ratio);
1035            }
1036        }
1037
1038        return byteData;
1039    }
1040
1041    /**
1042     * Create and initialize a new instance of the given class.
1043     *
1044     * @param cls
1045     *           the class of the instance
1046     * @param initargs
1047     *            array of objects to be passed as arguments.
1048     *
1049     * @return a new instance of the given class.
1050     *
1051     * @throws Exception if a failure occurred
1052     */
1053    public static Object newInstance(Class<?> cls, Object[] initargs) throws Exception {
1054        log.trace("newInstance(Class = {}): start", cls);
1055
1056        if (cls == null) {
1057            return null;
1058        }
1059
1060        Object instance = null;
1061
1062        if ((initargs == null) || (initargs.length == 0)) {
1063            instance = cls.getDeclaredConstructor().newInstance();
1064        }
1065        else {
1066            Constructor<?>[] constructors = cls.getConstructors();
1067            if ((constructors == null) || (constructors.length == 0)) {
1068                return null;
1069            }
1070
1071            boolean isConstructorMatched = false;
1072            Constructor<?> constructor = null;
1073            Class<?>[] params = null;
1074            int m = constructors.length;
1075            int n = initargs.length;
1076            for (int i = 0; i < m; i++) {
1077                constructor = constructors[i];
1078                params = constructor.getParameterTypes();
1079                if (params.length == n) {
1080                    // check if all the parameters are matched
1081                    isConstructorMatched = params[0].isInstance(initargs[0]);
1082                    for (int j = 0; j < n; j++) {
1083                        isConstructorMatched = isConstructorMatched && params[j].isInstance(initargs[j]);
1084                    }
1085
1086                    if (isConstructorMatched) {
1087                        try {
1088                            instance = constructor.newInstance(initargs);
1089                        }
1090                        catch (Exception ex) {
1091                            log.debug("Error creating instance of {}: ", cls, ex);
1092                            ex.printStackTrace();
1093                        }
1094                        break;
1095                    }
1096                }
1097            } // (int i=0; i<m; i++)
1098        }
1099
1100        return instance;
1101    }
1102
1103    /**
1104     * Computes autocontrast parameters (gain equates to contrast and bias
1105     * equates to brightness) for integers.
1106     * <p>
1107     * The computation is based on the following scaling
1108     *
1109     * <pre>
1110     *      int_8       [0, 127]
1111     *      uint_8      [0, 255]
1112     *      int_16      [0, 32767]
1113     *      uint_16     [0, 65535]
1114     *      int_32      [0, 2147483647]
1115     *      uint_32     [0, 4294967295]
1116     *      int_64      [0, 9223372036854775807]
1117     *      uint_64     [0, 18446744073709551615] // Not supported.
1118     * </pre>
1119     *
1120     * @param data
1121     *            the raw data array of signed/unsigned integers
1122     * @param params
1123     *            the auto gain parameter. params[0]=gain, params[1]=bias,
1124     * @param isUnsigned
1125     *            the flag to indicate if the data array is unsigned integer.
1126     *
1127     * @return non-negative if successful; otherwise, returns negative
1128     */
1129    public static int autoContrastCompute(Object data, double[] params, boolean isUnsigned) {
1130        int retval = 1;
1131        long maxDataValue = 255;
1132        double[] minmax = new double[2];
1133
1134        // check parameters
1135        if ((data == null) || (params == null) || (Array.getLength(data) <= 0) || (params.length < 2)) {
1136            return -1;
1137        }
1138
1139        retval = autoContrastComputeMinMax(data, minmax);
1140
1141        // force the min_max method so we can look at the target grids data sets
1142        if ((retval < 0) || (minmax[1] - minmax[0] < 10)) {
1143            retval = findMinMax(data, minmax, null);
1144        }
1145
1146        if (retval < 0) {
1147            return -1;
1148        }
1149
1150        String cname = data.getClass().getName();
1151        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1152        switch (dname) {
1153            case 'B':
1154                maxDataValue = MAX_INT8;
1155                break;
1156            case 'S':
1157                maxDataValue = MAX_INT16;
1158                if (isUnsigned) {
1159                    maxDataValue = MAX_UINT8; // data was upgraded from unsigned byte
1160                }
1161                break;
1162            case 'I':
1163                maxDataValue = MAX_INT32;
1164                if (isUnsigned) {
1165                    maxDataValue = MAX_UINT16; // data was upgraded from unsigned short
1166                }
1167                break;
1168            case 'J':
1169                maxDataValue = MAX_INT64;
1170                if (isUnsigned) {
1171                    maxDataValue = MAX_UINT32; // data was upgraded from unsigned int
1172                }
1173                break;
1174            default:
1175                retval = -1;
1176                break;
1177        } // (dname)
1178
1179        if (minmax[0] == minmax[1]) {
1180            params[0] = 1.0;
1181            params[1] = 0.0;
1182        }
1183        else {
1184            // This histogram method has a tendency to stretch the
1185            // range of values to be a bit too big, so we can
1186            // account for this by adding and subtracting some percent
1187            // of the difference to the max/min values
1188            // to prevent the gain from going too high.
1189            double diff = minmax[1] - minmax[0];
1190            double newmax = (minmax[1] + (diff * 0.1));
1191            double newmin = (minmax[0] - (diff * 0.1));
1192
1193            if (newmax <= maxDataValue) {
1194                minmax[1] = newmax;
1195            }
1196
1197            if (newmin >= 0) {
1198                minmax[0] = newmin;
1199            }
1200
1201            params[0] = maxDataValue / (minmax[1] - minmax[0]);
1202            params[1] = -minmax[0];
1203        }
1204
1205        return retval;
1206    }
1207
1208    /**
1209     * Apply autocontrast parameters to the original data in place (destructive)
1210     *
1211     * @param dataIN
1212     *            the original data array of signed/unsigned integers
1213     * @param dataOUT
1214     *            the converted data array of signed/unsigned integers
1215     * @param params
1216     *            the auto gain parameter. params[0]=gain, params[1]=bias
1217     * @param minmax
1218     *            the data range. minmax[0]=min, minmax[1]=max
1219     * @param isUnsigned
1220     *            the flag to indicate if the data array is unsigned integer
1221     *
1222     * @return the data array with the auto contrast conversion; otherwise,
1223     *         returns null
1224     */
1225    public static Object autoContrastApply(Object dataIN, Object dataOUT, double[] params, double[] minmax,
1226            boolean isUnsigned) {
1227        int size = 0;
1228        double min = -MAX_INT64;
1229        double max = MAX_INT64;
1230
1231        if ((dataIN == null) || (params == null) || (params.length < 2)) {
1232            return null;
1233        }
1234
1235        if (minmax != null) {
1236            min = minmax[0];
1237            max = minmax[1];
1238        }
1239        // input and output array must be the same size
1240        size = Array.getLength(dataIN);
1241        if ((dataOUT != null) && (size != Array.getLength(dataOUT))) {
1242            return null;
1243        }
1244
1245        double gain = params[0];
1246        double bias = params[1];
1247        double valueOut;
1248        double valueIn;
1249        String cname = dataIN.getClass().getName();
1250        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1251
1252        switch (dname) {
1253            case 'B':
1254                byte[] bIn = (byte[]) dataIN;
1255                if (dataOUT == null) {
1256                    dataOUT = new byte[size];
1257                }
1258                byte[] bOut = (byte[]) dataOUT;
1259                byte bMax = (byte) MAX_INT8;
1260
1261                for (int i = 0; i < size; i++) {
1262                    valueIn = Math.max(bIn[i], min);
1263                    valueIn = Math.min(valueIn, max);
1264                    valueOut = (valueIn + bias) * gain;
1265                    valueOut = Math.max(valueOut, 0.0);
1266                    valueOut = Math.min(valueOut, bMax);
1267                    bOut[i] = (byte) valueOut;
1268                }
1269                break;
1270            case 'S':
1271                short[] sIn = (short[]) dataIN;
1272                if (dataOUT == null) {
1273                    dataOUT = new short[size];
1274                }
1275                short[] sOut = (short[]) dataOUT;
1276                short sMax = (short) MAX_INT16;
1277
1278                if (isUnsigned) {
1279                    sMax = (short) MAX_UINT8; // data was upgraded from unsigned byte
1280                }
1281
1282                for (int i = 0; i < size; i++) {
1283                    valueIn = Math.max(sIn[i], min);
1284                    valueIn = Math.min(valueIn, max);
1285                    valueOut = (valueIn + bias) * gain;
1286                    valueOut = Math.max(valueOut, 0.0);
1287                    valueOut = Math.min(valueOut, sMax);
1288                    sOut[i] = (byte) valueOut;
1289                }
1290                break;
1291            case 'I':
1292                int[] iIn = (int[]) dataIN;
1293                if (dataOUT == null) {
1294                    dataOUT = new int[size];
1295                }
1296                int[] iOut = (int[]) dataOUT;
1297                int iMax = (int) MAX_INT32;
1298                if (isUnsigned) {
1299                    iMax = (int) MAX_UINT16; // data was upgraded from unsigned short
1300                }
1301
1302                for (int i = 0; i < size; i++) {
1303                    valueIn = Math.max(iIn[i], min);
1304                    valueIn = Math.min(valueIn, max);
1305                    valueOut = (valueIn + bias) * gain;
1306                    valueOut = Math.max(valueOut, 0.0);
1307                    valueOut = Math.min(valueOut, iMax);
1308                    iOut[i] = (byte) valueOut;
1309                }
1310                break;
1311            case 'J':
1312                long[] lIn = (long[]) dataIN;
1313                if (dataOUT == null) {
1314                    dataOUT = new long[size];
1315                }
1316                long[] lOut = (long[]) dataOUT;
1317                long lMax = MAX_INT64;
1318                if (isUnsigned) {
1319                    lMax = MAX_UINT32; // data was upgraded from unsigned int
1320                }
1321
1322                for (int i = 0; i < size; i++) {
1323                    valueIn = Math.max(lIn[i], min);
1324                    valueIn = Math.min(valueIn, max);
1325                    valueOut = (valueIn + bias) * gain;
1326                    valueOut = Math.max(valueOut, 0.0);
1327                    valueOut = Math.min(valueOut, lMax);
1328                    lOut[i] = (byte) valueOut;
1329                }
1330                break;
1331            default:
1332                break;
1333        } // (dname)
1334
1335        return dataOUT;
1336    }
1337
1338    /**
1339     * Converts image raw data to bytes.
1340     *
1341     * The integer data is converted to byte data based on the following rule
1342     *
1343     * <pre>
1344     *         uint_8       x
1345     *         int_8       (x &amp; 0x7F) &lt;&lt; 1
1346     *         uint_16     (x &gt;&gt; 8) &amp; 0xFF
1347     *         int_16      (x &gt;&gt; 7) &amp; 0xFF
1348     *         uint_32     (x &gt;&gt; 24) &amp; 0xFF
1349     *         int_32      (x &gt;&gt; 23) &amp; 0xFF
1350     *         uint_64     (x &gt;&gt; 56) &amp; 0xFF
1351     *         int_64      (x &gt;&gt; 55) &amp; 0xFF
1352     * </pre>
1353     *
1354     * @param src
1355     *            the source data array of signed integers or unsigned shorts
1356     * @param dst
1357     *            the destination data array of bytes
1358     * @param isUnsigned
1359     *            the flag to indicate if the data array is unsigned integer.
1360     *
1361     * @return non-negative if successful; otherwise, returns negative
1362     */
1363    public static int autoContrastConvertImageBuffer(Object src, byte[] dst, boolean isUnsigned) {
1364        int retval = 0;
1365
1366        if ((src == null) || (dst == null) || (dst.length != Array.getLength(src))) {
1367            return -1;
1368        }
1369
1370        int size = dst.length;
1371        String cname = src.getClass().getName();
1372        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1373        switch (dname) {
1374            case 'B':
1375                byte[] bSrc = (byte[]) src;
1376                if (isUnsigned) {
1377                    for (int i = 0; i < size; i++) {
1378                        dst[i] = bSrc[i];
1379                    }
1380                }
1381                else {
1382                    for (int i = 0; i < size; i++) {
1383                        dst[i] = (byte) ((bSrc[i] & 0x7F) << 1);
1384                    }
1385                }
1386                break;
1387            case 'S':
1388                short[] sSrc = (short[]) src;
1389                if (isUnsigned) { // data was upgraded from unsigned byte
1390                    for (int i = 0; i < size; i++) {
1391                        dst[i] = (byte) sSrc[i];
1392                    }
1393                }
1394                else {
1395                    for (int i = 0; i < size; i++) {
1396                        dst[i] = (byte) ((sSrc[i] >> 7) & 0xFF);
1397                    }
1398                }
1399                break;
1400            case 'I':
1401                int[] iSrc = (int[]) src;
1402                if (isUnsigned) { // data was upgraded from unsigned short
1403                    for (int i = 0; i < size; i++) {
1404                        dst[i] = (byte) ((iSrc[i] >> 8) & 0xFF);
1405                    }
1406                }
1407                else {
1408                    for (int i = 0; i < size; i++) {
1409                        dst[i] = (byte) ((iSrc[i] >> 23) & 0xFF);
1410                    }
1411                }
1412                break;
1413            case 'J':
1414                long[] lSrc = (long[]) src;
1415                if (isUnsigned) { // data was upgraded from unsigned int
1416                    for (int i = 0; i < size; i++) {
1417                        dst[i] = (byte) ((lSrc[i] >> 24) & 0xFF);
1418                    }
1419                }
1420                else {
1421                    for (int i = 0; i < size; i++) {
1422                        dst[i] = (byte) ((lSrc[i] >> 55) & 0xFF);
1423                    }
1424                }
1425                break;
1426            default:
1427                retval = -1;
1428                break;
1429        } // (dname)
1430
1431        return retval;
1432    }
1433
1434    /**
1435     * Computes autocontrast parameters by
1436     *
1437     * <pre>
1438     *    min = mean - 3 * std.dev
1439     *    max = mean + 3 * std.dev
1440     * </pre>
1441     *
1442     * @param data
1443     *            the raw data array
1444     * @param minmax
1445     *            the min and max values.
1446     *
1447     * @return non-negative if successful; otherwise, returns negative
1448     */
1449    public static int autoContrastComputeMinMax(Object data, double[] minmax) {
1450        int retval = 1;
1451
1452        if ((data == null) || (minmax == null) || (Array.getLength(data) <= 0) || (Array.getLength(minmax) < 2)) {
1453            return -1;
1454        }
1455
1456        double[] avgstd = { 0, 0 };
1457        retval = computeStatistics(data, avgstd, null);
1458        if (retval < 0) {
1459            return retval;
1460        }
1461
1462        minmax[0] = avgstd[0] - 3.0 * avgstd[1];
1463        minmax[1] = avgstd[0] + 3.0 * avgstd[1];
1464
1465        return retval;
1466    }
1467
1468    /**
1469     * Finds the min and max values of the data array
1470     *
1471     * @param data
1472     *            the raw data array
1473     * @param minmax
1474     *            the mmin and max values of the array.
1475     * @param fillValue
1476     *            the missing value or fill value. Exclude this value when check
1477     *            for min/max
1478     *
1479     * @return non-negative if successful; otherwise, returns negative
1480     */
1481    public static int findMinMax(Object data, double[] minmax, Object fillValue) {
1482        int retval = 1;
1483
1484        if ((data == null) || (minmax == null) || (Array.getLength(data) <= 0) || (Array.getLength(minmax) < 2)) {
1485            return -1;
1486        }
1487
1488        int n = Array.getLength(data);
1489        double fill = 0.0;
1490        boolean hasFillValue = (fillValue != null && fillValue.getClass().isArray());
1491
1492        String cname = data.getClass().getName();
1493        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1494        log.trace("findMinMax() cname={} : dname={}", cname, dname);
1495
1496        minmax[0] = Float.MAX_VALUE;
1497        minmax[1] = -Float.MAX_VALUE;
1498
1499        switch (dname) {
1500            case 'B':
1501                byte[] b = (byte[]) data;
1502                minmax[0] = minmax[1] = b[0];
1503
1504                if (hasFillValue) fill = ((byte[]) fillValue)[0];
1505                for (int i = 0; i < n; i++) {
1506                    if (hasFillValue && b[i] == fill) continue;
1507                    if (minmax[0] > b[i]) {
1508                        minmax[0] = b[i];
1509                    }
1510                    if (minmax[1] < b[i]) {
1511                        minmax[1] = b[i];
1512                    }
1513                }
1514                break;
1515            case 'S':
1516                short[] s = (short[]) data;
1517                minmax[0] = minmax[1] = s[0];
1518
1519                if (hasFillValue) fill = ((short[]) fillValue)[0];
1520
1521                for (int i = 0; i < n; i++) {
1522                    if (hasFillValue && s[i] == fill) continue;
1523                    if (minmax[0] > s[i]) {
1524                        minmax[0] = s[i];
1525                    }
1526                    if (minmax[1] < s[i]) {
1527                        minmax[1] = s[i];
1528                    }
1529                }
1530                break;
1531            case 'I':
1532                int[] ia = (int[]) data;
1533                minmax[0] = minmax[1] = ia[0];
1534
1535                if (hasFillValue) fill = ((int[]) fillValue)[0];
1536
1537                for (int i = 0; i < n; i++) {
1538                    if (hasFillValue && ia[i] == fill) continue;
1539                    if (minmax[0] > ia[i]) {
1540                        minmax[0] = ia[i];
1541                    }
1542                    if (minmax[1] < ia[i]) {
1543                        minmax[1] = ia[i];
1544                    }
1545                }
1546                break;
1547            case 'J':
1548                long[] l = (long[]) data;
1549                minmax[0] = minmax[1] = l[0];
1550
1551                if (hasFillValue) fill = ((long[]) fillValue)[0];
1552                for (int i = 0; i < n; i++) {
1553                    if (hasFillValue && l[i] == fill) continue;
1554                    if (minmax[0] > l[i]) {
1555                        minmax[0] = l[i];
1556                    }
1557                    if (minmax[1] < l[i]) {
1558                        minmax[1] = l[i];
1559                    }
1560                }
1561                break;
1562            case 'F':
1563                float[] f = (float[]) data;
1564                minmax[0] = minmax[1] = f[0];
1565
1566                if (hasFillValue) fill = ((float[]) fillValue)[0];
1567                for (int i = 0; i < n; i++) {
1568                    if ((hasFillValue && f[i] == fill) || isNaNINF(f[i])) continue;
1569                    if (minmax[0] > f[i]) {
1570                        minmax[0] = f[i];
1571                    }
1572                    if (minmax[1] < f[i]) {
1573                        minmax[1] = f[i];
1574                    }
1575                }
1576
1577                break;
1578            case 'D':
1579                double[] d = (double[]) data;
1580                minmax[0] = minmax[1] = d[0];
1581
1582                if (hasFillValue) fill = ((double[]) fillValue)[0];
1583                for (int i = 0; i < n; i++) {
1584                    if ((hasFillValue && d[i] == fill) || isNaNINF(d[i])) continue;
1585
1586                    if (minmax[0] > d[i]) {
1587                        minmax[0] = d[i];
1588                    }
1589                    if (minmax[1] < d[i]) {
1590                        minmax[1] = d[i];
1591                    }
1592                }
1593                break;
1594            default:
1595                retval = -1;
1596                break;
1597        } // (dname)
1598
1599        return retval;
1600    }
1601
1602    /**
1603     * Finds the distribution of data values
1604     *
1605     * @param data
1606     *            the raw data array
1607     * @param dataDist
1608     *            the data distirbution.
1609     * @param minmax
1610     *            the data range
1611     *
1612     * @return non-negative if successful; otherwise, returns negative
1613     */
1614    public static int findDataDist(Object data, int[] dataDist, double[] minmax) {
1615        int retval = 0;
1616        double delt = 1;
1617
1618        if ((data == null) || (minmax == null) || dataDist == null) return -1;
1619
1620        int n = Array.getLength(data);
1621
1622        if (minmax[1] != minmax[0]) delt = (dataDist.length - 1) / (minmax[1] - minmax[0]);
1623
1624        for (int i = 0; i < dataDist.length; i++)
1625            dataDist[i] = 0;
1626
1627        int idx;
1628        double val;
1629        for (int i = 0; i < n; i++) {
1630            val = ((Number) Array.get(data, i)).doubleValue();
1631            if (val>=minmax[0] && val <=minmax[1]) {
1632                idx = (int) ((val - minmax[0]) * delt);
1633                dataDist[idx]++;
1634            } // don't count invalid values
1635        }
1636
1637        return retval;
1638    }
1639
1640    /**
1641     * Computes mean and standard deviation of a data array
1642     *
1643     * @param data
1644     *            the raw data array
1645     * @param avgstd
1646     *            the statistics: avgstd[0]=mean and avgstd[1]=stdev.
1647     * @param fillValue
1648     *            the missing value or fill value. Exclude this value when
1649     *            compute statistics
1650     *
1651     * @return non-negative if successful; otherwise, returns negative
1652     */
1653    public static int computeStatistics(Object data, double[] avgstd, Object fillValue) {
1654        int retval = 1;
1655        double sum = 0;
1656        double avg = 0.0;
1657        double var = 0.0;
1658        double diff = 0.0;
1659        double fill = 0.0;
1660
1661        if ((data == null) || (avgstd == null) || (Array.getLength(data) <= 0) || (Array.getLength(avgstd) < 2)) {
1662            return -1;
1663        }
1664
1665        int n = Array.getLength(data);
1666        boolean hasFillValue = (fillValue != null && fillValue.getClass().isArray());
1667
1668        String cname = data.getClass().getName();
1669        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1670        log.trace("computeStatistics() cname={} : dname={}", cname, dname);
1671
1672        int npoints = 0;
1673        switch (dname) {
1674            case 'B':
1675                byte[] b = (byte[]) data;
1676                if (hasFillValue) fill = ((byte[]) fillValue)[0];
1677                for (int i = 0; i < n; i++) {
1678                    if (hasFillValue && b[i] == fill) continue;
1679                    sum += b[i];
1680                    npoints++;
1681                }
1682                if (npoints > 0) {
1683                    avg = sum / npoints;
1684                    for (int i = 0; i < n; i++) {
1685                        if (hasFillValue && b[i] == fill)
1686                            continue;
1687                        diff = b[i] - avg;
1688                        var += diff * diff;
1689                    }
1690                }
1691                break;
1692            case 'S':
1693                short[] s = (short[]) data;
1694                if (hasFillValue) fill = ((short[]) fillValue)[0];
1695                for (int i = 0; i < n; i++) {
1696                    if (hasFillValue && s[i] == fill) continue;
1697                    sum += s[i];
1698                    npoints++;
1699                }
1700                if (npoints > 0) {
1701                    avg = sum / npoints;
1702                    for (int i = 0; i < n; i++) {
1703                        if (hasFillValue && s[i] == fill)
1704                            continue;
1705                        diff = s[i] - avg;
1706                        var += diff * diff;
1707                    }
1708                }
1709                break;
1710            case 'I':
1711                int[] ia = (int[]) data;
1712                if (hasFillValue) fill = ((int[]) fillValue)[0];
1713                for (int i = 0; i < n; i++) {
1714                    if (hasFillValue && ia[i] == fill) continue;
1715                    sum += ia[i];
1716                    npoints++;
1717                }
1718                if (npoints > 0) {
1719                    avg = sum / npoints;
1720                    for (int i = 0; i < n; i++) {
1721                        if (hasFillValue && ia[i] == fill)
1722                            continue;
1723                        diff = ia[i] - avg;
1724                        var += diff * diff;
1725                    }
1726                }
1727                break;
1728            case 'J':
1729                long[] l = (long[]) data;
1730                if (hasFillValue) fill = ((long[]) fillValue)[0];
1731                for (int i = 0; i < n; i++) {
1732                    if (hasFillValue && l[i] == fill) continue;
1733                    sum += l[i];
1734                    npoints++;
1735                }
1736                if (npoints > 0) {
1737                    avg = sum / npoints;
1738                    for (int i = 0; i < n; i++) {
1739                        if (hasFillValue && l[i] == fill)
1740                            continue;
1741                        diff = l[i] - avg;
1742                        var += diff * diff;
1743                    }
1744                }
1745                break;
1746            case 'F':
1747                float[] f = (float[]) data;
1748                if (hasFillValue) fill = ((float[]) fillValue)[0];
1749                for (int i = 0; i < n; i++) {
1750                    if (hasFillValue && f[i] == fill) continue;
1751                    sum += f[i];
1752                    npoints++;
1753                }
1754                if (npoints > 0) {
1755                    avg = sum / npoints;
1756                    for (int i = 0; i < n; i++) {
1757                        if (hasFillValue && f[i] == fill)
1758                            continue;
1759                        diff = f[i] - avg;
1760                        var += diff * diff;
1761                    }
1762                }
1763                break;
1764            case 'D':
1765                double[] d = (double[]) data;
1766                if (hasFillValue) fill = ((double[]) fillValue)[0];
1767                for (int i = 0; i < n; i++) {
1768                    if (hasFillValue && d[i] == fill) continue;
1769                    sum += d[i];
1770                    npoints++;
1771                }
1772                if (npoints > 0) {
1773                    avg = sum / npoints;
1774                    for (int i = 0; i < n; i++) {
1775                        if (hasFillValue && d[i] == fill)
1776                            continue;
1777                        diff = d[i] - avg;
1778                        var += diff * diff;
1779                    }
1780                }
1781                break;
1782            default:
1783                retval = -1;
1784                break;
1785        } // (dname)
1786
1787        if (npoints <= 1) {
1788            if (npoints < 1) avgstd[0] = fill;
1789            avgstd[1] = 0;
1790        }
1791        else {
1792            avgstd[0] = avg;
1793            avgstd[1] = Math.sqrt(var / (npoints - 1));
1794        }
1795
1796        return retval;
1797    }
1798
1799    public static void saveAsBinary(DataOutputStream out, Object data, ByteOrder order) throws Exception {
1800        String cname = data.getClass().getName();
1801        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1802        ByteBuffer bb = null;
1803
1804        int size = Array.getLength(data);
1805
1806        if (dname == 'B') {
1807            byte[] bdata = (byte[]) data;
1808
1809            bb = ByteBuffer.allocate(BYTE_BUFFER_SIZE);
1810            bb.order(order);
1811
1812            int remainingSize = size - BYTE_BUFFER_SIZE;
1813            int allocValue = 0;
1814            int iterationNumber = 0;
1815            do {
1816                if (remainingSize <= 0) {
1817                    allocValue = remainingSize + BYTE_BUFFER_SIZE;
1818                }
1819                else {
1820                    allocValue = BYTE_BUFFER_SIZE;
1821                }
1822                bb.clear();
1823                bb.put(bdata, (iterationNumber * BYTE_BUFFER_SIZE), allocValue);
1824                out.write(bb.array(), 0, allocValue);
1825                remainingSize = remainingSize - BYTE_BUFFER_SIZE;
1826                iterationNumber++;
1827            } while (remainingSize > -BYTE_BUFFER_SIZE);
1828
1829            out.flush();
1830            out.close();
1831        }
1832        else if (dname == 'S') {
1833            short[] sdata = (short[]) data;
1834            bb = ByteBuffer.allocate(SHORT_BUFFER_SIZE * 2);
1835            bb.order(order);
1836
1837            ShortBuffer sb = bb.asShortBuffer();
1838            int remainingSize = size - SHORT_BUFFER_SIZE;
1839            int allocValue = 0;
1840            int iterationNumber = 0;
1841            do {
1842                if (remainingSize <= 0) {
1843                    allocValue = remainingSize + SHORT_BUFFER_SIZE;
1844                }
1845                else {
1846                    allocValue = SHORT_BUFFER_SIZE;
1847                }
1848                bb.clear();
1849                sb.clear();
1850                sb.put(sdata, (iterationNumber * SHORT_BUFFER_SIZE), allocValue);
1851                out.write(bb.array(), 0, allocValue * 2);
1852                remainingSize = remainingSize - SHORT_BUFFER_SIZE;
1853                iterationNumber++;
1854            } while (remainingSize > -SHORT_BUFFER_SIZE);
1855
1856            out.flush();
1857            out.close();
1858        }
1859        else if (dname == 'I') {
1860            int[] idata = (int[]) data;
1861            bb = ByteBuffer.allocate(INT_BUFFER_SIZE * 4);
1862            bb.order(order);
1863
1864            IntBuffer ib = bb.asIntBuffer();
1865            int remainingSize = size - INT_BUFFER_SIZE;
1866            int allocValue = 0;
1867            int iterationNumber = 0;
1868            do {
1869                if (remainingSize <= 0) {
1870                    allocValue = remainingSize + INT_BUFFER_SIZE;
1871                }
1872                else {
1873                    allocValue = INT_BUFFER_SIZE;
1874                }
1875                bb.clear();
1876                ib.clear();
1877                ib.put(idata, (iterationNumber * INT_BUFFER_SIZE), allocValue);
1878                out.write(bb.array(), 0, allocValue * 4);
1879                remainingSize = remainingSize - INT_BUFFER_SIZE;
1880                iterationNumber++;
1881            } while (remainingSize > -INT_BUFFER_SIZE);
1882
1883            out.flush();
1884            out.close();
1885        }
1886        else if (dname == 'J') {
1887            long[] ldata = (long[]) data;
1888
1889            bb = ByteBuffer.allocate(LONG_BUFFER_SIZE * 8);
1890            bb.order(order);
1891
1892            LongBuffer lb = bb.asLongBuffer();
1893            int remainingSize = size - LONG_BUFFER_SIZE;
1894            int allocValue = 0;
1895            int iterationNumber = 0;
1896            do {
1897                if (remainingSize <= 0) {
1898                    allocValue = remainingSize + LONG_BUFFER_SIZE;
1899                }
1900                else {
1901                    allocValue = LONG_BUFFER_SIZE;
1902                }
1903                bb.clear();
1904                lb.clear();
1905                lb.put(ldata, (iterationNumber * LONG_BUFFER_SIZE), allocValue);
1906                out.write(bb.array(), 0, allocValue * 8);
1907                remainingSize = remainingSize - LONG_BUFFER_SIZE;
1908                iterationNumber++;
1909            } while (remainingSize > -LONG_BUFFER_SIZE);
1910
1911            out.flush();
1912            out.close();
1913        }
1914        else if (dname == 'F') {
1915            float[] fdata = (float[]) data;
1916
1917            bb = ByteBuffer.allocate(FLOAT_BUFFER_SIZE * 4);
1918            bb.order(order);
1919
1920            FloatBuffer fb = bb.asFloatBuffer();
1921            int remainingSize = size - FLOAT_BUFFER_SIZE;
1922            int allocValue = 0;
1923            int iterationNumber = 0;
1924            do {
1925                if (remainingSize <= 0) {
1926                    allocValue = remainingSize + FLOAT_BUFFER_SIZE;
1927                }
1928                else {
1929                    allocValue = FLOAT_BUFFER_SIZE;
1930                }
1931                bb.clear();
1932                fb.clear();
1933                fb.put(fdata, (iterationNumber * FLOAT_BUFFER_SIZE), allocValue);
1934                out.write(bb.array(), 0, allocValue * 4);
1935                remainingSize = remainingSize - FLOAT_BUFFER_SIZE;
1936                iterationNumber++;
1937            } while (remainingSize > -FLOAT_BUFFER_SIZE);
1938
1939            out.flush();
1940            out.close();
1941        }
1942        else if (dname == 'D') {
1943            double[] ddata = (double[]) data;
1944
1945            bb = ByteBuffer.allocate(DOUBLE_BUFFER_SIZE * 8);
1946            bb.order(order);
1947
1948            DoubleBuffer db = bb.asDoubleBuffer();
1949            int remainingSize = size - DOUBLE_BUFFER_SIZE;
1950            int allocValue = 0;
1951            int iterationNumber = 0;
1952            do {
1953                if (remainingSize <= 0) {
1954                    allocValue = remainingSize + DOUBLE_BUFFER_SIZE;
1955                }
1956                else {
1957                    allocValue = DOUBLE_BUFFER_SIZE;
1958                }
1959                bb.clear();
1960                db.clear();
1961                db.put(ddata, (iterationNumber * DOUBLE_BUFFER_SIZE), allocValue);
1962                out.write(bb.array(), 0, allocValue * 8);
1963                remainingSize = remainingSize - DOUBLE_BUFFER_SIZE;
1964                iterationNumber++;
1965            } while (remainingSize > -DOUBLE_BUFFER_SIZE);
1966
1967            out.flush();
1968            out.close();
1969        }
1970    }
1971
1972    /**
1973     * Reads data from a binary file into a buffer.
1974     *
1975     * @param dataOut
1976     *            the output stream
1977     * @param fileName
1978     *            the file to read binary data from
1979     * @param order
1980     *            the new byte order, either BIG_ENDIAN or LITTLE_ENDIAN
1981     *
1982     * @return true if successful; otherwise, false.
1983     */
1984    public static boolean getBinaryDataFromFile(Object dataOut, String fileName, ByteOrder order) {
1985        if (dataOut == null) return false;
1986
1987        String fname = fileName;
1988        BufferedInputStream in = null;
1989        ByteBuffer byteBuffer = null;
1990        boolean valChanged = false;
1991
1992        try (FileInputStream inputFile = new FileInputStream(fname)) {
1993            long fileSize = inputFile.getChannel().size();
1994            in = new BufferedInputStream(inputFile);
1995
1996            int datasetSize = Array.getLength(dataOut);
1997            String cname = dataOut.getClass().getName();
1998            char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1999
2000            if (dname == 'B') {
2001                long datasetByteSize = datasetSize;
2002                byteBuffer = ByteBuffer.allocate(BYTE_BUFFER_SIZE);
2003                byteBuffer.order(order);
2004
2005                int bufferSize = (int) Math.min(fileSize, datasetByteSize);
2006
2007                int remainingSize = bufferSize - (BYTE_BUFFER_SIZE);
2008                int allocValue = 0;
2009                int iterationNumber = 0;
2010                byte[] byteArray = new byte[BYTE_BUFFER_SIZE];
2011                do {
2012                    if (remainingSize <= 0) {
2013                        allocValue = remainingSize + (BYTE_BUFFER_SIZE);
2014                    }
2015                    else {
2016                        allocValue = (BYTE_BUFFER_SIZE);
2017                    }
2018
2019                    in.read(byteBuffer.array(), 0, allocValue);
2020
2021                    byteBuffer.get(byteArray, 0, allocValue);
2022                    System.arraycopy(byteArray, 0, dataOut, (iterationNumber * BYTE_BUFFER_SIZE), allocValue);
2023                    byteBuffer.clear();
2024                    remainingSize = remainingSize - (BYTE_BUFFER_SIZE);
2025                    iterationNumber++;
2026                } while (remainingSize > -(BYTE_BUFFER_SIZE));
2027
2028                valChanged = true;
2029            }
2030            else if (dname == 'S') {
2031                long datasetShortSize = (long) datasetSize * 2;
2032                byteBuffer = ByteBuffer.allocate(SHORT_BUFFER_SIZE * 2);
2033                byteBuffer.order(order);
2034
2035                int bufferSize = (int) Math.min(fileSize, datasetShortSize);
2036                int remainingSize = bufferSize - (SHORT_BUFFER_SIZE * 2);
2037                int allocValue = 0;
2038                int iterationNumber = 0;
2039                ShortBuffer sb = byteBuffer.asShortBuffer();
2040                short[] shortArray = new short[SHORT_BUFFER_SIZE];
2041
2042                do {
2043                    if (remainingSize <= 0) {
2044                        allocValue = remainingSize + (SHORT_BUFFER_SIZE * 2);
2045                    }
2046                    else {
2047                        allocValue = (SHORT_BUFFER_SIZE * 2);
2048                    }
2049                    in.read(byteBuffer.array(), 0, allocValue);
2050                    sb.get(shortArray, 0, allocValue / 2);
2051                    System.arraycopy(shortArray, 0, dataOut, (iterationNumber * SHORT_BUFFER_SIZE), allocValue / 2);
2052                    byteBuffer.clear();
2053                    sb.clear();
2054                    remainingSize = remainingSize - (SHORT_BUFFER_SIZE * 2);
2055                    iterationNumber++;
2056                } while (remainingSize > -(SHORT_BUFFER_SIZE * 2));
2057
2058                valChanged = true;
2059            }
2060            else if (dname == 'I') {
2061                long datasetIntSize = (long) datasetSize * 4;
2062                byteBuffer = ByteBuffer.allocate(INT_BUFFER_SIZE * 4);
2063                byteBuffer.order(order);
2064
2065                int bufferSize = (int) Math.min(fileSize, datasetIntSize);
2066                int remainingSize = bufferSize - (INT_BUFFER_SIZE * 4);
2067                int allocValue = 0;
2068                int iterationNumber = 0;
2069                int[] intArray = new int[INT_BUFFER_SIZE];
2070                byte[] tmpBuf = byteBuffer.array();
2071                IntBuffer ib = byteBuffer.asIntBuffer();
2072
2073                do {
2074                    if (remainingSize <= 0) {
2075                        allocValue = remainingSize + (INT_BUFFER_SIZE * 4);
2076                    }
2077                    else {
2078                        allocValue = (INT_BUFFER_SIZE * 4);
2079                    }
2080                    in.read(tmpBuf, 0, allocValue);
2081                    ib.get(intArray, 0, allocValue / 4);
2082                    System.arraycopy(intArray, 0, dataOut, (iterationNumber * INT_BUFFER_SIZE), allocValue / 4);
2083                    byteBuffer.clear();
2084                    ib.clear();
2085                    remainingSize = remainingSize - (INT_BUFFER_SIZE * 4);
2086                    iterationNumber++;
2087                } while (remainingSize > -(INT_BUFFER_SIZE * 4));
2088
2089                valChanged = true;
2090            }
2091            else if (dname == 'J') {
2092                long datasetLongSize = (long) datasetSize * 8;
2093                byteBuffer = ByteBuffer.allocate(LONG_BUFFER_SIZE * 8);
2094                byteBuffer.order(order);
2095
2096                int bufferSize = (int) Math.min(fileSize, datasetLongSize);
2097                int remainingSize = bufferSize - (LONG_BUFFER_SIZE * 8);
2098                int allocValue = 0;
2099                int iterationNumber = 0;
2100                long[] longArray = new long[LONG_BUFFER_SIZE];
2101                LongBuffer lb = byteBuffer.asLongBuffer();
2102
2103                do {
2104                    if (remainingSize <= 0) {
2105                        allocValue = remainingSize + (LONG_BUFFER_SIZE * 8);
2106                    }
2107                    else {
2108                        allocValue = (LONG_BUFFER_SIZE * 8);
2109                    }
2110
2111                    in.read(byteBuffer.array(), 0, allocValue);
2112                    lb.get(longArray, 0, allocValue / 8);
2113                    System.arraycopy(longArray, 0, dataOut, (iterationNumber * LONG_BUFFER_SIZE), allocValue / 8);
2114                    byteBuffer.clear();
2115                    lb.clear();
2116                    remainingSize = remainingSize - (LONG_BUFFER_SIZE * 8);
2117                    iterationNumber++;
2118                } while (remainingSize > -(LONG_BUFFER_SIZE * 8));
2119
2120                valChanged = true;
2121            }
2122            else if (dname == 'F') {
2123                long datasetFloatSize = (long) datasetSize * 4;
2124                byteBuffer = ByteBuffer.allocate(FLOAT_BUFFER_SIZE * 4);
2125                byteBuffer.order(order);
2126
2127                int bufferSize = (int) Math.min(fileSize, datasetFloatSize);
2128                int remainingSize = bufferSize - (FLOAT_BUFFER_SIZE * 4);
2129                int allocValue = 0;
2130                int iterationNumber = 0;
2131                FloatBuffer fb = byteBuffer.asFloatBuffer();
2132                float[] floatArray = new float[FLOAT_BUFFER_SIZE];
2133                do {
2134                    if (remainingSize <= 0) {
2135                        allocValue = remainingSize + (FLOAT_BUFFER_SIZE * 4);
2136                    }
2137                    else {
2138                        allocValue = (FLOAT_BUFFER_SIZE * 4);
2139                    }
2140
2141                    in.read(byteBuffer.array(), 0, allocValue);
2142                    fb.get(floatArray, 0, allocValue / 4);
2143                    System.arraycopy(floatArray, 0, dataOut, (iterationNumber * FLOAT_BUFFER_SIZE), allocValue / 4);
2144                    byteBuffer.clear();
2145                    fb.clear();
2146                    remainingSize = remainingSize - (FLOAT_BUFFER_SIZE * 4);
2147                    iterationNumber++;
2148                } while (remainingSize > -(FLOAT_BUFFER_SIZE * 4));
2149
2150                valChanged = true;
2151            }
2152            else if (dname == 'D') {
2153                long datasetDoubleSize = (long) datasetSize * 8;
2154                byteBuffer = ByteBuffer.allocate(DOUBLE_BUFFER_SIZE * 8);
2155                byteBuffer.order(order);
2156
2157                int bufferSize = (int) Math.min(fileSize, datasetDoubleSize);
2158                int remainingSize = bufferSize - (DOUBLE_BUFFER_SIZE * 8);
2159                int allocValue = 0;
2160                int iterationNumber = 0;
2161                DoubleBuffer db = byteBuffer.asDoubleBuffer();
2162                double[] doubleArray = new double[DOUBLE_BUFFER_SIZE];
2163
2164                do {
2165                    if (remainingSize <= 0) {
2166                        allocValue = remainingSize + (DOUBLE_BUFFER_SIZE * 8);
2167                    }
2168                    else {
2169                        allocValue = (DOUBLE_BUFFER_SIZE * 8);
2170                    }
2171
2172                    in.read(byteBuffer.array(), 0, allocValue);
2173                    db.get(doubleArray, 0, allocValue / 8);
2174                    System.arraycopy(doubleArray, 0, dataOut, (iterationNumber * DOUBLE_BUFFER_SIZE), allocValue / 8);
2175                    byteBuffer.clear();
2176                    db.clear();
2177                    remainingSize = remainingSize - (DOUBLE_BUFFER_SIZE * 8);
2178                    iterationNumber++;
2179                } while (remainingSize > -(DOUBLE_BUFFER_SIZE * 8));
2180
2181                valChanged = true;
2182            }
2183        }
2184        catch (Exception es) {
2185            es.printStackTrace();
2186        }
2187        finally {
2188            try {
2189                in.close();
2190            }
2191            catch (IOException ex) {
2192                // Empty on purpose
2193            }
2194        }
2195
2196        return valChanged;
2197    }
2198
2199    /**
2200     * Returns a string representation of the long argument as an unsigned
2201     * integer in base 2. This is different from Long.toBinaryString(long i).
2202     * This function add padding (0's) to the string based on the nbytes. For
2203     * example, if v=15, nbytes=1, the string will be "00001111".
2204     *
2205     * @param v
2206     *            the long value
2207     * @param nbytes
2208     *            number of bytes in the integer
2209     *
2210     * @return the string representation of the unsigned long value represented
2211     *         by the argument in binary (base 2).
2212     */
2213    public static final String toBinaryString(long v, int nbytes) {
2214        if (nbytes <= 0) return null;
2215
2216        int nhex = nbytes * 2;
2217        short[] hex = new short[nhex];
2218
2219        for (int i = 0; i < nhex; i++)
2220            hex[i] = (short) (0x0F & (v >> (i * 4)));
2221
2222        StringBuilder sb = new StringBuilder();
2223        boolean isEven = true;
2224        for (int i = nhex - 1; i >= 0; i--) {
2225            if (isEven && i < nhex - 1) sb.append(" ");
2226            isEven = !isEven; // toggle
2227
2228            switch (hex[i]) {
2229                case 0:
2230                    sb.append("0000");
2231                    break;
2232                case 1:
2233                    sb.append("0001");
2234                    break;
2235                case 2:
2236                    sb.append("0010");
2237                    break;
2238                case 3:
2239                    sb.append("0011");
2240                    break;
2241                case 4:
2242                    sb.append("0100");
2243                    break;
2244                case 5:
2245                    sb.append("0101");
2246                    break;
2247                case 6:
2248                    sb.append("0110");
2249                    break;
2250                case 7:
2251                    sb.append("0111");
2252                    break;
2253                case 8:
2254                    sb.append("1000");
2255                    break;
2256                case 9:
2257                    sb.append("1001");
2258                    break;
2259                case 10:
2260                    sb.append("1010");
2261                    break;
2262                case 11:
2263                    sb.append("1011");
2264                    break;
2265                case 12:
2266                    sb.append("1100");
2267                    break;
2268                case 13:
2269                    sb.append("1101");
2270                    break;
2271                case 14:
2272                    sb.append("1110");
2273                    break;
2274                case 15:
2275                    sb.append("1111");
2276                    break;
2277                default:
2278                    break;
2279            }
2280        }
2281
2282        return sb.toString();
2283    }
2284
2285    public static final String toBinaryString(BigDecimal v, int nbytes) {
2286        StringBuilder sb = new StringBuilder();
2287        /*
2288         * String val = String.format("%" + (8 * nbytes) + "s", v.toString(2)).replace(" ", "0").toUpperCase(); //
2289         * Insert spacing for (int i = 0; i < nbytes; i++) { sb.append(val.substring(i * nbytes, nbytes * (i + 1))); if
2290         * (i < nbytes - 1) sb.append(" "); }
2291         */
2292        return sb.toString();
2293    }
2294
2295    public static final String toBinaryString(BigInteger v, int nbytes) {
2296        StringBuilder sb = new StringBuilder();
2297        String val = String.format("%" + (8 * nbytes) + "s", v.toString(2)).replace(" ", "0").toUpperCase();
2298
2299        // Insert spacing
2300        for (int i = 0; i < nbytes; i++) {
2301            sb.append(val.substring(i * nbytes, nbytes * (i + 1)));
2302            if (i < nbytes - 1) sb.append(" ");
2303        }
2304
2305        return sb.toString();
2306    }
2307
2308    static final char[] HEXCHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
2309
2310    /**
2311     * Returns a string representation of the long argument as an unsigned integer in base 16. This
2312     * is different from Long.toHexString(long i). This function add padding (0's) to the string
2313     * based on the nbytes. For example, if v=42543, nbytes=4, the string will be "0000A62F".
2314     *
2315     * @param v
2316     *            the long value
2317     * @param nbytes
2318     *            number of bytes in the integer
2319     * @return the string representation of the unsigned long value represented by the argument in
2320     *         hexadecimal (base 16).
2321     */
2322    public static final String toHexString (long v, int nbytes) {
2323        if (nbytes <= 0) return null;
2324
2325        int nhex = nbytes * 2;
2326        short[] hex = new short[nhex];
2327
2328        for (int i = 0; i < nhex; i++) {
2329            hex[i] = (short) (0x0F & (v >> (i * 4)));
2330        }
2331
2332        StringBuilder sb = new StringBuilder();
2333        for (int i = nhex - 1; i >= 0; i--) {
2334            sb.append(HEXCHARS[hex[i]]);
2335        }
2336
2337        return sb.toString();
2338    }
2339
2340    /**
2341     * Returns a string representation of the BigInteger argument as an unsigned integer in base 16.
2342     * This is different from BigInteger.toString(16). This function adds padding (0's) to the string
2343     * based on the nbytes. For example, if v=42543, nbytes=4, the string will be "0000A62F".
2344     *
2345     * @param v
2346     *            the BigInteger value
2347     * @param nbytes
2348     *            number of bytes in the integer
2349     * @return the string representation of the unsigned long value represented by the argument in
2350     *         hexadecimal (base 16).
2351     */
2352    public static final String toHexString (BigInteger v, int nbytes) {
2353        return String.format("%" + (2 * nbytes) + "s", v.toString(16)).replace(" ", "0").toUpperCase();
2354    }
2355
2356    public static final String toHexString (BigDecimal v, int nbytes) {
2357        return null; // String.format("%" + (2 * nbytes) + "s", v.toString(16)).replace(" ", "0").toUpperCase();
2358    }
2359
2360    /**
2361     * Apply bitmask to a data array.
2362     *
2363     * @param theData
2364     *            the data array which the bitmask is applied to.
2365     * @param theMask
2366     *            the bitmask to be applied to the data array.
2367     * @param op
2368     *            the bitmask op to be applied
2369     *
2370     * @return true if bitmask is applied successfully; otherwise, false.
2371     */
2372    public static final boolean applyBitmask(Object theData, BitSet theMask, ViewProperties.BITMASK_OP op) {
2373        if (     theData == null
2374            || !(theData instanceof Array)
2375            || ((theData instanceof Array) && (Array.getLength(theData) <= 0))
2376            ||   theMask == null) return false;
2377
2378        char nt = '0';
2379        String cName = theData.getClass().getName();
2380        int cIndex = cName.lastIndexOf('[');
2381        if (cIndex >= 0) {
2382            nt = cName.charAt(cIndex + 1);
2383        }
2384
2385        // only deal with 8/16/32/64 bit datasets
2386        if (!(nt == 'B' || nt == 'S' || nt == 'I' || nt == 'J')) return false;
2387
2388        long bmask = 0;
2389        long theValue = 0;
2390        long packedValue = 0;
2391
2392        int nbits = theMask.length();
2393        int len = Array.getLength(theData);
2394
2395        for (int i = 0; i < nbits; i++) {
2396            if (theMask.get(i)) bmask += 1 << i;
2397        }
2398
2399        for (int i = 0; i < len; i++) {
2400            if (nt == 'B')
2401                theValue = ((byte[]) theData)[i] & bmask;
2402            else if (nt == 'S')
2403                theValue = ((short[]) theData)[i] & bmask;
2404            else if (nt == 'I')
2405                theValue = ((int[]) theData)[i] & bmask;
2406            else if (nt == 'J')
2407                theValue = ((long[]) theData)[i] & bmask;
2408
2409            // apply bitmask only
2410            if (op == BITMASK_OP.AND)
2411                packedValue = theValue;
2412            else {
2413                // extract bits
2414                packedValue = 0;
2415                int bitPosition = 0;
2416
2417                for (int j = 0; j < nbits; j++) {
2418                    if (theMask.get(j)) {
2419                        long bitValue = (theValue & 1);
2420                        packedValue += (bitValue << bitPosition);
2421                        bitPosition++;
2422                    }
2423                    // move to the next bit
2424                    theValue = theValue >> 1;
2425                }
2426            }
2427
2428            if (nt == 'B')
2429                ((byte[]) theData)[i] = (byte) packedValue;
2430            else if (nt == 'S')
2431                ((short[]) theData)[i] = (short) packedValue;
2432            else if (nt == 'I')
2433                ((int[]) theData)[i] = (int) packedValue;
2434            else if (nt == 'J')
2435                ((long[]) theData)[i] = packedValue;
2436        } // (int i = 0; i < len; i++)
2437
2438        return true;
2439    } /* public static final boolean applyBitmask() */
2440
2441    /**
2442     * Read HDF5 user block data into byte array.
2443     *
2444     * @param filename the HDF5 file from which to get the user block
2445     *
2446     * @return a byte array of user block, or null if there is user data.
2447     */
2448    public static byte[] getHDF5UserBlock(String filename) {
2449        byte[] userBlock = null;
2450
2451        try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
2452            byte[] header = new byte[8];
2453            long fileSize = raf.length();
2454
2455            // The super block is located by searching for the HDF5 file signature
2456            // at byte offset 0, byte offset 512 and at successive locations in the
2457            // file, each a multiple of two of the previous location, i.e. 0, 512,
2458            // 1024, 2048, etc
2459            long offset = 0;
2460            boolean ish5 = false;
2461            while (offset < fileSize) {
2462                raf.seek(offset);
2463                raf.read(header);
2464
2465                if ((header[0] == -119) && (header[1] == 72) && (header[2] == 68) && (header[3] == 70) && (header[4] == 13) && (header[5] == 10) && (header[6] == 26)
2466                        && (header[7] == 10)) {
2467                    ish5 = true;
2468                    break; // find the end of user block
2469                }
2470                else {
2471                    ish5 = false;
2472                    if (offset == 0) {
2473                        offset = 512;
2474                    }
2475                    else {
2476                        offset *= 2;
2477                    }
2478                }
2479            }
2480
2481            if (!ish5 || (offset == 0)) {
2482                return null;
2483            }
2484
2485            int blockSize = (int) offset;
2486            userBlock = new byte[blockSize];
2487            raf.seek(0);
2488            raf.read(userBlock, 0, blockSize);
2489        }
2490        catch (Exception ex) {
2491            userBlock = null;
2492        }
2493
2494        return userBlock;
2495    }
2496
2497    /**
2498     * Write HDF5 user block data into byte array.
2499     *
2500     * @param fin the input filename
2501     * @param fout the output filename
2502     * @param buf  the data to write into the user block
2503     *
2504     * @return a byte array of user block, or null if there is user data.
2505     */
2506    public static boolean setHDF5UserBlock(String fin, String fout, byte[] buf) {
2507        boolean ish5 = false;
2508
2509        if ((buf == null) || (buf.length <= 0)) {
2510            return false;
2511        }
2512
2513        File tmpFile = new File(fin);
2514        if (!tmpFile.exists()) {
2515            return false;
2516        }
2517
2518        long offset = 0;
2519        // find the end of user block for the input file
2520        try (RandomAccessFile raf = new RandomAccessFile(fin, "r")) {
2521            byte[] header = new byte[8];
2522            long fileSize = raf.length();
2523
2524            // The super block is located by searching for the HDF5 file signature
2525            // at byte offset 0, byte offset 512 and at successive locations in the
2526            // file, each a multiple of two of the previous location, i.e. 0, 512,
2527            // 1024, 2048, etc
2528            while (offset < fileSize) {
2529                raf.seek(offset);
2530                raf.read(header);
2531
2532                if ((header[0] == -119) && (header[1] == 72) && (header[2] == 68) && (header[3] == 70) && (header[4] == 13) && (header[5] == 10) && (header[6] == 26)
2533                        && (header[7] == 10)) {
2534                    ish5 = true;
2535                    break;
2536                }
2537                else {
2538                    ish5 = false;
2539                    if (offset == 0) {
2540                        offset = 512;
2541                    }
2542                    else {
2543                        offset *= 2;
2544                    }
2545                }
2546            }
2547        }
2548        catch (Exception ex) {
2549            return false;
2550        }
2551
2552        if (!ish5) {
2553            return false;
2554        }
2555
2556        int length = 0;
2557        int bsize = 1024;
2558        byte[] buffer;
2559
2560        try (BufferedInputStream bi = new BufferedInputStream(new FileInputStream(fin))) {
2561            try (BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(fout))) {
2562                // skip the header of original file
2563                try {
2564                    long count = bi.skip(offset);
2565                    if (count != offset)
2566                        log.debug("file skip actual:{} req:{}", count, offset);
2567                }
2568                catch (Exception ex) {
2569                    // Empty on purpose
2570                }
2571
2572                // write the header into the new file
2573                try {
2574                    bo.write(buf, 0, buf.length);
2575                }
2576                catch (Exception ex) {
2577                    // Empty on purpose
2578                }
2579
2580                // The super block space is allocated by offset 0, 512, 1024, 2048, etc
2581                offset = 512;
2582                while (offset < buf.length) {
2583                    offset *= 2;
2584                }
2585                int padSize = (int) (offset - buf.length);
2586                if (padSize > 0) {
2587                    byte[] padBuf = new byte[padSize];
2588                    try {
2589                        bo.write(padBuf, 0, padSize);
2590                    }
2591                    catch (Exception ex) {
2592                        // Empty on purpose
2593                    }
2594                }
2595
2596                // copy the hdf5 file content from input file to the output file
2597                buffer = new byte[bsize];
2598                try {
2599                    length = bi.read(buffer, 0, bsize);
2600                }
2601                catch (Exception ex) {
2602                    length = 0;
2603                }
2604                while (length > 0) {
2605                    try {
2606                        bo.write(buffer, 0, length);
2607                        length = bi.read(buffer, 0, bsize);
2608                    }
2609                    catch (Exception ex) {
2610                        length = 0;
2611                    }
2612                }
2613
2614                try {
2615                    bo.flush();
2616                }
2617                catch (Exception ex) {
2618                    // Empty on purpose
2619                }
2620            }
2621            catch (Exception ex) {
2622                return false;
2623            }
2624        }
2625        catch (Exception ex) {
2626            return false;
2627        }
2628        return true;
2629    }
2630
2631    /**
2632     * look at the first 4 bytes of the file to see if it is an HDF4 file.
2633     * byte[0]=14, byte[1]=3, byte[2]=19, byte[3]=1 or if it is a netCDF file
2634     * byte[0]=67, byte[1]=68, byte[2]=70, byte[3]=1
2635     *
2636     * @param filename the file to test if HDF4
2637     *
2638     * @return true if the file is of type HDF4
2639     */
2640    public static boolean isHDF4(String filename) {
2641        boolean ish4 = false;
2642
2643        try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
2644            byte[] header = new byte[4];
2645            raf.read(header);
2646
2647            if ((header[0] == 14) && (header[1] == 3) && (header[2] == 19) && (header[3] == 1)) {
2648                ish4 = true;
2649            }
2650            else {
2651                ish4 = false;
2652            }
2653        }
2654        catch (Exception ex) {
2655            return false;
2656        }
2657
2658        return ish4;
2659    }
2660
2661    /**
2662     * look at the first 8 bytes of the file to see if it is an HDF5 file.
2663     * byte[0]=-199 which is 137 in unsigned byte, byte[1]=72, byte[2]=68,
2664     * byte[3]=70, byte[4]=13, byte[5]=10, byte[6]=26, byte[7]=10
2665     *
2666     * @param filename the file to test if HDF5
2667     *
2668     * @return true if the file is of type HDF5
2669     */
2670    public static boolean isHDF5(String filename) {
2671        boolean ish5 = false;
2672
2673        try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
2674            byte[] header = new byte[8];
2675            long fileSize = raf.length();
2676
2677            // The super block is located by searching for the HDF5 file signature
2678            // at byte offset 0, byte offset 512 and at successive locations in the
2679            // file, each a multiple of two of the previous location, i.e. 0, 512,
2680            // 1024, 2048, etc
2681            long offset = 0;
2682            while (!ish5 && (offset < fileSize)) {
2683                raf.seek(offset);
2684                raf.read(header);
2685
2686                if ((header[0] == -119) && (header[1] == 72) && (header[2] == 68) && (header[3] == 70) && (header[4] == 13) && (header[5] == 10) && (header[6] == 26)
2687                        && (header[7] == 10)) {
2688                    ish5 = true;
2689                }
2690                else {
2691                    ish5 = false;
2692                    if (offset == 0) {
2693                        offset = 512;
2694                    }
2695                    else {
2696                        offset *= 2;
2697                    }
2698                }
2699            }
2700        }
2701        catch (Exception ex) {
2702            return false;
2703        }
2704
2705        return ish5;
2706    }
2707
2708    /**
2709     * look at the first 4 bytes of the file to see if it is a netCDF file
2710     * byte[0]=67, byte[1]=68, byte[2]=70, byte[3]=1 or
2711     *
2712     * @param filename the file to test if netcdf
2713     *
2714     * @return true if the file is of type netcdf
2715     */
2716    public static boolean isNetcdf(String filename) {
2717        boolean isnc = false;
2718
2719        try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
2720
2721        byte[] header = new byte[4];
2722            raf.read(header);
2723            // netCDF
2724            if ((header[0] == 67) && (header[1] == 68) && (header[2] == 70)
2725                    && (header[3] == 1)) {
2726                isnc = true;
2727            }
2728            else {
2729                isnc = false;
2730            }
2731        }
2732        catch (Exception ex) {
2733            return false;
2734        }
2735
2736        return isnc;
2737    }
2738
2739    /**
2740     * Launch default browser for a given URL.
2741     *
2742     * @param url
2743     *            the URL to open.
2744     *
2745     * @throws Exception if a failure occurred
2746     */
2747    public static final void launchBrowser(String url) throws Exception {
2748        String os = System.getProperty("os.name");
2749        Runtime runtime = Runtime.getRuntime();
2750
2751        // Block for Windows Platform
2752        if (os.startsWith("Windows")) {
2753            String cmd = "rundll32 url.dll,FileProtocolHandler " + url;
2754
2755            if (new File(url).exists()) cmd = "cmd /c start \"\" \"" + url + "\"";
2756            runtime.exec(cmd);
2757        }
2758        // Block for Mac OS
2759        else if (os.startsWith("Mac OS")) {
2760            Class<?> fileMgr = Class.forName("com.apple.eio.FileManager");
2761            Method openURL = fileMgr.getDeclaredMethod("openURL", new Class[] { String.class });
2762
2763            if (new File(url).exists()) {
2764                // local file
2765                url = "file://" + url;
2766            }
2767            openURL.invoke(null, new Object[] { url });
2768        }
2769        // Block for UNIX Platform
2770        else {
2771            String[] browsers = { "firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape" };
2772            String browser = null;
2773            for (int count = 0; count < browsers.length && browser == null; count++)
2774                if (runtime.exec(new String[] { "which", browsers[count] }).waitFor() == 0) browser = browsers[count];
2775            if (browser == null)
2776                throw new Exception("Could not find web browser");
2777            else
2778                runtime.exec(new String[] { browser, url });
2779        }
2780    }
2781
2782    /** Create a new HDF file with default file creation properties
2783     *
2784     * @param filename
2785     *          the file to create
2786     * @param dir
2787     *          the directory for file
2788     * @param type
2789     *          the type of the file
2790     * @param openFiles
2791     *          the list of already opened files
2792     *
2793     * @return the FileFormat instance of the newly created file
2794     *
2795     * @throws Exception if a failure occurred
2796     */
2797    public static FileFormat createNewFile(String filename, String dir,
2798            String type, List<FileFormat> openFiles) throws Exception {
2799        log.trace("createNewFile: {} start", filename);
2800        File f = new File(filename);
2801
2802        String fname = f.getAbsolutePath();
2803        if (fname == null) return null;
2804
2805        fname = fname.trim();
2806        if ((fname == null) || (fname.length() == 0)) {
2807            throw new Exception("Invalid file name.");
2808        }
2809
2810        String extensions = FileFormat.getFileExtensions();
2811        boolean noExtension = true;
2812        if ((extensions != null) && (extensions.length() > 0)) {
2813            java.util.StringTokenizer currentExt = new java.util.StringTokenizer(extensions, ",");
2814            String extension = "";
2815            String tmpFilename = fname.toLowerCase();
2816            while (currentExt.hasMoreTokens() && noExtension) {
2817                extension = currentExt.nextToken().trim().toLowerCase();
2818                noExtension = !tmpFilename.endsWith("." + extension);
2819            }
2820        }
2821
2822        if (noExtension) {
2823            if (type.equals(FileFormat.FILE_TYPE_HDF4)) {
2824                fname += ".hdf";
2825                f = new File(fname);
2826            }
2827            else if (type.equals(FileFormat.FILE_TYPE_HDF5)) {
2828                fname += ".h5";
2829                f = new File(fname);
2830            }
2831        }
2832
2833        if (f.exists() && f.isDirectory()) {
2834            throw new Exception("File is a directory.");
2835        }
2836        log.trace("createNewFile: {} not a directory", filename);
2837
2838        File pfile = f.getParentFile();
2839        if (pfile == null) {
2840            fname = dir + File.separator + fname;
2841            f = new File(fname);
2842        }
2843        else if (!pfile.exists()) {
2844            throw new Exception("File path does not exist at\n" + pfile.getPath());
2845        }
2846
2847        // check if the file is in use
2848        log.trace("createNewFile: {} check if the file is in use", filename);
2849        if (openFiles != null) {
2850            FileFormat theFile = null;
2851            Iterator<FileFormat> iterator = openFiles.iterator();
2852            while (iterator.hasNext()) {
2853                theFile = iterator.next();
2854                if (theFile.getFilePath().equals(fname)) {
2855                    throw new Exception("Unable to create the new file. \nThe file is being used.");
2856                }
2857            }
2858        }
2859
2860        if (f.exists()) {
2861            log.trace("createNewFile: {} file exists", filename);
2862
2863            if (!MessageDialog.openConfirm(display.getShells()[0], "Create New File",
2864                    "File exists. Do you want to replace it?")) {
2865                return null;
2866            }
2867        }
2868
2869        try {
2870            int aFlag = FileFormat.FILE_CREATE_DELETE;
2871            if (!ViewProperties.getEarlyLib().equalsIgnoreCase("Latest")) {
2872                aFlag = FileFormat.FILE_CREATE_DELETE | FileFormat.FILE_CREATE_EARLY_LIB;
2873                FileFormat.getFileFormat(type).setNewLibBounds(ViewProperties.getEarlyLib(), ViewProperties.getLateLib());
2874            }
2875            log.trace("createNewFile: {} FileFormat create", filename);
2876            return FileFormat.getFileFormat(type).createFile(fname, aFlag);
2877        }
2878        catch (Exception ex) {
2879            throw new Exception(ex.getMessage());
2880        }
2881    }
2882
2883    /**
2884     * Check and find a non-exist file.
2885     *
2886     * @param path
2887     *            -- the path that the new file will be checked.
2888     * @param ext
2889     *            -- the extention of the new file.
2890     *
2891     * @return -- the new file.
2892     */
2893    public static final File checkNewFile(String path, String ext) {
2894        File file = new File(path + "new" + ext);
2895        int i = 1;
2896
2897        while (file.exists()) {
2898            file = new File(path + "new" + i + ext);
2899            i++;
2900        }
2901
2902        return file;
2903    }
2904
2905    /**
2906     * Check if a given number if NaN or INF.
2907     *
2908     * @param val
2909     *            the number to be checked
2910     *
2911     * @return true if the number is Nan or INF; otherwise, false.
2912     */
2913    public static final boolean isNaNINF(double val) {
2914        return (Double.isNaN(val) || val == Float.NEGATIVE_INFINITY || val == Float.POSITIVE_INFINITY || val == Double.NEGATIVE_INFINITY || val == Double.POSITIVE_INFINITY);
2915    }
2916
2917    /**
2918     * Since Java does not allow array indices to be larger than int type, check the
2919     * given value to see if it is within the valid range of a Java int.
2920     *
2921     * @param value
2922     *            The value to check
2923     *
2924     * @return false if the value is outside the range of a Java int, true
2925     *         otherwise.
2926     */
2927    public static boolean checkValidJavaArrayIndex(final long value) {
2928        return (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE);
2929    }
2930
2931    /**
2932     * Show an SWT error dialog with the given error message.
2933     * @param parent
2934     *           The parent Shell of the MessageDialog
2935     * @param title
2936     *           The title to set for the MessageDialog
2937     * @param errorMsg
2938     *           The error message to display in the MessageDialog
2939     */
2940    public static void showError(Shell parent, String title, String errorMsg) {
2941        String dlgTitlePrefix = "";
2942        String dlgTitleSuffix = (title == null) ? "" : title;
2943
2944        if (parent != null) {
2945            dlgTitlePrefix = parent.getText();
2946            if (dlgTitlePrefix.length() > 0)
2947                dlgTitlePrefix += " - ";
2948        }
2949
2950        MessageDialog.openError(parent, dlgTitlePrefix + dlgTitleSuffix, (errorMsg == null) ? "UNKNOWN" : errorMsg);
2951    }
2952
2953    /**
2954     * Show an SWT Information dialog with the given message.
2955     * @param parent
2956     *           The parent Shell of the MessageDialog
2957     * @param title
2958     *           The title to set for the MessageDialog
2959     * @param infoMsg
2960     *           The message to display in the MessageDialog
2961     */
2962    public static void showInformation(Shell parent, String title, String infoMsg) {
2963        String dlgTitlePrefix = "";
2964        String dlgTitleSuffix = (title == null) ? "" : title;
2965
2966        if (parent != null) {
2967            dlgTitlePrefix = parent.getText();
2968            if (dlgTitlePrefix.length() > 0)
2969                dlgTitlePrefix += " - ";
2970        }
2971
2972        MessageDialog.openInformation(parent, dlgTitlePrefix + dlgTitleSuffix, (infoMsg == null) ? "UNKNOWN" : infoMsg);
2973    }
2974
2975    /**
2976     * Show an SWT Confirm dialog with the given message.
2977     *
2978     * @param parent
2979     *            The parent Shell of the MessageDialog
2980     * @param title
2981     *            The title to set for the MessageDialog
2982     * @param confMsg
2983     *            The message to display in the MessageDialog
2984     * @return The status of the dialog after closing
2985     */
2986    public static boolean showConfirm(Shell parent, String title, String confMsg) {
2987        String dlgTitlePrefix = "";
2988        String dlgTitleSuffix = (title == null) ? "" : title;
2989
2990        if (parent != null) {
2991            dlgTitlePrefix = parent.getText();
2992            if (dlgTitlePrefix.length() > 0)
2993                dlgTitlePrefix += " - ";
2994        }
2995
2996        return MessageDialog.openConfirm(parent, dlgTitlePrefix + dlgTitleSuffix, (confMsg == null) ? "UNKNOWN" : confMsg);
2997    }
2998
2999    /**
3000     * Show an SWT Warning dialog with the given message.
3001     * @param parent
3002     *           The parent Shell of the MessageDialog
3003     * @param title
3004     *           The title to set for the MessageDialog
3005     * @param warnMsg
3006     *           The message to display in the MessageDialog
3007     */
3008    public static void showWarning(Shell parent, String title, String warnMsg) {
3009        String dlgTitlePrefix = "";
3010        String dlgTitleSuffix = (title == null) ? "" : title;
3011
3012        if (parent != null) {
3013            dlgTitlePrefix = parent.getText();
3014            if (dlgTitlePrefix.length() > 0)
3015                dlgTitlePrefix += " - ";
3016        }
3017
3018        MessageDialog.openWarning(parent, dlgTitlePrefix + dlgTitleSuffix, (warnMsg == null) ? "UNKNOWN" : warnMsg);
3019    }
3020}