    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.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        MessageDialog.openError(parent, (title == null) ? parent.getText() : parent.getText() + " - " + title, (errorMsg == null) ? "UNKNOWN" : errorMsg);
    3147    }
    3148
    3149    /**
    3150     * Show an SWT Information dialog with the given message.
    3151     * @param parent
    3152     *           The parent Shell of the MessageDialog
    3153     * @param title
    3154     *           The title to set for the MessageDialog
    3155     * @param infoMsg
    3156     *           The message to display in the MessageDialog
    3157     */
    3158    public static void showInformation(Shell parent, String title, String infoMsg) {
    3159        MessageDialog.openInformation(parent, (title == null) ? parent.getText() : parent.getText() + " - " + title, (infoMsg == null) ? "UNKNOWN" : infoMsg);
    3160    }
    3161
    3162    /**
    3163     * Show an SWT Confirm dialog with the given message.
    3164     *
    3165     * @param parent
    3166     *            The parent Shell of the MessageDialog
    3167     * @param title
    3168     *            The title to set for the MessageDialog
    3169     * @param confMsg
    3170     *            The message to display in the MessageDialog
    3171     * @return The status of the dialog after closing
    3172     */
    3173    public static boolean showConfirm(Shell parent, String title, String confMsg) {
    3174        return MessageDialog.openConfirm(parent, (title == null) ? parent.getText() : parent.getText() + " - " + title, (confMsg == null) ? "UNKNOWN" : confMsg);
    3175    }
    3176
    3177    /**
    3178     * Show an SWT Warning dialog with the given message.
    3179     * @param parent
    3180     *           The parent Shell of the MessageDialog
    3181     * @param title
    3182     *           The title to set for the MessageDialog
    3183     * @param warnMsg
    3184     *           The message to display in the MessageDialog
    3185     */
    3186    public static void showWarning(Shell parent, String title, String warnMsg) {
    3187        MessageDialog.openWarning(parent, (title == null) ? parent.getText() : parent.getText() + " - " + title, (warnMsg == null) ? "UNKNOWN" : warnMsg);
    3188    }
    3189}