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