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