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