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