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