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        log.trace("newInstance(Class = {}): finish", cls);
1099
1100        return instance;
1101    }
1102
1103    /**
1104     * Computes autocontrast parameters (gain equates to contrast and bias
1105     * equates to brightness) for integers.
1106     * <p>
1107     * The computation is based on the following scaling
1108     *
1109     * <pre>
1110     *      int_8       [0, 127]
1111     *      uint_8      [0, 255]
1112     *      int_16      [0, 32767]
1113     *      uint_16     [0, 65535]
1114     *      int_32      [0, 2147483647]
1115     *      uint_32     [0, 4294967295]
1116     *      int_64      [0, 9223372036854775807]
1117     *      uint_64     [0, 18446744073709551615] // Not supported.
1118     * </pre>
1119     *
1120     * @param data
1121     *            the raw data array of signed/unsigned integers
1122     * @param params
1123     *            the auto gain parameter. params[0]=gain, params[1]=bias,
1124     * @param isUnsigned
1125     *            the flag to indicate if the data array is unsigned integer.
1126     *
1127     * @return non-negative if successful; otherwise, returns negative
1128     */
1129    public static int autoContrastCompute(Object data, double[] params, boolean isUnsigned) {
1130        int retval = 1;
1131        long maxDataValue = 255;
1132        double[] minmax = new double[2];
1133
1134        // check parameters
1135        if ((data == null) || (params == null) || (Array.getLength(data) <= 0) || (params.length < 2)) {
1136            return -1;
1137        }
1138
1139        retval = autoContrastComputeMinMax(data, minmax);
1140
1141        // force the min_max method so we can look at the target grids data sets
1142        if ((retval < 0) || (minmax[1] - minmax[0] < 10)) {
1143            retval = findMinMax(data, minmax, null);
1144        }
1145
1146        if (retval < 0) {
1147            return -1;
1148        }
1149
1150        String cname = data.getClass().getName();
1151        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1152        switch (dname) {
1153            case 'B':
1154                maxDataValue = MAX_INT8;
1155                break;
1156            case 'S':
1157                maxDataValue = MAX_INT16;
1158                if (isUnsigned) {
1159                    maxDataValue = MAX_UINT8; // data was upgraded from unsigned byte
1160                }
1161                break;
1162            case 'I':
1163                maxDataValue = MAX_INT32;
1164                if (isUnsigned) {
1165                    maxDataValue = MAX_UINT16; // data was upgraded from unsigned short
1166                }
1167                break;
1168            case 'J':
1169                maxDataValue = MAX_INT64;
1170                if (isUnsigned) {
1171                    maxDataValue = MAX_UINT32; // data was upgraded from unsigned int
1172                }
1173                break;
1174            default:
1175                retval = -1;
1176                break;
1177        } // (dname)
1178
1179        if (minmax[0] == minmax[1]) {
1180            params[0] = 1.0;
1181            params[1] = 0.0;
1182        }
1183        else {
1184            // This histogram method has a tendency to stretch the
1185            // range of values to be a bit too big, so we can
1186            // account for this by adding and subtracting some percent
1187            // of the difference to the max/min values
1188            // to prevent the gain from going too high.
1189            double diff = minmax[1] - minmax[0];
1190            double newmax = (minmax[1] + (diff * 0.1));
1191            double newmin = (minmax[0] - (diff * 0.1));
1192
1193            if (newmax <= maxDataValue) {
1194                minmax[1] = newmax;
1195            }
1196
1197            if (newmin >= 0) {
1198                minmax[0] = newmin;
1199            }
1200
1201            params[0] = maxDataValue / (minmax[1] - minmax[0]);
1202            params[1] = -minmax[0];
1203        }
1204
1205        return retval;
1206    }
1207
1208    /**
1209     * Apply autocontrast parameters to the original data in place (destructive)
1210     *
1211     * @param dataIN
1212     *            the original data array of signed/unsigned integers
1213     * @param dataOUT
1214     *            the converted data array of signed/unsigned integers
1215     * @param params
1216     *            the auto gain parameter. params[0]=gain, params[1]=bias
1217     * @param minmax
1218     *            the data range. minmax[0]=min, minmax[1]=max
1219     * @param isUnsigned
1220     *            the flag to indicate if the data array is unsigned integer
1221     *
1222     * @return the data array with the auto contrast conversion; otherwise,
1223     *         returns null
1224     */
1225    public static Object autoContrastApply(Object dataIN, Object dataOUT, double[] params, double[] minmax,
1226            boolean isUnsigned) {
1227        int size = 0;
1228        double min = -MAX_INT64;
1229        double max = MAX_INT64;
1230
1231        if ((dataIN == null) || (params == null) || (params.length < 2)) {
1232            return null;
1233        }
1234
1235        if (minmax != null) {
1236            min = minmax[0];
1237            max = minmax[1];
1238        }
1239        // input and output array must be the same size
1240        size = Array.getLength(dataIN);
1241        if ((dataOUT != null) && (size != Array.getLength(dataOUT))) {
1242            return null;
1243        }
1244
1245        double gain = params[0];
1246        double bias = params[1];
1247        double valueOut;
1248        double valueIn;
1249        String cname = dataIN.getClass().getName();
1250        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1251
1252        switch (dname) {
1253            case 'B':
1254                byte[] bIn = (byte[]) dataIN;
1255                if (dataOUT == null) {
1256                    dataOUT = new byte[size];
1257                }
1258                byte[] bOut = (byte[]) dataOUT;
1259                byte bMax = (byte) MAX_INT8;
1260
1261                for (int i = 0; i < size; i++) {
1262                    valueIn = Math.max(bIn[i], min);
1263                    valueIn = Math.min(valueIn, max);
1264                    valueOut = (valueIn + bias) * gain;
1265                    valueOut = Math.max(valueOut, 0.0);
1266                    valueOut = Math.min(valueOut, bMax);
1267                    bOut[i] = (byte) valueOut;
1268                }
1269                break;
1270            case 'S':
1271                short[] sIn = (short[]) dataIN;
1272                if (dataOUT == null) {
1273                    dataOUT = new short[size];
1274                }
1275                short[] sOut = (short[]) dataOUT;
1276                short sMax = (short) MAX_INT16;
1277
1278                if (isUnsigned) {
1279                    sMax = (short) MAX_UINT8; // data was upgraded from unsigned byte
1280                }
1281
1282                for (int i = 0; i < size; i++) {
1283                    valueIn = Math.max(sIn[i], min);
1284                    valueIn = Math.min(valueIn, max);
1285                    valueOut = (valueIn + bias) * gain;
1286                    valueOut = Math.max(valueOut, 0.0);
1287                    valueOut = Math.min(valueOut, sMax);
1288                    sOut[i] = (byte) valueOut;
1289                }
1290                break;
1291            case 'I':
1292                int[] iIn = (int[]) dataIN;
1293                if (dataOUT == null) {
1294                    dataOUT = new int[size];
1295                }
1296                int[] iOut = (int[]) dataOUT;
1297                int iMax = (int) MAX_INT32;
1298                if (isUnsigned) {
1299                    iMax = (int) MAX_UINT16; // data was upgraded from unsigned short
1300                }
1301
1302                for (int i = 0; i < size; i++) {
1303                    valueIn = Math.max(iIn[i], min);
1304                    valueIn = Math.min(valueIn, max);
1305                    valueOut = (valueIn + bias) * gain;
1306                    valueOut = Math.max(valueOut, 0.0);
1307                    valueOut = Math.min(valueOut, iMax);
1308                    iOut[i] = (byte) valueOut;
1309                }
1310                break;
1311            case 'J':
1312                long[] lIn = (long[]) dataIN;
1313                if (dataOUT == null) {
1314                    dataOUT = new long[size];
1315                }
1316                long[] lOut = (long[]) dataOUT;
1317                long lMax = MAX_INT64;
1318                if (isUnsigned) {
1319                    lMax = MAX_UINT32; // data was upgraded from unsigned int
1320                }
1321
1322                for (int i = 0; i < size; i++) {
1323                    valueIn = Math.max(lIn[i], min);
1324                    valueIn = Math.min(valueIn, max);
1325                    valueOut = (valueIn + bias) * gain;
1326                    valueOut = Math.max(valueOut, 0.0);
1327                    valueOut = Math.min(valueOut, lMax);
1328                    lOut[i] = (byte) valueOut;
1329                }
1330                break;
1331            default:
1332                break;
1333        } // (dname)
1334
1335        return dataOUT;
1336    }
1337
1338    /**
1339     * Converts image raw data to bytes.
1340     *
1341     * The integer data is converted to byte data based on the following rule
1342     *
1343     * <pre>
1344     *         uint_8       x
1345     *         int_8       (x &amp; 0x7F) &lt;&lt; 1
1346     *         uint_16     (x &gt;&gt; 8) &amp; 0xFF
1347     *         int_16      (x &gt;&gt; 7) &amp; 0xFF
1348     *         uint_32     (x &gt;&gt; 24) &amp; 0xFF
1349     *         int_32      (x &gt;&gt; 23) &amp; 0xFF
1350     *         uint_64     (x &gt;&gt; 56) &amp; 0xFF
1351     *         int_64      (x &gt;&gt; 55) &amp; 0xFF
1352     * </pre>
1353     *
1354     * @param src
1355     *            the source data array of signed integers or unsigned shorts
1356     * @param dst
1357     *            the destination data array of bytes
1358     * @param isUnsigned
1359     *            the flag to indicate if the data array is unsigned integer.
1360     *
1361     * @return non-negative if successful; otherwise, returns negative
1362     */
1363    public static int autoContrastConvertImageBuffer(Object src, byte[] dst, boolean isUnsigned) {
1364        int retval = 0;
1365
1366        if ((src == null) || (dst == null) || (dst.length != Array.getLength(src))) {
1367            return -1;
1368        }
1369
1370        int size = dst.length;
1371        String cname = src.getClass().getName();
1372        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1373        switch (dname) {
1374            case 'B':
1375                byte[] bSrc = (byte[]) src;
1376                if (isUnsigned) {
1377                    for (int i = 0; i < size; i++) {
1378                        dst[i] = bSrc[i];
1379                    }
1380                }
1381                else {
1382                    for (int i = 0; i < size; i++) {
1383                        dst[i] = (byte) ((bSrc[i] & 0x7F) << 1);
1384                    }
1385                }
1386                break;
1387            case 'S':
1388                short[] sSrc = (short[]) src;
1389                if (isUnsigned) { // data was upgraded from unsigned byte
1390                    for (int i = 0; i < size; i++) {
1391                        dst[i] = (byte) sSrc[i];
1392                    }
1393                }
1394                else {
1395                    for (int i = 0; i < size; i++) {
1396                        dst[i] = (byte) ((sSrc[i] >> 7) & 0xFF);
1397                    }
1398                }
1399                break;
1400            case 'I':
1401                int[] iSrc = (int[]) src;
1402                if (isUnsigned) { // data was upgraded from unsigned short
1403                    for (int i = 0; i < size; i++) {
1404                        dst[i] = (byte) ((iSrc[i] >> 8) & 0xFF);
1405                    }
1406                }
1407                else {
1408                    for (int i = 0; i < size; i++) {
1409                        dst[i] = (byte) ((iSrc[i] >> 23) & 0xFF);
1410                    }
1411                }
1412                break;
1413            case 'J':
1414                long[] lSrc = (long[]) src;
1415                if (isUnsigned) { // data was upgraded from unsigned int
1416                    for (int i = 0; i < size; i++) {
1417                        dst[i] = (byte) ((lSrc[i] >> 24) & 0xFF);
1418                    }
1419                }
1420                else {
1421                    for (int i = 0; i < size; i++) {
1422                        dst[i] = (byte) ((lSrc[i] >> 55) & 0xFF);
1423                    }
1424                }
1425                break;
1426            default:
1427                retval = -1;
1428                break;
1429        } // (dname)
1430
1431        return retval;
1432    }
1433
1434    /**
1435     * Computes autocontrast parameters by
1436     *
1437     * <pre>
1438     *    min = mean - 3 * std.dev
1439     *    max = mean + 3 * std.dev
1440     * </pre>
1441     *
1442     * @param data
1443     *            the raw data array
1444     * @param minmax
1445     *            the min and max values.
1446     *
1447     * @return non-negative if successful; otherwise, returns negative
1448     */
1449    public static int autoContrastComputeMinMax(Object data, double[] minmax) {
1450        int retval = 1;
1451
1452        if ((data == null) || (minmax == null) || (Array.getLength(data) <= 0) || (Array.getLength(minmax) < 2)) {
1453            return -1;
1454        }
1455
1456        double[] avgstd = { 0, 0 };
1457        retval = computeStatistics(data, avgstd, null);
1458        if (retval < 0) {
1459            return retval;
1460        }
1461
1462        minmax[0] = avgstd[0] - 3.0 * avgstd[1];
1463        minmax[1] = avgstd[0] + 3.0 * avgstd[1];
1464
1465        return retval;
1466    }
1467
1468    /**
1469     * Finds the min and max values of the data array
1470     *
1471     * @param data
1472     *            the raw data array
1473     * @param minmax
1474     *            the mmin and max values of the array.
1475     * @param fillValue
1476     *            the missing value or fill value. Exclude this value when check
1477     *            for min/max
1478     *
1479     * @return non-negative if successful; otherwise, returns negative
1480     */
1481    public static int findMinMax(Object data, double[] minmax, Object fillValue) {
1482        int retval = 1;
1483
1484        if ((data == null) || (minmax == null) || (Array.getLength(data) <= 0) || (Array.getLength(minmax) < 2)) {
1485            return -1;
1486        }
1487
1488        int n = Array.getLength(data);
1489        double fill = 0.0;
1490        boolean hasFillValue = (fillValue != null && fillValue.getClass().isArray());
1491
1492        String cname = data.getClass().getName();
1493        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1494        log.trace("findMinMax() cname={} : dname={}", cname, dname);
1495
1496        minmax[0] = Float.MAX_VALUE;
1497        minmax[1] = -Float.MAX_VALUE;
1498
1499        switch (dname) {
1500            case 'B':
1501                byte[] b = (byte[]) data;
1502                minmax[0] = minmax[1] = b[0];
1503
1504                if (hasFillValue) fill = ((byte[]) fillValue)[0];
1505                for (int i = 0; i < n; i++) {
1506                    if (hasFillValue && b[i] == fill) continue;
1507                    if (minmax[0] > b[i]) {
1508                        minmax[0] = b[i];
1509                    }
1510                    if (minmax[1] < b[i]) {
1511                        minmax[1] = b[i];
1512                    }
1513                }
1514                break;
1515            case 'S':
1516                short[] s = (short[]) data;
1517                minmax[0] = minmax[1] = s[0];
1518
1519                if (hasFillValue) fill = ((short[]) fillValue)[0];
1520
1521                for (int i = 0; i < n; i++) {
1522                    if (hasFillValue && s[i] == fill) continue;
1523                    if (minmax[0] > s[i]) {
1524                        minmax[0] = s[i];
1525                    }
1526                    if (minmax[1] < s[i]) {
1527                        minmax[1] = s[i];
1528                    }
1529                }
1530                break;
1531            case 'I':
1532                int[] ia = (int[]) data;
1533                minmax[0] = minmax[1] = ia[0];
1534
1535                if (hasFillValue) fill = ((int[]) fillValue)[0];
1536
1537                for (int i = 0; i < n; i++) {
1538                    if (hasFillValue && ia[i] == fill) continue;
1539                    if (minmax[0] > ia[i]) {
1540                        minmax[0] = ia[i];
1541                    }
1542                    if (minmax[1] < ia[i]) {
1543                        minmax[1] = ia[i];
1544                    }
1545                }
1546                break;
1547            case 'J':
1548                long[] l = (long[]) data;
1549                minmax[0] = minmax[1] = l[0];
1550
1551                if (hasFillValue) fill = ((long[]) fillValue)[0];
1552                for (int i = 0; i < n; i++) {
1553                    if (hasFillValue && l[i] == fill) continue;
1554                    if (minmax[0] > l[i]) {
1555                        minmax[0] = l[i];
1556                    }
1557                    if (minmax[1] < l[i]) {
1558                        minmax[1] = l[i];
1559                    }
1560                }
1561                break;
1562            case 'F':
1563                float[] f = (float[]) data;
1564                minmax[0] = minmax[1] = f[0];
1565
1566                if (hasFillValue) fill = ((float[]) fillValue)[0];
1567                for (int i = 0; i < n; i++) {
1568                    if ((hasFillValue && f[i] == fill) || isNaNINF(f[i])) continue;
1569                    if (minmax[0] > f[i]) {
1570                        minmax[0] = f[i];
1571                    }
1572                    if (minmax[1] < f[i]) {
1573                        minmax[1] = f[i];
1574                    }
1575                }
1576
1577                break;
1578            case 'D':
1579                double[] d = (double[]) data;
1580                minmax[0] = minmax[1] = d[0];
1581
1582                if (hasFillValue) fill = ((double[]) fillValue)[0];
1583                for (int i = 0; i < n; i++) {
1584                    if ((hasFillValue && d[i] == fill) || isNaNINF(d[i])) continue;
1585
1586                    if (minmax[0] > d[i]) {
1587                        minmax[0] = d[i];
1588                    }
1589                    if (minmax[1] < d[i]) {
1590                        minmax[1] = d[i];
1591                    }
1592                }
1593                break;
1594            default:
1595                retval = -1;
1596                break;
1597        } // (dname)
1598
1599        return retval;
1600    }
1601
1602    /**
1603     * Finds the distribution of data values
1604     *
1605     * @param data
1606     *            the raw data array
1607     * @param dataDist
1608     *            the data distirbution.
1609     * @param minmax
1610     *            the data range
1611     *
1612     * @return non-negative if successful; otherwise, returns negative
1613     */
1614    public static int findDataDist(Object data, int[] dataDist, double[] minmax) {
1615        int retval = 0;
1616        double delt = 1;
1617
1618        if ((data == null) || (minmax == null) || dataDist == null) return -1;
1619
1620        int n = Array.getLength(data);
1621
1622        if (minmax[1] != minmax[0]) delt = (dataDist.length - 1) / (minmax[1] - minmax[0]);
1623
1624        for (int i = 0; i < dataDist.length; i++)
1625            dataDist[i] = 0;
1626
1627        int idx;
1628        double val;
1629        for (int i = 0; i < n; i++) {
1630            val = ((Number) Array.get(data, i)).doubleValue();
1631            if (val>=minmax[0] && val <=minmax[1]) {
1632                idx = (int) ((val - minmax[0]) * delt);
1633                dataDist[idx]++;
1634            } // don't count invalid values
1635        }
1636
1637        return retval;
1638    }
1639
1640    /**
1641     * Computes mean and standard deviation of a data array
1642     *
1643     * @param data
1644     *            the raw data array
1645     * @param avgstd
1646     *            the statistics: avgstd[0]=mean and avgstd[1]=stdev.
1647     * @param fillValue
1648     *            the missing value or fill value. Exclude this value when
1649     *            compute statistics
1650     *
1651     * @return non-negative if successful; otherwise, returns negative
1652     */
1653    public static int computeStatistics(Object data, double[] avgstd, Object fillValue) {
1654        int retval = 1;
1655        double sum = 0;
1656        double avg = 0.0;
1657        double var = 0.0;
1658        double diff = 0.0;
1659        double fill = 0.0;
1660
1661        if ((data == null) || (avgstd == null) || (Array.getLength(data) <= 0) || (Array.getLength(avgstd) < 2)) {
1662            return -1;
1663        }
1664
1665        int n = Array.getLength(data);
1666        boolean hasFillValue = (fillValue != null && fillValue.getClass().isArray());
1667
1668        String cname = data.getClass().getName();
1669        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1670        log.trace("computeStatistics() cname={} : dname={}", cname, dname);
1671
1672        int npoints = 0;
1673        switch (dname) {
1674            case 'B':
1675                byte[] b = (byte[]) data;
1676                if (hasFillValue) fill = ((byte[]) fillValue)[0];
1677                for (int i = 0; i < n; i++) {
1678                    if (hasFillValue && b[i] == fill) continue;
1679                    sum += b[i];
1680                    npoints++;
1681                }
1682                if (npoints > 0) {
1683                    avg = sum / npoints;
1684                    for (int i = 0; i < n; i++) {
1685                        if (hasFillValue && b[i] == fill)
1686                            continue;
1687                        diff = b[i] - avg;
1688                        var += diff * diff;
1689                    }
1690                }
1691                break;
1692            case 'S':
1693                short[] s = (short[]) data;
1694                if (hasFillValue) fill = ((short[]) fillValue)[0];
1695                for (int i = 0; i < n; i++) {
1696                    if (hasFillValue && s[i] == fill) continue;
1697                    sum += s[i];
1698                    npoints++;
1699                }
1700                if (npoints > 0) {
1701                    avg = sum / npoints;
1702                    for (int i = 0; i < n; i++) {
1703                        if (hasFillValue && s[i] == fill)
1704                            continue;
1705                        diff = s[i] - avg;
1706                        var += diff * diff;
1707                    }
1708                }
1709                break;
1710            case 'I':
1711                int[] ia = (int[]) data;
1712                if (hasFillValue) fill = ((int[]) fillValue)[0];
1713                for (int i = 0; i < n; i++) {
1714                    if (hasFillValue && ia[i] == fill) continue;
1715                    sum += ia[i];
1716                    npoints++;
1717                }
1718                if (npoints > 0) {
1719                    avg = sum / npoints;
1720                    for (int i = 0; i < n; i++) {
1721                        if (hasFillValue && ia[i] == fill)
1722                            continue;
1723                        diff = ia[i] - avg;
1724                        var += diff * diff;
1725                    }
1726                }
1727                break;
1728            case 'J':
1729                long[] l = (long[]) data;
1730                if (hasFillValue) fill = ((long[]) fillValue)[0];
1731                for (int i = 0; i < n; i++) {
1732                    if (hasFillValue && l[i] == fill) continue;
1733                    sum += l[i];
1734                    npoints++;
1735                }
1736                if (npoints > 0) {
1737                    avg = sum / npoints;
1738                    for (int i = 0; i < n; i++) {
1739                        if (hasFillValue && l[i] == fill)
1740                            continue;
1741                        diff = l[i] - avg;
1742                        var += diff * diff;
1743                    }
1744                }
1745                break;
1746            case 'F':
1747                float[] f = (float[]) data;
1748                if (hasFillValue) fill = ((float[]) fillValue)[0];
1749                for (int i = 0; i < n; i++) {
1750                    if (hasFillValue && f[i] == fill) continue;
1751                    sum += f[i];
1752                    npoints++;
1753                }
1754                if (npoints > 0) {
1755                    avg = sum / npoints;
1756                    for (int i = 0; i < n; i++) {
1757                        if (hasFillValue && f[i] == fill)
1758                            continue;
1759                        diff = f[i] - avg;
1760                        var += diff * diff;
1761                    }
1762                }
1763                break;
1764            case 'D':
1765                double[] d = (double[]) data;
1766                if (hasFillValue) fill = ((double[]) fillValue)[0];
1767                for (int i = 0; i < n; i++) {
1768                    if (hasFillValue && d[i] == fill) continue;
1769                    sum += d[i];
1770                    npoints++;
1771                }
1772                if (npoints > 0) {
1773                    avg = sum / npoints;
1774                    for (int i = 0; i < n; i++) {
1775                        if (hasFillValue && d[i] == fill)
1776                            continue;
1777                        diff = d[i] - avg;
1778                        var += diff * diff;
1779                    }
1780                }
1781                break;
1782            default:
1783                retval = -1;
1784                break;
1785        } // (dname)
1786
1787        if (npoints <= 1) {
1788            if (npoints < 1) avgstd[0] = fill;
1789            avgstd[1] = 0;
1790        }
1791        else {
1792            avgstd[0] = avg;
1793            avgstd[1] = Math.sqrt(var / (npoints - 1));
1794        }
1795
1796        return retval;
1797    }
1798
1799    public static void saveAsBinary(DataOutputStream out, Object data, ByteOrder order) throws Exception {
1800        String cname = data.getClass().getName();
1801        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1802        ByteBuffer bb = null;
1803
1804        int size = Array.getLength(data);
1805
1806        if (dname == 'B') {
1807            byte[] bdata = (byte[]) data;
1808
1809            bb = ByteBuffer.allocate(BYTE_BUFFER_SIZE);
1810            bb.order(order);
1811
1812            int remainingSize = size - BYTE_BUFFER_SIZE;
1813            int allocValue = 0;
1814            int iterationNumber = 0;
1815            do {
1816                if (remainingSize <= 0) {
1817                    allocValue = remainingSize + BYTE_BUFFER_SIZE;
1818                }
1819                else {
1820                    allocValue = BYTE_BUFFER_SIZE;
1821                }
1822                bb.clear();
1823                bb.put(bdata, (iterationNumber * BYTE_BUFFER_SIZE), allocValue);
1824                out.write(bb.array(), 0, allocValue);
1825                remainingSize = remainingSize - BYTE_BUFFER_SIZE;
1826                iterationNumber++;
1827            } while (remainingSize > -BYTE_BUFFER_SIZE);
1828
1829            out.flush();
1830            out.close();
1831        }
1832        else if (dname == 'S') {
1833            short[] sdata = (short[]) data;
1834            bb = ByteBuffer.allocate(SHORT_BUFFER_SIZE * 2);
1835            bb.order(order);
1836
1837            ShortBuffer sb = bb.asShortBuffer();
1838            int remainingSize = size - SHORT_BUFFER_SIZE;
1839            int allocValue = 0;
1840            int iterationNumber = 0;
1841            do {
1842                if (remainingSize <= 0) {
1843                    allocValue = remainingSize + SHORT_BUFFER_SIZE;
1844                }
1845                else {
1846                    allocValue = SHORT_BUFFER_SIZE;
1847                }
1848                bb.clear();
1849                sb.clear();
1850                sb.put(sdata, (iterationNumber * SHORT_BUFFER_SIZE), allocValue);
1851                out.write(bb.array(), 0, allocValue * 2);
1852                remainingSize = remainingSize - SHORT_BUFFER_SIZE;
1853                iterationNumber++;
1854            } while (remainingSize > -SHORT_BUFFER_SIZE);
1855
1856            out.flush();
1857            out.close();
1858        }
1859        else if (dname == 'I') {
1860            int[] idata = (int[]) data;
1861            bb = ByteBuffer.allocate(INT_BUFFER_SIZE * 4);
1862            bb.order(order);
1863
1864            IntBuffer ib = bb.asIntBuffer();
1865            int remainingSize = size - INT_BUFFER_SIZE;
1866            int allocValue = 0;
1867            int iterationNumber = 0;
1868            do {
1869                if (remainingSize <= 0) {
1870                    allocValue = remainingSize + INT_BUFFER_SIZE;
1871                }
1872                else {
1873                    allocValue = INT_BUFFER_SIZE;
1874                }
1875                bb.clear();
1876                ib.clear();
1877                ib.put(idata, (iterationNumber * INT_BUFFER_SIZE), allocValue);
1878                out.write(bb.array(), 0, allocValue * 4);
1879                remainingSize = remainingSize - INT_BUFFER_SIZE;
1880                iterationNumber++;
1881            } while (remainingSize > -INT_BUFFER_SIZE);
1882
1883            out.flush();
1884            out.close();
1885        }
1886        else if (dname == 'J') {
1887            long[] ldata = (long[]) data;
1888
1889            bb = ByteBuffer.allocate(LONG_BUFFER_SIZE * 8);
1890            bb.order(order);
1891
1892            LongBuffer lb = bb.asLongBuffer();
1893            int remainingSize = size - LONG_BUFFER_SIZE;
1894            int allocValue = 0;
1895            int iterationNumber = 0;
1896            do {
1897                if (remainingSize <= 0) {
1898                    allocValue = remainingSize + LONG_BUFFER_SIZE;
1899                }
1900                else {
1901                    allocValue = LONG_BUFFER_SIZE;
1902                }
1903                bb.clear();
1904                lb.clear();
1905                lb.put(ldata, (iterationNumber * LONG_BUFFER_SIZE), allocValue);
1906                out.write(bb.array(), 0, allocValue * 8);
1907                remainingSize = remainingSize - LONG_BUFFER_SIZE;
1908                iterationNumber++;
1909            } while (remainingSize > -LONG_BUFFER_SIZE);
1910
1911            out.flush();
1912            out.close();
1913        }
1914        else if (dname == 'F') {
1915            float[] fdata = (float[]) data;
1916
1917            bb = ByteBuffer.allocate(FLOAT_BUFFER_SIZE * 4);
1918            bb.order(order);
1919
1920            FloatBuffer fb = bb.asFloatBuffer();
1921            int remainingSize = size - FLOAT_BUFFER_SIZE;
1922            int allocValue = 0;
1923            int iterationNumber = 0;
1924            do {
1925                if (remainingSize <= 0) {
1926                    allocValue = remainingSize + FLOAT_BUFFER_SIZE;
1927                }
1928                else {
1929                    allocValue = FLOAT_BUFFER_SIZE;
1930                }
1931                bb.clear();
1932                fb.clear();
1933                fb.put(fdata, (iterationNumber * FLOAT_BUFFER_SIZE), allocValue);
1934                out.write(bb.array(), 0, allocValue * 4);
1935                remainingSize = remainingSize - FLOAT_BUFFER_SIZE;
1936                iterationNumber++;
1937            } while (remainingSize > -FLOAT_BUFFER_SIZE);
1938
1939            out.flush();
1940            out.close();
1941        }
1942        else if (dname == 'D') {
1943            double[] ddata = (double[]) data;
1944
1945            bb = ByteBuffer.allocate(DOUBLE_BUFFER_SIZE * 8);
1946            bb.order(order);
1947
1948            DoubleBuffer db = bb.asDoubleBuffer();
1949            int remainingSize = size - DOUBLE_BUFFER_SIZE;
1950            int allocValue = 0;
1951            int iterationNumber = 0;
1952            do {
1953                if (remainingSize <= 0) {
1954                    allocValue = remainingSize + DOUBLE_BUFFER_SIZE;
1955                }
1956                else {
1957                    allocValue = DOUBLE_BUFFER_SIZE;
1958                }
1959                bb.clear();
1960                db.clear();
1961                db.put(ddata, (iterationNumber * DOUBLE_BUFFER_SIZE), allocValue);
1962                out.write(bb.array(), 0, allocValue * 8);
1963                remainingSize = remainingSize - DOUBLE_BUFFER_SIZE;
1964                iterationNumber++;
1965            } while (remainingSize > -DOUBLE_BUFFER_SIZE);
1966
1967            out.flush();
1968            out.close();
1969        }
1970    }
1971
1972    /**
1973     * Reads data from a binary file into a buffer.
1974     *
1975     * @param dataOut
1976     *            the output stream
1977     * @param fileName
1978     *            the file to read binary data from
1979     * @param order
1980     *            the new byte order, either BIG_ENDIAN or LITTLE_ENDIAN
1981     *
1982     * @return true if successful; otherwise, false.
1983     */
1984    public static boolean getBinaryDataFromFile(Object dataOut, String fileName, ByteOrder order) {
1985        if (dataOut == null) return false;
1986
1987        String fname = fileName;
1988        BufferedInputStream in = null;
1989        ByteBuffer byteBuffer = null;
1990        boolean valChanged = false;
1991
1992        try (FileInputStream inputFile = new FileInputStream(fname)) {
1993            long fileSize = inputFile.getChannel().size();
1994            in = new BufferedInputStream(inputFile);
1995
1996            int datasetSize = Array.getLength(dataOut);
1997            String cname = dataOut.getClass().getName();
1998            char dname = cname.charAt(cname.lastIndexOf('[') + 1);
1999
2000            if (dname == 'B') {
2001                long datasetByteSize = datasetSize;
2002                byteBuffer = ByteBuffer.allocate(BYTE_BUFFER_SIZE);
2003                byteBuffer.order(order);
2004
2005                int bufferSize = (int) Math.min(fileSize, datasetByteSize);
2006
2007                int remainingSize = bufferSize - (BYTE_BUFFER_SIZE);
2008                int allocValue = 0;
2009                int iterationNumber = 0;
2010                byte[] byteArray = new byte[BYTE_BUFFER_SIZE];
2011                do {
2012                    if (remainingSize <= 0) {
2013                        allocValue = remainingSize + (BYTE_BUFFER_SIZE);
2014                    }
2015                    else {
2016                        allocValue = (BYTE_BUFFER_SIZE);
2017                    }
2018
2019                    in.read(byteBuffer.array(), 0, allocValue);
2020
2021                    byteBuffer.get(byteArray, 0, allocValue);
2022                    System.arraycopy(byteArray, 0, dataOut, (iterationNumber * BYTE_BUFFER_SIZE), allocValue);
2023                    byteBuffer.clear();
2024                    remainingSize = remainingSize - (BYTE_BUFFER_SIZE);
2025                    iterationNumber++;
2026                } while (remainingSize > -(BYTE_BUFFER_SIZE));
2027
2028                valChanged = true;
2029            }
2030            else if (dname == 'S') {
2031                long datasetShortSize = (long) datasetSize * 2;
2032                byteBuffer = ByteBuffer.allocate(SHORT_BUFFER_SIZE * 2);
2033                byteBuffer.order(order);
2034
2035                int bufferSize = (int) Math.min(fileSize, datasetShortSize);
2036                int remainingSize = bufferSize - (SHORT_BUFFER_SIZE * 2);
2037                int allocValue = 0;
2038                int iterationNumber = 0;
2039                ShortBuffer sb = byteBuffer.asShortBuffer();
2040                short[] shortArray = new short[SHORT_BUFFER_SIZE];
2041
2042                do {
2043                    if (remainingSize <= 0) {
2044                        allocValue = remainingSize + (SHORT_BUFFER_SIZE * 2);
2045                    }
2046                    else {
2047                        allocValue = (SHORT_BUFFER_SIZE * 2);
2048                    }
2049                    in.read(byteBuffer.array(), 0, allocValue);
2050                    sb.get(shortArray, 0, allocValue / 2);
2051                    System.arraycopy(shortArray, 0, dataOut, (iterationNumber * SHORT_BUFFER_SIZE), allocValue / 2);
2052                    byteBuffer.clear();
2053                    sb.clear();
2054                    remainingSize = remainingSize - (SHORT_BUFFER_SIZE * 2);
2055                    iterationNumber++;
2056                } while (remainingSize > -(SHORT_BUFFER_SIZE * 2));
2057
2058                valChanged = true;
2059            }
2060            else if (dname == 'I') {
2061                long datasetIntSize = (long) datasetSize * 4;
2062                byteBuffer = ByteBuffer.allocate(INT_BUFFER_SIZE * 4);
2063                byteBuffer.order(order);
2064
2065                int bufferSize = (int) Math.min(fileSize, datasetIntSize);
2066                int remainingSize = bufferSize - (INT_BUFFER_SIZE * 4);
2067                int allocValue = 0;
2068                int iterationNumber = 0;
2069                int[] intArray = new int[INT_BUFFER_SIZE];
2070                byte[] tmpBuf = byteBuffer.array();
2071                IntBuffer ib = byteBuffer.asIntBuffer();
2072
2073                do {
2074                    if (remainingSize <= 0) {
2075                        allocValue = remainingSize + (INT_BUFFER_SIZE * 4);
2076                    }
2077                    else {
2078                        allocValue = (INT_BUFFER_SIZE * 4);
2079                    }
2080                    in.read(tmpBuf, 0, allocValue);
2081                    ib.get(intArray, 0, allocValue / 4);
2082                    System.arraycopy(intArray, 0, dataOut, (iterationNumber * INT_BUFFER_SIZE), allocValue / 4);
2083                    byteBuffer.clear();
2084                    ib.clear();
2085                    remainingSize = remainingSize - (INT_BUFFER_SIZE * 4);
2086                    iterationNumber++;
2087                } while (remainingSize > -(INT_BUFFER_SIZE * 4));
2088
2089                valChanged = true;
2090            }
2091            else if (dname == 'J') {
2092                long datasetLongSize = (long) datasetSize * 8;
2093                byteBuffer = ByteBuffer.allocate(LONG_BUFFER_SIZE * 8);
2094                byteBuffer.order(order);
2095
2096                int bufferSize = (int) Math.min(fileSize, datasetLongSize);
2097                int remainingSize = bufferSize - (LONG_BUFFER_SIZE * 8);
2098                int allocValue = 0;
2099                int iterationNumber = 0;
2100                long[] longArray = new long[LONG_BUFFER_SIZE];
2101                LongBuffer lb = byteBuffer.asLongBuffer();
2102
2103                do {
2104                    if (remainingSize <= 0) {
2105                        allocValue = remainingSize + (LONG_BUFFER_SIZE * 8);
2106                    }
2107                    else {
2108                        allocValue = (LONG_BUFFER_SIZE * 8);
2109                    }
2110
2111                    in.read(byteBuffer.array(), 0, allocValue);
2112                    lb.get(longArray, 0, allocValue / 8);
2113                    System.arraycopy(longArray, 0, dataOut, (iterationNumber * LONG_BUFFER_SIZE), allocValue / 8);
2114                    byteBuffer.clear();
2115                    lb.clear();
2116                    remainingSize = remainingSize - (LONG_BUFFER_SIZE * 8);
2117                    iterationNumber++;
2118                } while (remainingSize > -(LONG_BUFFER_SIZE * 8));
2119
2120                valChanged = true;
2121            }
2122            else if (dname == 'F') {
2123                long datasetFloatSize = (long) datasetSize * 4;
2124                byteBuffer = ByteBuffer.allocate(FLOAT_BUFFER_SIZE * 4);
2125                byteBuffer.order(order);
2126
2127                int bufferSize = (int) Math.min(fileSize, datasetFloatSize);
2128                int remainingSize = bufferSize - (FLOAT_BUFFER_SIZE * 4);
2129                int allocValue = 0;
2130                int iterationNumber = 0;
2131                FloatBuffer fb = byteBuffer.asFloatBuffer();
2132                float[] floatArray = new float[FLOAT_BUFFER_SIZE];
2133                do {
2134                    if (remainingSize <= 0) {
2135                        allocValue = remainingSize + (FLOAT_BUFFER_SIZE * 4);
2136                    }
2137                    else {
2138                        allocValue = (FLOAT_BUFFER_SIZE * 4);
2139                    }
2140
2141                    in.read(byteBuffer.array(), 0, allocValue);
2142                    fb.get(floatArray, 0, allocValue / 4);
2143                    System.arraycopy(floatArray, 0, dataOut, (iterationNumber * FLOAT_BUFFER_SIZE), allocValue / 4);
2144                    byteBuffer.clear();
2145                    fb.clear();
2146                    remainingSize = remainingSize - (FLOAT_BUFFER_SIZE * 4);
2147                    iterationNumber++;
2148                } while (remainingSize > -(FLOAT_BUFFER_SIZE * 4));
2149
2150                valChanged = true;
2151            }
2152            else if (dname == 'D') {
2153                long datasetDoubleSize = (long) datasetSize * 8;
2154                byteBuffer = ByteBuffer.allocate(DOUBLE_BUFFER_SIZE * 8);
2155                byteBuffer.order(order);
2156
2157                int bufferSize = (int) Math.min(fileSize, datasetDoubleSize);
2158                int remainingSize = bufferSize - (DOUBLE_BUFFER_SIZE * 8);
2159                int allocValue = 0;
2160                int iterationNumber = 0;
2161                DoubleBuffer db = byteBuffer.asDoubleBuffer();
2162                double[] doubleArray = new double[DOUBLE_BUFFER_SIZE];
2163
2164                do {
2165                    if (remainingSize <= 0) {
2166                        allocValue = remainingSize + (DOUBLE_BUFFER_SIZE * 8);
2167                    }
2168                    else {
2169                        allocValue = (DOUBLE_BUFFER_SIZE * 8);
2170                    }
2171
2172                    in.read(byteBuffer.array(), 0, allocValue);
2173                    db.get(doubleArray, 0, allocValue / 8);
2174                    System.arraycopy(doubleArray, 0, dataOut, (iterationNumber * DOUBLE_BUFFER_SIZE), allocValue / 8);
2175                    byteBuffer.clear();
2176                    db.clear();
2177                    remainingSize = remainingSize - (DOUBLE_BUFFER_SIZE * 8);
2178                    iterationNumber++;
2179                } while (remainingSize > -(DOUBLE_BUFFER_SIZE * 8));
2180
2181                valChanged = true;
2182            }
2183        }
2184        catch (Exception es) {
2185            es.printStackTrace();
2186        }
2187        finally {
2188            try {
2189                in.close();
2190            }
2191            catch (IOException ex) {
2192                // Empty on purpose
2193            }
2194        }
2195
2196        return valChanged;
2197    }
2198
2199    /**
2200     * Returns a string representation of the long argument as an unsigned
2201     * integer in base 2. This is different from Long.toBinaryString(long i).
2202     * This function add padding (0's) to the string based on the nbytes. For
2203     * example, if v=15, nbytes=1, the string will be "00001111".
2204     *
2205     * @param v
2206     *            the long value
2207     * @param nbytes
2208     *            number of bytes in the integer
2209     *
2210     * @return the string representation of the unsigned long value represented
2211     *         by the argument in binary (base 2).
2212     */
2213    public static final String toBinaryString(long v, int nbytes) {
2214        if (nbytes <= 0) return null;
2215
2216        int nhex = nbytes * 2;
2217        short[] hex = new short[nhex];
2218
2219        for (int i = 0; i < nhex; i++)
2220            hex[i] = (short) (0x0F & (v >> (i * 4)));
2221
2222        StringBuilder sb = new StringBuilder();
2223        boolean isEven = true;
2224        for (int i = nhex - 1; i >= 0; i--) {
2225            if (isEven && i < nhex - 1) sb.append(" ");
2226            isEven = !isEven; // toggle
2227
2228            switch (hex[i]) {
2229                case 0:
2230                    sb.append("0000");
2231                    break;
2232                case 1:
2233                    sb.append("0001");
2234                    break;
2235                case 2:
2236                    sb.append("0010");
2237                    break;
2238                case 3:
2239                    sb.append("0011");
2240                    break;
2241                case 4:
2242                    sb.append("0100");
2243                    break;
2244                case 5:
2245                    sb.append("0101");
2246                    break;
2247                case 6:
2248                    sb.append("0110");
2249                    break;
2250                case 7:
2251                    sb.append("0111");
2252                    break;
2253                case 8:
2254                    sb.append("1000");
2255                    break;
2256                case 9:
2257                    sb.append("1001");
2258                    break;
2259                case 10:
2260                    sb.append("1010");
2261                    break;
2262                case 11:
2263                    sb.append("1011");
2264                    break;
2265                case 12:
2266                    sb.append("1100");
2267                    break;
2268                case 13:
2269                    sb.append("1101");
2270                    break;
2271                case 14:
2272                    sb.append("1110");
2273                    break;
2274                case 15:
2275                    sb.append("1111");
2276                    break;
2277                default:
2278                    break;
2279            }
2280        }
2281
2282        return sb.toString();
2283    }
2284
2285    public static final String toBinaryString(BigInteger v, int nbytes) {
2286        StringBuilder sb = new StringBuilder();
2287        String val = String.format("%" + (8 * nbytes) + "s", v.toString(2)).replace(" ", "0").toUpperCase();
2288
2289        // Insert spacing
2290        for (int i = 0; i < nbytes; i++) {
2291            sb.append(val.substring(i * nbytes, nbytes * (i + 1)));
2292            if (i < nbytes - 1) sb.append(" ");
2293        }
2294
2295        return sb.toString();
2296    }
2297
2298    static final char[] HEXCHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
2299
2300    /**
2301     * Returns a string representation of the long argument as an unsigned integer in base 16. This
2302     * is different from Long.toHexString(long i). This function add padding (0's) to the string
2303     * based on the nbytes. For example, if v=42543, nbytes=4, the string will be "0000A62F".
2304     *
2305     * @param v
2306     *            the long value
2307     * @param nbytes
2308     *            number of bytes in the integer
2309     * @return the string representation of the unsigned long value represented by the argument in
2310     *         hexadecimal (base 16).
2311     */
2312    public static final String toHexString (long v, int nbytes) {
2313        if (nbytes <= 0) return null;
2314
2315        int nhex = nbytes * 2;
2316        short[] hex = new short[nhex];
2317
2318        for (int i = 0; i < nhex; i++) {
2319            hex[i] = (short) (0x0F & (v >> (i * 4)));
2320        }
2321
2322        StringBuilder sb = new StringBuilder();
2323        for (int i = nhex - 1; i >= 0; i--) {
2324            sb.append(HEXCHARS[hex[i]]);
2325        }
2326
2327        return sb.toString();
2328    }
2329
2330    /**
2331     * Returns a string representation of the BigInteger argument as an unsigned integer in base 16.
2332     * This is different from BigInteger.toString(16). This function adds padding (0's) to the string
2333     * based on the nbytes. For example, if v=42543, nbytes=4, the string will be "0000A62F".
2334     *
2335     * @param v
2336     *            the BigInteger value
2337     * @param nbytes
2338     *            number of bytes in the integer
2339     * @return the string representation of the unsigned long value represented by the argument in
2340     *         hexadecimal (base 16).
2341     */
2342    public static final String toHexString (BigInteger v, int nbytes) {
2343        return String.format("%" + (2 * nbytes) + "s", v.toString(16)).replace(" ", "0").toUpperCase();
2344    }
2345
2346    /**
2347     * Apply bitmask to a data array.
2348     *
2349     * @param theData
2350     *            the data array which the bitmask is applied to.
2351     * @param theMask
2352     *            the bitmask to be applied to the data array.
2353     * @param op
2354     *            the bitmask op to be applied
2355     *
2356     * @return true if bitmask is applied successfully; otherwise, false.
2357     */
2358    public static final boolean applyBitmask(Object theData, BitSet theMask, ViewProperties.BITMASK_OP op) {
2359        if (     theData == null
2360            || !(theData instanceof Array)
2361            || ((theData instanceof Array) && (Array.getLength(theData) <= 0))
2362            ||   theMask == null) return false;
2363
2364        char nt = '0';
2365        String cName = theData.getClass().getName();
2366        int cIndex = cName.lastIndexOf('[');
2367        if (cIndex >= 0) {
2368            nt = cName.charAt(cIndex + 1);
2369        }
2370
2371        // only deal with 8/16/32/64 bit datasets
2372        if (!(nt == 'B' || nt == 'S' || nt == 'I' || nt == 'J')) return false;
2373
2374        long bmask = 0;
2375        long theValue = 0;
2376        long packedValue = 0;
2377
2378        int nbits = theMask.length();
2379        int len = Array.getLength(theData);
2380
2381        for (int i = 0; i < nbits; i++) {
2382            if (theMask.get(i)) bmask += 1 << i;
2383        }
2384
2385        for (int i = 0; i < len; i++) {
2386            if (nt == 'B')
2387                theValue = ((byte[]) theData)[i] & bmask;
2388            else if (nt == 'S')
2389                theValue = ((short[]) theData)[i] & bmask;
2390            else if (nt == 'I')
2391                theValue = ((int[]) theData)[i] & bmask;
2392            else if (nt == 'J')
2393                theValue = ((long[]) theData)[i] & bmask;
2394
2395            // apply bitmask only
2396            if (op == BITMASK_OP.AND)
2397                packedValue = theValue;
2398            else {
2399                // extract bits
2400                packedValue = 0;
2401                int bitPosition = 0;
2402
2403                for (int j = 0; j < nbits; j++) {
2404                    if (theMask.get(j)) {
2405                        long bitValue = (theValue & 1);
2406                        packedValue += (bitValue << bitPosition);
2407                        bitPosition++;
2408                    }
2409                    // move to the next bit
2410                    theValue = theValue >> 1;
2411                }
2412            }
2413
2414            if (nt == 'B')
2415                ((byte[]) theData)[i] = (byte) packedValue;
2416            else if (nt == 'S')
2417                ((short[]) theData)[i] = (short) packedValue;
2418            else if (nt == 'I')
2419                ((int[]) theData)[i] = (int) packedValue;
2420            else if (nt == 'J')
2421                ((long[]) theData)[i] = packedValue;
2422        } // (int i = 0; i < len; i++)
2423
2424        return true;
2425    } /* public static final boolean applyBitmask() */
2426
2427    /**
2428     * Read HDF5 user block data into byte array.
2429     *
2430     * @param filename the HDF5 file from which to get the user block
2431     *
2432     * @return a byte array of user block, or null if there is user data.
2433     */
2434    public static byte[] getHDF5UserBlock(String filename) {
2435        byte[] userBlock = null;
2436
2437        try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
2438            byte[] header = new byte[8];
2439            long fileSize = raf.length();
2440
2441            // The super block is located by searching for the HDF5 file signature
2442            // at byte offset 0, byte offset 512 and at successive locations in the
2443            // file, each a multiple of two of the previous location, i.e. 0, 512,
2444            // 1024, 2048, etc
2445            long offset = 0;
2446            boolean ish5 = false;
2447            while (offset < fileSize) {
2448                raf.seek(offset);
2449                raf.read(header);
2450
2451                if ((header[0] == -119) && (header[1] == 72) && (header[2] == 68) && (header[3] == 70) && (header[4] == 13) && (header[5] == 10) && (header[6] == 26)
2452                        && (header[7] == 10)) {
2453                    ish5 = true;
2454                    break; // find the end of user block
2455                }
2456                else {
2457                    ish5 = false;
2458                    if (offset == 0) {
2459                        offset = 512;
2460                    }
2461                    else {
2462                        offset *= 2;
2463                    }
2464                }
2465            }
2466
2467            if (!ish5 || (offset == 0)) {
2468                return null;
2469            }
2470
2471            int blockSize = (int) offset;
2472            userBlock = new byte[blockSize];
2473            raf.seek(0);
2474            raf.read(userBlock, 0, blockSize);
2475        }
2476        catch (Exception ex) {
2477            userBlock = null;
2478        }
2479
2480        return userBlock;
2481    }
2482
2483    /**
2484     * Write HDF5 user block data into byte array.
2485     *
2486     * @param fin the input filename
2487     * @param fout the output filename
2488     * @param buf  the data to write into the user block
2489     *
2490     * @return a byte array of user block, or null if there is user data.
2491     */
2492    public static boolean setHDF5UserBlock(String fin, String fout, byte[] buf) {
2493        boolean ish5 = false;
2494
2495        if ((buf == null) || (buf.length <= 0)) {
2496            return false;
2497        }
2498
2499        File tmpFile = new File(fin);
2500        if (!tmpFile.exists()) {
2501            return false;
2502        }
2503
2504        long offset = 0;
2505        // find the end of user block for the input file
2506        try (RandomAccessFile raf = new RandomAccessFile(fin, "r")) {
2507            byte[] header = new byte[8];
2508            long fileSize = raf.length();
2509
2510            // The super block is located by searching for the HDF5 file signature
2511            // at byte offset 0, byte offset 512 and at successive locations in the
2512            // file, each a multiple of two of the previous location, i.e. 0, 512,
2513            // 1024, 2048, etc
2514            while (offset < fileSize) {
2515                raf.seek(offset);
2516                raf.read(header);
2517
2518                if ((header[0] == -119) && (header[1] == 72) && (header[2] == 68) && (header[3] == 70) && (header[4] == 13) && (header[5] == 10) && (header[6] == 26)
2519                        && (header[7] == 10)) {
2520                    ish5 = true;
2521                    break;
2522                }
2523                else {
2524                    ish5 = false;
2525                    if (offset == 0) {
2526                        offset = 512;
2527                    }
2528                    else {
2529                        offset *= 2;
2530                    }
2531                }
2532            }
2533        }
2534        catch (Exception ex) {
2535            return false;
2536        }
2537
2538        if (!ish5) {
2539            return false;
2540        }
2541
2542        int length = 0;
2543        int bsize = 1024;
2544        byte[] buffer;
2545
2546        try (BufferedInputStream bi = new BufferedInputStream(new FileInputStream(fin))) {
2547            try (BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(fout))) {
2548                // skip the header of original file
2549                try {
2550                    long count = bi.skip(offset);
2551                    if (count != offset)
2552                        log.debug("file skip actual:{} req:{}", count, offset);
2553                }
2554                catch (Exception ex) {
2555                    // Empty on purpose
2556                }
2557
2558                // write the header into the new file
2559                try {
2560                    bo.write(buf, 0, buf.length);
2561                }
2562                catch (Exception ex) {
2563                    // Empty on purpose
2564                }
2565
2566                // The super block space is allocated by offset 0, 512, 1024, 2048, etc
2567                offset = 512;
2568                while (offset < buf.length) {
2569                    offset *= 2;
2570                }
2571                int padSize = (int) (offset - buf.length);
2572                if (padSize > 0) {
2573                    byte[] padBuf = new byte[padSize];
2574                    try {
2575                        bo.write(padBuf, 0, padSize);
2576                    }
2577                    catch (Exception ex) {
2578                        // Empty on purpose
2579                    }
2580                }
2581
2582                // copy the hdf5 file content from input file to the output file
2583                buffer = new byte[bsize];
2584                try {
2585                    length = bi.read(buffer, 0, bsize);
2586                }
2587                catch (Exception ex) {
2588                    length = 0;
2589                }
2590                while (length > 0) {
2591                    try {
2592                        bo.write(buffer, 0, length);
2593                        length = bi.read(buffer, 0, bsize);
2594                    }
2595                    catch (Exception ex) {
2596                        length = 0;
2597                    }
2598                }
2599
2600                try {
2601                    bo.flush();
2602                }
2603                catch (Exception ex) {
2604                    // Empty on purpose
2605                }
2606            }
2607            catch (Exception ex) {
2608                return false;
2609            }
2610        }
2611        catch (Exception ex) {
2612            return false;
2613        }
2614        return true;
2615    }
2616
2617    /**
2618     * look at the first 4 bytes of the file to see if it is an HDF4 file.
2619     * byte[0]=14, byte[1]=3, byte[2]=19, byte[3]=1 or if it is a netCDF file
2620     * byte[0]=67, byte[1]=68, byte[2]=70, byte[3]=1
2621     *
2622     * @param filename the file to test if HDF4
2623     *
2624     * @return true if the file is of type HDF4
2625     */
2626    public static boolean isHDF4(String filename) {
2627        boolean ish4 = false;
2628
2629        try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
2630            byte[] header = new byte[4];
2631            raf.read(header);
2632
2633            if ((header[0] == 14) && (header[1] == 3) && (header[2] == 19) && (header[3] == 1)) {
2634                ish4 = true;
2635            }
2636            else {
2637                ish4 = false;
2638            }
2639        }
2640        catch (Exception ex) {
2641            return false;
2642        }
2643
2644        return ish4;
2645    }
2646
2647    /**
2648     * look at the first 8 bytes of the file to see if it is an HDF5 file.
2649     * byte[0]=-199 which is 137 in unsigned byte, byte[1]=72, byte[2]=68,
2650     * byte[3]=70, byte[4]=13, byte[5]=10, byte[6]=26, byte[7]=10
2651     *
2652     * @param filename the file to test if HDF5
2653     *
2654     * @return true if the file is of type HDF5
2655     */
2656    public static boolean isHDF5(String filename) {
2657        boolean ish5 = false;
2658
2659        try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
2660            byte[] header = new byte[8];
2661            long fileSize = raf.length();
2662
2663            // The super block is located by searching for the HDF5 file signature
2664            // at byte offset 0, byte offset 512 and at successive locations in the
2665            // file, each a multiple of two of the previous location, i.e. 0, 512,
2666            // 1024, 2048, etc
2667            long offset = 0;
2668            while (!ish5 && (offset < fileSize)) {
2669                raf.seek(offset);
2670                raf.read(header);
2671
2672                if ((header[0] == -119) && (header[1] == 72) && (header[2] == 68) && (header[3] == 70) && (header[4] == 13) && (header[5] == 10) && (header[6] == 26)
2673                        && (header[7] == 10)) {
2674                    ish5 = true;
2675                }
2676                else {
2677                    ish5 = false;
2678                    if (offset == 0) {
2679                        offset = 512;
2680                    }
2681                    else {
2682                        offset *= 2;
2683                    }
2684                }
2685            }
2686        }
2687        catch (Exception ex) {
2688            return false;
2689        }
2690
2691        return ish5;
2692    }
2693
2694    /**
2695     * look at the first 4 bytes of the file to see if it is a netCDF file
2696     * byte[0]=67, byte[1]=68, byte[2]=70, byte[3]=1 or
2697     *
2698     * @param filename the file to test if netcdf
2699     *
2700     * @return true if the file is of type netcdf
2701     */
2702    public static boolean isNetcdf(String filename) {
2703        boolean isnc = false;
2704
2705        try (RandomAccessFile raf = new RandomAccessFile(filename, "r")) {
2706
2707        byte[] header = new byte[4];
2708            raf.read(header);
2709            // netCDF
2710            if ((header[0] == 67) && (header[1] == 68) && (header[2] == 70)
2711                    && (header[3] == 1)) {
2712                isnc = true;
2713            }
2714            else {
2715                isnc = false;
2716            }
2717        }
2718        catch (Exception ex) {
2719            return false;
2720        }
2721
2722        return isnc;
2723    }
2724
2725    /**
2726     * Launch default browser for a given URL.
2727     *
2728     * @param url
2729     *            the URL to open.
2730     *
2731     * @throws Exception if a failure occurred
2732     */
2733    public static final void launchBrowser(String url) throws Exception {
2734        String os = System.getProperty("os.name");
2735        Runtime runtime = Runtime.getRuntime();
2736
2737        // Block for Windows Platform
2738        if (os.startsWith("Windows")) {
2739            String cmd = "rundll32 url.dll,FileProtocolHandler " + url;
2740
2741            if (new File(url).exists()) cmd = "cmd /c start \"\" \"" + url + "\"";
2742            runtime.exec(cmd);
2743        }
2744        // Block for Mac OS
2745        else if (os.startsWith("Mac OS")) {
2746            Class<?> fileMgr = Class.forName("com.apple.eio.FileManager");
2747            Method openURL = fileMgr.getDeclaredMethod("openURL", new Class[] { String.class });
2748
2749            if (new File(url).exists()) {
2750                // local file
2751                url = "file://" + url;
2752            }
2753            openURL.invoke(null, new Object[] { url });
2754        }
2755        // Block for UNIX Platform
2756        else {
2757            String[] browsers = { "firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape" };
2758            String browser = null;
2759            for (int count = 0; count < browsers.length && browser == null; count++)
2760                if (runtime.exec(new String[] { "which", browsers[count] }).waitFor() == 0) browser = browsers[count];
2761            if (browser == null)
2762                throw new Exception("Could not find web browser");
2763            else
2764                runtime.exec(new String[] { browser, url });
2765        }
2766    }
2767
2768    /** Create a new HDF file with default file creation properties
2769     *
2770     * @param filename
2771     *          the file to create
2772     * @param dir
2773     *          the directory for file
2774     * @param type
2775     *          the type of the file
2776     * @param openFiles
2777     *          the list of already opened files
2778     *
2779     * @return the FileFormat instance of the newly created file
2780     *
2781     * @throws Exception if a failure occurred
2782     */
2783    public static FileFormat createNewFile(String filename, String dir,
2784            String type, List<FileFormat> openFiles) throws Exception {
2785        log.trace("createNewFile: {} start", filename);
2786        File f = new File(filename);
2787
2788        String fname = f.getAbsolutePath();
2789        if (fname == null) return null;
2790
2791        fname = fname.trim();
2792        if ((fname == null) || (fname.length() == 0)) {
2793            throw new Exception("Invalid file name.");
2794        }
2795
2796        String extensions = FileFormat.getFileExtensions();
2797        boolean noExtension = true;
2798        if ((extensions != null) && (extensions.length() > 0)) {
2799            java.util.StringTokenizer currentExt = new java.util.StringTokenizer(extensions, ",");
2800            String extension = "";
2801            String tmpFilename = fname.toLowerCase();
2802            while (currentExt.hasMoreTokens() && noExtension) {
2803                extension = currentExt.nextToken().trim().toLowerCase();
2804                noExtension = !tmpFilename.endsWith("." + extension);
2805            }
2806        }
2807
2808        if (noExtension) {
2809            if (type.equals(FileFormat.FILE_TYPE_HDF4)) {
2810                fname += ".hdf";
2811                f = new File(fname);
2812            }
2813            else if (type.equals(FileFormat.FILE_TYPE_HDF5)) {
2814                fname += ".h5";
2815                f = new File(fname);
2816            }
2817        }
2818
2819        if (f.exists() && f.isDirectory()) {
2820            throw new Exception("File is a directory.");
2821        }
2822        log.trace("createNewFile: {} not a directory", filename);
2823
2824        File pfile = f.getParentFile();
2825        if (pfile == null) {
2826            fname = dir + File.separator + fname;
2827            f = new File(fname);
2828        }
2829        else if (!pfile.exists()) {
2830            throw new Exception("File path does not exist at\n" + pfile.getPath());
2831        }
2832
2833        // check if the file is in use
2834        log.trace("createNewFile: {} check if the file is in use", filename);
2835        if (openFiles != null) {
2836            FileFormat theFile = null;
2837            Iterator<FileFormat> iterator = openFiles.iterator();
2838            while (iterator.hasNext()) {
2839                theFile = iterator.next();
2840                if (theFile.getFilePath().equals(fname)) {
2841                    throw new Exception("Unable to create the new file. \nThe file is being used.");
2842                }
2843            }
2844        }
2845
2846        if (f.exists()) {
2847            log.trace("createNewFile: {} file exists", filename);
2848
2849            if (!MessageDialog.openConfirm(display.getShells()[0], "Create New File",
2850                    "File exists. Do you want to replace it?")) {
2851                return null;
2852            }
2853        }
2854
2855        try {
2856            int aFlag = FileFormat.FILE_CREATE_DELETE;
2857            if (!ViewProperties.getEarlyLib().equalsIgnoreCase("Latest")) {
2858                aFlag = FileFormat.FILE_CREATE_DELETE | FileFormat.FILE_CREATE_EARLY_LIB;
2859                FileFormat.getFileFormat(type).setNewLibBounds(ViewProperties.getEarlyLib(), ViewProperties.getLateLib());
2860            }
2861            log.trace("createNewFile: {} FileFormat create", filename);
2862            return FileFormat.getFileFormat(type).createFile(fname, aFlag);
2863        }
2864        catch (Exception ex) {
2865            throw new Exception(ex.getMessage());
2866        }
2867    }
2868
2869    /**
2870     * Check and find a non-exist file.
2871     *
2872     * @param path
2873     *            -- the path that the new file will be checked.
2874     * @param ext
2875     *            -- the extention of the new file.
2876     *
2877     * @return -- the new file.
2878     */
2879    public static final File checkNewFile(String path, String ext) {
2880        File file = new File(path + "new" + ext);
2881        int i = 1;
2882
2883        while (file.exists()) {
2884            file = new File(path + "new" + i + ext);
2885            i++;
2886        }
2887
2888        return file;
2889    }
2890
2891    /**
2892     * Check if a given number if NaN or INF.
2893     *
2894     * @param val
2895     *            the number to be checked
2896     *
2897     * @return true if the number is Nan or INF; otherwise, false.
2898     */
2899    public static final boolean isNaNINF(double val) {
2900        return (Double.isNaN(val) || val == Float.NEGATIVE_INFINITY || val == Float.POSITIVE_INFINITY || val == Double.NEGATIVE_INFINITY || val == Double.POSITIVE_INFINITY);
2901    }
2902
2903    /**
2904     * Since Java does not allow array indices to be larger than int type, check the
2905     * given value to see if it is within the valid range of a Java int.
2906     *
2907     * @param value
2908     *            The value to check
2909     *
2910     * @return false if the value is outside the range of a Java int, true
2911     *         otherwise.
2912     */
2913    public static boolean checkValidJavaArrayIndex(final long value) {
2914        return (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE);
2915    }
2916
2917    /**
2918     * Show an SWT error dialog with the given error message.
2919     * @param parent
2920     *           The parent Shell of the MessageDialog
2921     * @param title
2922     *           The title to set for the MessageDialog
2923     * @param errorMsg
2924     *           The error message to display in the MessageDialog
2925     */
2926    public static void showError(Shell parent, String title, String errorMsg) {
2927        String dlgTitlePrefix = "";
2928        String dlgTitleSuffix = (title == null) ? "" : title;
2929
2930        if (parent != null) {
2931            dlgTitlePrefix = parent.getText();
2932            if (dlgTitlePrefix.length() > 0)
2933                dlgTitlePrefix += " - ";
2934        }
2935
2936        MessageDialog.openError(parent, dlgTitlePrefix + dlgTitleSuffix, (errorMsg == null) ? "UNKNOWN" : errorMsg);
2937    }
2938
2939    /**
2940     * Show an SWT Information dialog with the given message.
2941     * @param parent
2942     *           The parent Shell of the MessageDialog
2943     * @param title
2944     *           The title to set for the MessageDialog
2945     * @param infoMsg
2946     *           The message to display in the MessageDialog
2947     */
2948    public static void showInformation(Shell parent, String title, String infoMsg) {
2949        String dlgTitlePrefix = "";
2950        String dlgTitleSuffix = (title == null) ? "" : title;
2951
2952        if (parent != null) {
2953            dlgTitlePrefix = parent.getText();
2954            if (dlgTitlePrefix.length() > 0)
2955                dlgTitlePrefix += " - ";
2956        }
2957
2958        MessageDialog.openInformation(parent, dlgTitlePrefix + dlgTitleSuffix, (infoMsg == null) ? "UNKNOWN" : infoMsg);
2959    }
2960
2961    /**
2962     * Show an SWT Confirm dialog with the given message.
2963     *
2964     * @param parent
2965     *            The parent Shell of the MessageDialog
2966     * @param title
2967     *            The title to set for the MessageDialog
2968     * @param confMsg
2969     *            The message to display in the MessageDialog
2970     * @return The status of the dialog after closing
2971     */
2972    public static boolean showConfirm(Shell parent, String title, String confMsg) {
2973        String dlgTitlePrefix = "";
2974        String dlgTitleSuffix = (title == null) ? "" : title;
2975
2976        if (parent != null) {
2977            dlgTitlePrefix = parent.getText();
2978            if (dlgTitlePrefix.length() > 0)
2979                dlgTitlePrefix += " - ";
2980        }
2981
2982        return MessageDialog.openConfirm(parent, dlgTitlePrefix + dlgTitleSuffix, (confMsg == null) ? "UNKNOWN" : confMsg);
2983    }
2984
2985    /**
2986     * Show an SWT Warning dialog with the given message.
2987     * @param parent
2988     *           The parent Shell of the MessageDialog
2989     * @param title
2990     *           The title to set for the MessageDialog
2991     * @param warnMsg
2992     *           The message to display in the MessageDialog
2993     */
2994    public static void showWarning(Shell parent, String title, String warnMsg) {
2995        String dlgTitlePrefix = "";
2996        String dlgTitleSuffix = (title == null) ? "" : title;
2997
2998        if (parent != null) {
2999            dlgTitlePrefix = parent.getText();
3000            if (dlgTitlePrefix.length() > 0)
3001                dlgTitlePrefix += " - ";
3002        }
3003
3004        MessageDialog.openWarning(parent, dlgTitlePrefix + dlgTitleSuffix, (warnMsg == null) ? "UNKNOWN" : warnMsg);
3005    }
3006}