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     * @return the Fits file.
275     */
276    public Fits getFitsFile() { return fitsFile; }
277
278    // implementign FileFormat
279    @Override
280    public Group createGroup(String name, Group pgroup) throws Exception
281    {
282        throw new UnsupportedOperationException("Unsupported createGroup operation for Fits.");
283    }
284
285    // implementign FileFormat
286    @Override
287    public Datatype createDatatype(int tclass, int tsize, int torder, int tsign) throws Exception
288    {
289        throw new UnsupportedOperationException("Unsupported createDatatype operation for Fits.");
290    }
291
292    // implementign FileFormat
293    @Override
294    public Datatype createNamedDatatype(Datatype tnative, String name) throws Exception
295    {
296        throw new UnsupportedOperationException("Fits does not support named datatype.");
297    }
298
299    // implementign FileFormat
300    @Override
301    public Dataset createScalarDS(String name, Group pgroup, Datatype type, long[] dims, long[] maxdims,
302                                  long[] chunks, int gzip, Object fillValue, Object data) throws Exception
303    {
304        throw new UnsupportedOperationException("Unsupported createScalarDS operation.");
305    }
306
307    // implementign FileFormat
308    @Override
309    public Dataset createImage(String name, Group pgroup, Datatype type, long[] dims, long[] maxdims,
310                               long[] chunks, int gzip, int ncomp, int intelace, Object data) throws Exception
311    {
312        throw new UnsupportedOperationException("Unsupported createImage operation.");
313    }
314
315    // implementing FileFormat
316    @Override
317    public void delete(HObject obj)throws Exception
318    {
319        throw new UnsupportedOperationException("Unsupported delete operation.");
320    }
321
322    // implementing FileFormat
323    @Override
324    public HObject copy(HObject srcObj, Group dstGroup, String dstName) throws Exception
325    {
326        throw new UnsupportedOperationException("Unsupported copy operation.");
327    }
328
329    /**
330     * copy a dataset into another group.
331     *
332     * @param srcDataset the dataset to be copied.
333     * @param pgroup the group where the dataset is copied to.
334     *
335     * @return the treeNode containing the new copy of the dataset.
336     */
337    private void copyDataset(Dataset srcDataset, FitsGroup pgroup) throws Exception
338    {
339        throw new UnsupportedOperationException("Unsupported copyDataset operation.");
340    }
341
342    private void copyGroup(FitsGroup srcGroup, FitsGroup pgroup) throws Exception
343    {
344        throw new UnsupportedOperationException("Unsupported copyGroup operation.");
345    }
346
347    /**
348     * Copies the attributes of one object to another object.
349     *
350     * FITS does not support attributes
351     *
352     * @param src
353     *            The source object.
354     * @param dst
355     *            The destination object.
356     *
357     * @see #copyAttributes(long, long)
358     */
359    public void copyAttributes(HObject src, HObject dst)
360    {
361        throw new UnsupportedOperationException("Unsupported copyAttributes operation.");
362    }
363
364    /**
365     * Copies the attributes of one object to another object.
366     *
367     * FITS does not support attributes
368     *
369     * @param srcID
370     *            The source identifier.
371     * @param dstID
372     *            The destination identifier.
373     *
374     * @see #copyAttributes(long, long)
375     */
376    public void copyAttributes(long srcID, long dstID)
377    {
378        throw new UnsupportedOperationException("Unsupported copyAttributes with id operation.");
379    }
380
381    /**
382     * Creates a new attribute and attached to the object if attribute does
383     * not exist. Otherwise, just update the value of the attribute.
384     *
385     * @param obj
386     *        the object which the attribute is to be attached to.
387     * @param attr
388     *        the atribute to attach.
389     * @param attrExisted
390     *        The indicator if the given attribute exists.
391     */
392    @Override
393    public void writeAttribute(HObject obj, hdf.object.Attribute attr, boolean attrExisted) throws Exception
394    {
395        throw new UnsupportedOperationException("Unsupported operation.");
396    }
397
398    /**
399     *  Returns the version of the library.
400     */
401    @Override
402    public String getLibversion()
403    {
404        String ver = "Fits Java (version 2.4)";
405
406        return ver;
407    }
408
409    // implementing FileFormat
410    @Override
411    public HObject get(String path) throws Exception
412    {
413        throw new UnsupportedOperationException("get() is not supported");
414    }
415}