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