    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 COPYING file, which can be found  *
    009 * at the root of the source code distribution tree,                         *
    010 * or in https://www.hdfgroup.org/licenses.                                  *
    011 * If you do not have access to either file, you may request a copy from     *
    012 * help@hdfgroup.org.                                                        *
    013 ****************************************************************************/
    014
    015package hdf.object.fits;
    016
    017import java.lang.reflect.Array;
    018import java.util.Iterator;
    019import java.util.List;
    020import java.util.Vector;
    021
    022import org.slf4j.Logger;
    023import org.slf4j.LoggerFactory;
    024
    025import hdf.object.Dataset;
    026import hdf.object.Datatype;
    027import hdf.object.FileFormat;
    028import hdf.object.Group;
    029import hdf.object.HObject;
    030import hdf.object.ScalarDS;
    031import hdf.object.MetaDataContainer;
    032
    033import hdf.object.fits.FitsAttribute;
    034
    035import nom.tam.fits.BasicHDU;
    036import nom.tam.fits.Header;
    037import nom.tam.fits.HeaderCard;
    038
    039/**
    040 * FitsDataset describes an multi-dimension array of HDF5 scalar or atomic data types, such as byte, int, short, long,
    041 * float, double and string, and operations performed on the scalar dataset
    042 *
    043 * The library predefines a modest number of datatypes. For details, read
    044 * <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
    045 *
    046 * @version 1.1 9/4/2007
    047 * @author Peter X. Cao
    048 */
    049public class FitsDataset extends ScalarDS implements MetaDataContainer
    050{
    051    private static final long serialVersionUID = 3944770379558335171L;
    052
    053    private static final Logger log = LoggerFactory.getLogger(FitsDataset.class);
    054
    055    /**
    056     * The list of attributes of this data object. Members of the list are
    057     * instance of Attribute.
    058     */
    059    private List attributeList;
    060
    061    /** the native dataset */
    062    private BasicHDU nativeDataset;
    063
    064    /**
    065     * Constructs an FitsDataset object with specific netcdf variable.
    066     *
    067     * @param fileFormat the netcdf file.
    068     * @param hdu the BasicHDU.
    069     * @param dName the name for this dataset.
    070     * @param oid the unique identifier for this dataset.
    071     */
    072    public FitsDataset(FileFormat fileFormat, BasicHDU hdu, String dName, long[] oid) {
    073        super(fileFormat, dName, HObject.SEPARATOR, oid);
    074        unsignedConverted = false;
    075        nativeDataset = hdu;
    076    }
    077
    078    /**
    079     * Check if the object has any attributes attached.
    080     *
    081     * @return true if it has any attributes, false otherwise.
    082     */
    083    @Override
    084    public boolean hasAttribute() {
    085        return false;
    086    }
    087
    088    // Implementing Dataset
    089    @Override
    090    public Dataset copy(Group pgroup, String dstName, long[] dims, Object buff) throws Exception {
    091        // not supported
    092        throw new UnsupportedOperationException("copy operation unsupported for FITS.");
    093    }
    094
    095    /*
    096     * (non-Javadoc)
    097     * @see hdf.object.Dataset#readBytes()
    098     */
    099    @Override
    100    public byte[] readBytes() throws Exception {
    101        // not supported
    102        throw new UnsupportedOperationException("readBytes operation unsupported for FITS.");
    103    }
    104
    105    /**
    106     * Reads the data from file.
    107     *
    108     * read() reads the data from file to a memory buffer and returns the memory
    109     * buffer. The dataset object does not hold the memory buffer. To store the
    110     * memory buffer in the dataset object, one must call getData().
    111     *
    112     * By default, the whole dataset is read into memory. Users can also select
    113     * a subset to read. Subsetting is done in an implicit way.
    114     *
    115     * @return the data read from file.
    116     *
    117     * @see #getData()
    118     *
    119     * @throws Exception
    120     *             if object can not be read
    121     * @throws OutOfMemoryError
    122     *             if memory is exhausted
    123     */
    124    @Override
    125    public Object read() throws Exception {
    126        Object theData = null;
    127        Object fitsData = null;
    128
    129        if (nativeDataset == null)
    130            return null;
    131
    132        try {
    133            fitsData = nativeDataset.getData().getData();
    134        }
    135        catch (Exception ex) {
    136            throw new UnsupportedOperationException("This implementation only supports integer and float dataset. " +
    137                    "It may not work for other datatypes. \n"+ex);
    138        }
    139
    140        int n = get1DLength(fitsData);
    141
    142        theData = FitsDatatype.allocateArray(nativeDataset.getBitPix(), n);
    143
    144        to1Darray(fitsData, theData, 0);
    145
    146        return theData;
    147    }
    148
    149    /**
    150     * Writes a memory buffer to the object in the file.
    151     *
    152     * @param buf
    153     *            the data to write
    154     *
    155     * @throws Exception
    156     *             if data can not be written
    157     */
    158    @Override
    159    public void write(Object buf) throws Exception {
    160        // not supported
    161        throw new UnsupportedOperationException("write operation unsupported for FITS.");
    162    }
    163
    164    /**
    165     * Retrieves the object's metadata, such as attributes, from the file.
    166     *
    167     * Metadata, such as attributes, is stored in a List.
    168     *
    169     * @return the list of metadata objects.
    170     *
    171     * @throws Exception
    172     *             if the metadata can not be retrieved
    173     */
    174    @SuppressWarnings("rawtypes")
    175    public List getMetadata() throws Exception {
    176        if (attributeList != null)
    177            return attributeList;
    178
    179        if (nativeDataset == null)
    180            return null;
    181
    182        Header header = nativeDataset.getHeader();
    183        if (header == null)
    184            return null;
    185
    186        attributeList = new Vector();
    187        HeaderCard hc = null;
    188        Iterator it = header.iterator();
    189        FitsAttribute attr = null;
    190        Datatype dtype = new FitsDatatype(Datatype.CLASS_STRING, 80, 0, 0);
    191        long[] dims = {1};
    192        String value = null;
    193        while (it.hasNext()) {
    194            value = "";
    195            hc = (HeaderCard)it.next();
    196            attr = new FitsAttribute(this, hc.getKey(), dtype, dims);
    197            String tvalue = hc.getValue();
    198            if (tvalue != null)
    199                value += tvalue;
    200            tvalue = hc.getComment();
    201            if (tvalue != null)
    202                value += " / " + tvalue;
    203            attr.setAttributeData(value);
    204            attributeList.add(attr);
    205        }
    206
    207        return attributeList;
    208    }
    209
    210    /**
    211     * Writes a specific piece of metadata (such as an attribute) into the file.
    212     *
    213     * If an HDF(4&amp;5) attribute exists in the file, this method updates its
    214     * value. If the attribute does not exist in the file, it creates the
    215     * attribute in the file and attaches it to the object. It will fail to
    216     * write a new attribute to the object where an attribute with the same name
    217     * already exists. To update the value of an existing attribute in the file,
    218     * one needs to get the instance of the attribute by getMetadata(), change
    219     * its values, then use writeMetadata() to write the value.
    220     *
    221     * @param info
    222     *            the metadata to write.
    223     *
    224     * @throws Exception
    225     *             if the metadata can not be written
    226     */
    227    public void writeMetadata(Object info) throws Exception {
    228        // not supported
    229        throw new UnsupportedOperationException("writeMetadata operation unsupported for FITS.");
    230    }
    231
    232    /**
    233     * Deletes an existing piece of metadata from this object.
    234     *
    235     * @param info
    236     *            the metadata to delete.
    237     *
    238     * @throws Exception
    239     *             if the metadata can not be removed
    240     */
    241    public void removeMetadata(Object info) throws Exception {
    242        // not supported
    243        throw new UnsupportedOperationException("removeMetadata operation unsupported for FITS.");
    244    }
    245
    246    /**
    247     * Updates an existing piece of metadata attached to this object.
    248     *
    249     * @param info
    250     *            the metadata to update.
    251     *
    252     * @throws Exception
    253     *             if the metadata can not be updated
    254     */
    255    public void updateMetadata(Object info) throws Exception {
    256        // not supported
    257        throw new UnsupportedOperationException("updateMetadata operation unsupported for FITS.");
    258    }
    259
    260    /*
    261     * (non-Javadoc)
    262     * @see hdf.object.HObject#open()
    263     */
    264    @Override
    265    public long open() {
    266        return -1;
    267    }
    268
    269    /*
    270     * (non-Javadoc)
    271     * @see hdf.object.HObject#close(int)
    272     */
    273    @Override
    274    public void close(long did) {
    275        // Nothing to implement
    276    }
    277
    278    /*
    279     * (non-Javadoc)
    280     * @see hdf.object.Dataset#init()
    281     */
    282    @Override
    283    public void init() {
    284        if (nativeDataset == null)
    285            return;
    286
    287        if (inited)
    288            return; // already called. Initialize only once
    289
    290        int[] axes= null;
    291        try {
    292            axes = nativeDataset.getAxes();
    293        }
    294        catch (Exception ex) {
    295            log.debug("nativeDataset.getAxes():", ex);
    296        }
    297
    298        if (axes == null)
    299            return;
    300
    301
    302        rank = axes.length;
    303        if (rank == 0) {
    304            // a scalar data point
    305            isScalar = true;
    306            rank = 1;
    307            dims = new long[] { 1 };
    308        }
    309        else {
    310            isScalar = false;
    311            dims = new long[rank];
    312            for (int i=0; i<rank; i++)
    313                dims[i] = axes[i];
    314        }
    315
    316        startDims = new long[rank];
    317        selectedDims = new long[rank];
    318        for (int i=0; i<rank; i++) {
    319            startDims[i] = 0;
    320            selectedDims[i] = 1;
    321        }
    322
    323        if (rank == 1) {
    324            selectedIndex[0] = 0;
    325            selectedDims[0] = dims[0];
    326        }
    327        else if (rank == 2) {
    328            selectedIndex[0] = 0;
    329            selectedIndex[1] = 1;
    330            selectedDims[0] = dims[0];
    331            selectedDims[1] = dims[1];
    332        }
    333        else if (rank > 2) {
    334            selectedIndex[0] = 0;
    335            selectedIndex[1] = 1;
    336            selectedIndex[2] = 2;
    337            selectedDims[0] = dims[0];
    338            selectedDims[1] = dims[1];
    339        }
    340
    341        if ((rank > 1) && isText)
    342            selectedDims[1] = 1;
    343
    344        inited = true;
    345    }
    346
    347    /* Implement abstart ScalarDS */
    348
    349    /**
    350     * Creates a new dataset.
    351     *
    352     * @param name the name of the dataset to create.
    353     * @param pgroup the parent group of the new dataset.
    354     * @param type the datatype of the dataset.
    355     * @param dims the dimension size of the dataset.
    356     * @param maxdims the max dimension size of the dataset.
    357     * @param chunks the chunk size of the dataset.
    358     * @param gzip the level of the gzip compression.
    359     * @param data the array of data values.
    360     *
    361     * @return the new dataset if successful. Otherwise returns null.
    362     *
    363     * @throws Exception
    364     *            if there is an error
    365     */
    366    public static FitsDataset create(String name, Group pgroup, Datatype type,
    367            long[] dims, long[] maxdims, long[] chunks, int gzip, Object data) throws Exception {
    368        // not supported
    369        throw new UnsupportedOperationException("Unsupported operation for FITS.");
    370    }
    371
    372    /**
    373     * Returns the datatype of the data object.
    374     *
    375     * @return the datatype of the data object.
    376     */
    377    @Override
    378    public Datatype getDatatype() {
    379        if (datatype == null) {
    380            try {
    381                datatype = new FitsDatatype(nativeDataset.getBitPix());
    382            }
    383            catch (Exception ex) {
    384                log.debug("getDatatype(): failed to create datatype: ", ex);
    385                datatype = null;
    386            }
    387        }
    388
    389        return datatype;
    390    }
    391
    392    /*
    393     * (non-Javadoc)
    394     * @see hdf.object.HObject#setName(java.lang.String)
    395     */
    396    @Override
    397    public void setName (String newName) throws Exception {
    398        // not supported
    399        throw new UnsupportedOperationException("Unsupported operation for FITS.");
    400    }
    401
    402    private int get1DLength(Object data) throws Exception {
    403        if (!data.getClass().isArray())
    404            return 1;
    405
    406        int len = Array.getLength(data);
    407
    408        int total = 0;
    409        for (int i = 0; i < len; i++)
    410            total += get1DLength(Array.get(data, i));
    411
    412        return total;
    413    }
    414
    415    /** copy multi-dimension array of fits data into 1D array */
    416    private int to1Darray(Object dataIn, Object dataOut, int offset) throws Exception {
    417        Class component = dataIn.getClass().getComponentType();
    418        if (component == null)
    419            return offset;
    420
    421        int size = Array.getLength(dataIn);
    422        if (!component.isArray()) {
    423            System.arraycopy(dataIn, 0, dataOut, offset, size);
    424            return offset+size;
    425        }
    426
    427        for (int i = size - 1; i >= 0; i--)
    428            offset = to1Darray(Array.get(dataIn, i), dataOut, offset);
    429
    430        return offset;
    431    }
    432
    433    //Implementing DataFormat
    434    /* FITS does not support metadata */
    435    /**
    436     * Retrieves the object's metadata, such as attributes, from the file.
    437     *
    438     * Metadata, such as attributes, is stored in a List.
    439     *
    440     * @param attrPropList
    441     *             the list of properties to get
    442     *
    443     * @return the list of metadata objects.
    444     *
    445     * @throws Exception
    446     *             if the metadata can not be retrieved
    447     */
    448    public List getMetadata(int... attrPropList) throws Exception {
    449        throw new UnsupportedOperationException("getMetadata(int... attrPropList) is not supported");
    450    }
    451}