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.object.fits;
016
017import java.io.DataInput;
018import java.io.IOException;
019import java.io.InputStream;
020import java.io.RandomAccessFile;
021
022import hdf.object.Attribute;
023import hdf.object.Dataset;
024import hdf.object.Datatype;
025import hdf.object.FileFormat;
026import hdf.object.Group;
027import hdf.object.HObject;
028import nom.tam.fits.AsciiTableHDU;
029import nom.tam.fits.BasicHDU;
030import nom.tam.fits.BinaryTableHDU;
031import nom.tam.fits.Fits;
032import nom.tam.fits.ImageHDU;
033import nom.tam.fits.RandomGroupsHDU;
034import nom.tam.fits.TableHDU;
035
036/**
037 * This class provides file level APIs. File access APIs include retrieving the
038 * file hierarchy, opening and closing file, and writing file content to disk.
039 *
040 * @version 2.4 9/4/2007
041 * @author Peter X. Cao
042 */
043public class FitsFile extends FileFormat
044{
045    private static final long serialVersionUID = -1965689032980605791L;
046
047    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FitsFile.class);
048
049    /**
050     * The root object of the file hierarchy.
051     */
052    private HObject rootObject;
053
054    /** the fits file */
055    private Fits fitsFile;
056
057    private static boolean isFileOpen;
058
059    /**
060     * Constructs an empty FitsFile with read-only access.
061     */
062    public FitsFile() {
063        this("");
064    }
065
066    /**
067     * Constructs an FitsFile object of given file name with read-only access.
068     *
069     * @param pathname the file name.
070     */
071    public FitsFile(String pathname) {
072        super(pathname);
073
074        isReadOnly = true;
075        isFileOpen = false;
076        this.fid = -1;
077        try {
078            fitsFile = new Fits(fullFileName);
079        }
080        catch (Exception ex) {
081            if(!pathname.isEmpty())
082                log.debug("Fits({}):", fullFileName, ex);
083        }
084    }
085
086
087    /**
088     * Checks if the given file format is a Fits file.
089     *
090     * @param fileformat the fileformat to be checked.
091     *
092     * @return true if the given file is an Fits file; otherwise returns false.
093     */
094    @Override
095    public boolean isThisType(FileFormat fileformat) {
096        return (fileformat instanceof FitsFile);
097    }
098
099    /**
100     * Checks if a given file is a Fits file.
101     *
102     * @param filename the file to be checked.
103     *
104     * @return true if the given file is an Fits file; otherwise returns false.
105     */
106    @Override
107    public boolean isThisType(String filename) {
108        boolean is_fits = false;
109        RandomAccessFile raf = null;
110        try {
111            raf = new RandomAccessFile(filename, "r");
112        }
113        catch (Exception ex) {
114            raf = null;
115        }
116
117        if (raf == null)
118            return false;
119
120        byte[] header = new byte[80];
121        try {
122            raf.read(header);
123        }
124        catch (Exception ex) {
125            header = null;
126        }
127
128        if (header != null) {
129            String front = new String(header, 0, 9);
130            if (!front.startsWith("SIMPLE  =")) {
131                try {
132                    raf.close();
133                }
134                catch (Exception ex) {
135                    log.debug("closing RandomAccessFile({}):", filename, ex);
136                }
137                return false;
138            }
139
140            String back = new String(header, 9, 70);
141            back = back.trim();
142            if ((back.length() < 1) || (back.charAt(0) != 'T')) {
143                try {
144                    raf.close();
145                }
146                catch (Exception ex) {
147                    log.debug("closing RandomAccessFile({}):", filename, ex);
148                }
149                return false;
150            }
151
152            is_fits = true;;
153        }
154
155        try {
156            raf.close();
157        }
158        catch (Exception ex) {
159            log.debug("closing RandomAccessFile({}):", filename, ex);
160        }
161
162        return is_fits;
163    }
164
165
166    /**
167     * Creates a FitsFile instance with specified file name and READ access.
168     *
169     * @param filename the full path name of the file.
170     * @param access the access properties of the file.
171     * Regardless of specified access, the FitsFile implementation uses* READ.
172     *
173     * @return the Fits file.
174     *
175     * @throws Exception
176     *             The exception thrown from the File class.
177     */
178    @Override
179    public FileFormat createInstance(String filename, int access) throws Exception {
180        return new FitsFile(filename);
181    }
182
183
184    // Implementing FileFormat
185    @Override
186    public long open() throws Exception {
187        if (!isFileOpen) {
188            isFileOpen = true;
189            rootObject = loadTree();
190        }
191
192        return 0;
193    }
194
195    private HObject loadTree() {
196        long[] oid = {0};
197        // root object does not have a parent path or a parent node
198        FitsGroup rootGroup = new FitsGroup(this, "/", null, null, oid);
199
200        if (fitsFile == null)
201            return rootGroup;
202
203        BasicHDU[] hdus = null;
204
205        try {
206            hdus = fitsFile.read();
207        }
208        catch (Exception ex) {
209            log.debug("fitsFile.read():", ex);
210        }
211
212        if (hdus == null)
213            return rootGroup;
214
215        int n = hdus.length;
216        int nImageHDU = 0;
217        int nTableHDU =  0;
218        String hduName = null;
219        BasicHDU hdu = null;
220        for (int i=0; i<n; i++) {
221            hdu = hdus[i];
222            hduName = null;
223            // only deal with ImageHDU and TableHDU
224            if (hdu instanceof ImageHDU) {
225                hduName = "ImageHDU #"+nImageHDU++;
226            }
227            else if (hdu instanceof RandomGroupsHDU) {
228                hduName = "RandomGroupsHDU #"+nImageHDU++;
229            }
230            else if (hdu instanceof TableHDU) {
231                if (hdu instanceof AsciiTableHDU)
232                    hduName = "AsciiTableHDU #"+nTableHDU++;
233                else if (hdu instanceof BinaryTableHDU)
234                    hduName = "BinaryTableHDU #"+nTableHDU++;
235                else
236                    hduName = "TableHDU #"+nTableHDU++;
237            }
238
239            if (hduName != null) {
240                oid[0] = hdu.hashCode();
241                FitsDataset d =  new FitsDataset(this, hdu, hduName, oid);
242                rootGroup.addToMemberList(d);
243            }
244        }
245
246        return rootGroup;
247    }
248
249    // Implementing FileFormat
250    @Override
251    public void close() throws IOException {
252        if (fitsFile == null)
253            return;
254
255        DataInput di = fitsFile.getStream();
256        if (di instanceof InputStream)
257            ((InputStream)di).close();
258    }
259
260    // Implementing FileFormat
261    @Override
262    public HObject getRootObject() {
263        return rootObject;
264    }
265
266    /**
267    * @return the Fits file.
268    */
269    public Fits getFitsFile() {
270        return fitsFile;
271    }
272
273    // implementign FileFormat
274    @Override
275    public Group createGroup(String name, Group pgroup) throws Exception {
276        throw new UnsupportedOperationException("Unsupported createGroup operation for Fits.");
277    }
278
279    // implementign FileFormat
280    @Override
281    public Datatype createDatatype(int tclass, int tsize, int torder, int tsign) throws Exception {
282        throw new UnsupportedOperationException("Unsupported createDatatype operation for Fits.");
283    }
284
285    // implementign FileFormat
286    @Override
287    public Datatype createNamedDatatype(Datatype tnative, String name) throws Exception {
288        throw new UnsupportedOperationException("Fits does not support named datatype.");
289    }
290
291    // implementign FileFormat
292    @Override
293    public Dataset createScalarDS(String name, Group pgroup, Datatype type,
294            long[] dims, long[] maxdims, long[] chunks,
295            int gzip, Object fillValue, Object data) throws Exception {
296        throw new UnsupportedOperationException("Unsupported createScalarDS operation.");
297    }
298
299    // implementign FileFormat
300    @Override
301    public Dataset createImage(String name, Group pgroup, Datatype type,
302            long[] dims, long[] maxdims, long[] chunks,
303            int gzip, int ncomp, int intelace, Object data) throws Exception {
304        throw new UnsupportedOperationException("Unsupported createImage operation.");
305    }
306
307    // implementing FileFormat
308    @Override
309    public void delete(HObject obj) throws Exception {
310        throw new UnsupportedOperationException("Unsupported delete operation.");
311    }
312
313    // implementing FileFormat
314    @Override
315    public HObject copy(HObject srcObj, Group dstGroup, String dstName) throws Exception {
316        throw new UnsupportedOperationException("Unsupported copy operation.");
317    }
318
319    /**
320     * copy a dataset into another group.
321     *
322     * @param srcDataset the dataset to be copied.
323     * @param pgroup the group where the dataset is copied to.
324     *
325     * @return the treeNode containing the new copy of the dataset.
326     */
327    private void copyDataset(Dataset srcDataset, FitsGroup pgroup) throws Exception {
328        throw new UnsupportedOperationException("Unsupported copyDataset operation.");
329    }
330
331    private void copyGroup(FitsGroup srcGroup, FitsGroup pgroup) throws Exception {
332        throw new UnsupportedOperationException("Unsupported copyGroup operation.");
333    }
334
335    /**
336     * Copies the attributes of one object to another object.
337     *
338     * FITS does not support attributes
339     *
340     * @param src
341     *            The source object.
342     * @param dst
343     *            The destination object.
344     *
345     * @see #copyAttributes(long, long)
346     */
347    public void copyAttributes(HObject src, HObject dst) {
348        throw new UnsupportedOperationException("Unsupported copyAttributes operation.");
349    }
350
351    /**
352     * Copies the attributes of one object to another object.
353     *
354     * FITS does not support attributes
355     *
356     * @param srcID
357     *            The source identifier.
358     * @param dstID
359     *            The destination identifier.
360     *
361     * @see #copyAttributes(long, long)
362     */
363    public void copyAttributes(long srcID, long dstID) {
364        throw new UnsupportedOperationException("Unsupported copyAttributes with id operation.");
365    }
366
367    /**
368     * Creates a new attribute and attached to the object if attribute does
369     * not exist. Otherwise, just update the value of the attribute.
370     *
371     * @param obj
372     *        the object which the attribute is to be attached to.
373     * @param attr
374     *        the atribute to attach.
375     * @param attrExisted
376     *        The indicator if the given attribute exists.
377     */
378    @Override
379    public void writeAttribute(HObject obj, hdf.object.Attribute attr, boolean attrExisted) throws Exception {
380        throw new UnsupportedOperationException("Unsupported operation.");
381    }
382
383    /**
384     *  Returns the version of the library.
385     */
386    @Override
387    public String getLibversion() {
388        String ver = "Fits Java (version 2.4)";
389
390        return ver;
391    }
392
393    // implementing FileFormat
394    @Override
395    public HObject get(String path) throws Exception {
396        throw new UnsupportedOperationException("get() is not supported");
397    }
398}
399