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