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