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