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