001    /* @(#)FitsFile.java     $Revision: 1.10 $    $Date: 2003/04/11 08:41:15 $
002     *
003     * Copyright (C) 2002 European Southern Observatory 
004     * License:  GNU General Public License version 2 or later
005     */
006    package org.eso.fits;
007    
008    import java.lang.*;
009    import java.util.*;
010    import java.io.*;
011    
012    /** FitsFile class represents a FITS file consisting of a set of
013     *  Header/Data Units. The header information is stored in
014     *  FitsHeader objects while data are not saved in objects but
015     *  accessed through file.  Thus, files may be corrupted if the
016     *  disk file is modified independently by other modules.
017     *
018     *  @version $Revision: 1.10 $ $Date: 2003/04/11 08:41:15 $
019     *  @author  P.Grosbol, DMD/ESO, <pgrosbol@eso.org> http://www.eso.org/~pgrosbol/fits_java/jfits.html
020     */
021    public class FitsFile {
022    
023        private File file;
024        private RandomAccessFile raFile;
025        private Vector hdUnits;
026        private boolean changeHDU = false;
027    
028        /** Default constructor for FitsFile class
029         */
030        public FitsFile() {
031            hdUnits = new Vector(1);
032        }
033    
034        /** Constructor for FitsFile class given a FITS stream.
035         *  
036         *
037         *  @param file    DataInput stream positioned at its start
038         *  @exception FitsException */
039        public FitsFile(DataInput file) throws FitsException {
040            this();
041            scanFitsFile(file, false);
042        }
043    
044        /** Constructor for FitsFile class given a FITS stream and a flag
045         *  indicating if the data matrices should be stored internally.
046         *  The store flag must be true to allow access to the data matrices
047         *  where as header data will always be available.
048         *
049         *  @param file    DataInput stream positioned at its start
050         *  @param sflag   Flag indicating if data matrices should be
051         *                 stored internally in the class.
052         *  @exception FitsException */
053        public FitsFile(DataInput file, boolean sflag) throws FitsException {
054            this();
055            scanFitsFile(file, sflag);
056        }
057    
058        /** Constructor specifying a name of a disk file. Note: the file
059         *  will be opened in read-only mode for security reasons.  This
060         *  means that the data matrix cannot be modified although headers
061         *  can as they are stored in memory.  If data should be modified,
062         *  one must create a DataInput object for the file explicitly
063         *  (with read/write permissions) and use the appropriate
064         *  constructor.
065         *
066         *  @param file name of disk file in FITS format
067         *  @exception IOException,FitsException */
068        public FitsFile(File file) throws IOException, FitsException {
069            this();
070            this.raFile = new RandomAccessFile(file, "r");
071            this.file = file;
072            scanFitsFile(raFile, false);
073        }
074    
075        /** Constructor from name of disk file. Note: the file will be
076         *  opened in read-only mode for security reasons.  This means
077         *  that the data matrix cannot be modified although headers can
078         *  as they are stored in memory.  If data should be modified, one
079         *  must create a DataInput object for the file explicitly (with
080         *  read/write permissions) and use the appropriate constructor.
081         *
082         *  @param filename name of disk file in FITS format
083         *  @exception IOException,FitsException */
084        public FitsFile(String filename) throws IOException, FitsException {
085            this(new File(filename));
086        }
087    
088        /** Private method which scans an input stream.  It is used by the
089         *  constructors.
090         *  @param file    DataInput file positioned at its start
091         *  @param sflag   Flag for internal storage of data matrices
092         *  @exception FitsException */
093        private void scanFitsFile(DataInput file, boolean sflag)
094            throws FitsException {
095            FitsHDUnit hdu;
096            int no_hdu = 0;
097    
098            try {
099                while (true) {
100                    hdu = new FitsHDUnit(file, sflag);
101                    hdUnits.setSize(no_hdu++);
102                    hdUnits.addElement(hdu);
103                }
104            } catch (FitsException e) {
105                if (no_hdu<1) {
106                    throw new FitsException("Not a FITS file (" + e + ")", FitsException.FILE);
107                }
108            }
109            hdUnits.trimToSize();
110        }
111    
112        /** Finalize method which close disk file
113         *
114         *  @exception IOException */
115        protected void finalize() throws IOException {
116            if (raFile != null) raFile.close();
117            file = null;
118        }
119    
120        /** Static method to test if a disk file possibly is in FITS format.
121         *  The test is trivial in the sense that the file may not be a correct
122         *  FITS file even if 'true' is returned.  On the other hand, it is
123         *  certainly not a FITS file if 'false' is returned.
124         *
125         *  @param file  disk file */
126        public static boolean isFitsFile(File file) {
127            int nb = 0;
128            byte[] card = new byte[Fits.CARD];
129            try {
130                RandomAccessFile raf = new RandomAccessFile(file, "r");
131                nb = raf.read(card);
132                raf.close();
133            } catch (IOException e) {
134                return false;
135            }
136            if (nb<Fits.CARD) {
137                return false;
138            }
139            String str = new String(card);
140            return str.startsWith("SIMPLE  = ");
141        }
142    
143        /** Static method to test if a disk file possibly is in FITS format.
144         *  The test is trivial in the sense that the file may not be a correct
145         *  FITS file even if 'true' is returned.  On the other hand, it is
146         *  certainly not a FITS file if 'false' is returned.
147         *
148         *  @param filename name of disk file */
149        public static boolean isFitsFile(String filename) {
150            return isFitsFile(new File(filename));
151        }
152    
153        /** Add new HDUnit to FITS file.
154         *
155         *  @param  hdu  FitsHDUnit to be added */
156        public void addHDUnit(FitsHDUnit hdu) {
157            hdUnits.addElement(hdu);
158            changeHDU = true;
159        }
160    
161        /** Insert new HDUnit to FITS file at specified location.
162         *
163         *  @param  hdu  FitsHDUnit to be inserted
164         *  @param  index  location at which hte HDU should be inserted */
165        public void insertHDUnitAt(FitsHDUnit hdu, int index) {
166            hdUnits.insertElementAt(hdu, index);
167            changeHDU = true;
168        }
169    
170        /** Remove HDUnit with given location from FITS file.
171         *
172         *  @param  index  location of the HDU to be removed  */
173        public void removeHDUnitAt(int index) {
174            hdUnits.removeElementAt(index);
175            changeHDU = true;
176        }
177    
178        /** Get HDUnit in FitsFile by its position.  If the position is
179         *  less that 0, the first HDU is returned while the last is given
180         *  for positions beyond the actual number.
181         *
182         *  @param  no  number of HDUnit to retrieve (starting with 0) */
183        final public FitsHDUnit getHDUnit(int no){
184            int  n = 0;
185    
186            if (no<0) {
187                return (FitsHDUnit) hdUnits.firstElement();
188            } else if (no>=hdUnits.size()) {
189                return (FitsHDUnit) hdUnits.lastElement();
190            }
191    
192            FitsHDUnit   hdu;
193            Enumeration  enumeration = hdUnits.elements();
194            while (enumeration.hasMoreElements()) {
195                hdu = (FitsHDUnit) enumeration.nextElement();
196                if (n==no) return hdu;
197                n++;
198            }
199            return (FitsHDUnit) hdUnits.lastElement();
200        }
201    
202        /** Save changes made to a FITS file on disk. The FitsFile must have
203         *  been creaded from a read/write RandomAccess disk file.
204         *  Further, headers and data must fit into the original file.
205         *  Not check is done to verify the correctness of the FITS headers.
206         *
207         *  @exception IOException, FitsException  */
208        public void saveFile() throws IOException,FitsException {
209            if (changeHDU) {
210                throw new FitsException("HD Units of file have been changes",
211                                        FitsException.FILE);
212            }
213            Enumeration  enumeration = hdUnits.elements();
214            while (enumeration.hasMoreElements()) {
215                if (!((FitsHDUnit) enumeration.nextElement()).canSave()) {
216                    throw new FitsException("No space in FITS header",
217                                            FitsException.NOHEADERSPACE);
218                }
219            }
220    
221            RandomAccessFile raf = new RandomAccessFile(file, "rw");
222    
223            enumeration = hdUnits.elements();
224            while (enumeration.hasMoreElements()) {
225                ((FitsHDUnit)enumeration.nextElement()).saveFile(raf);
226            }
227            raf.close();
228        }
229    
230        /** Write FITS file to a DataOutput stream. Not check is done to verify
231         *  the correctness of the FITS headers.
232         *
233         *  @param  file the data output to be written
234         *  @exception IOException, FitsException  */
235        public void writeFile(DataOutput file) throws IOException,FitsException {
236            Enumeration  enumeration = hdUnits.elements();
237            while (enumeration.hasMoreElements()) {
238                ((FitsHDUnit)enumeration.nextElement()).writeFile(file);
239            }
240        }
241    
242        /** Write FITS file on a new diskfile. Not check is done to verify
243         *  the correctness of the FITS headers.
244         *
245         *  @param  file  new file to be written
246         *  @exception IOException, FitsException  */
247        public void writeFile(File file) throws IOException,FitsException {
248            if (file == null) {
249                throw new FitsException("Cannot write to null-pointer file",
250                                        FitsException.FILE);
251            }
252            if (file.exists()) {
253                if (!file.isFile()) {
254                    throw new FitsException("Cannot overwrite special file",
255                                            FitsException.FILE);
256                }
257                if ((this.file != null)
258                    && this.file.getCanonicalPath().equals(
259                                                file.getCanonicalPath())) {
260                    throw new FitsException("Cannot overwrite itself",
261                                            FitsException.FILE);
262                }
263            }
264            RandomAccessFile raf = new RandomAccessFile(file, "rw");
265            writeFile(raf);
266            raf.close();
267        }
268    
269        /** Write FITS file on a new diskfile. Not check is done to verify
270         *  the correctness of the FITS headers.
271         *
272         *  @param  filename  name of new file to be written
273         *  @exception IOException, FitsException  */
274        public void writeFile(String filename) throws IOException,FitsException {
275            writeFile(new File(filename));
276        }
277    
278        /** Remove all references to associated DataInput files. */
279        public void closeFile() {
280            Enumeration  enumeration = hdUnits.elements();
281            while (enumeration.hasMoreElements()) {
282                ((FitsHDUnit)enumeration.nextElement()).closeFile();
283            }
284            if (raFile != null) {
285                try {
286                    raFile.close();
287                } catch (IOException e) {
288                }
289            }
290            this.file = null;
291        }
292    
293        /** Gets numnber of HDUnits in FITS file */
294        final public int getNoHDUnits(){
295            return hdUnits.size();
296        }
297    
298        /** Gets Canonical path of FITS file */
299        public String getName(){
300            String name = "";
301            if (file != null) {
302                try {
303                    name = file.getCanonicalPath();
304                } catch (IOException e) {
305                    name = file.getAbsolutePath();
306                }
307            }
308            return name;
309      }
310    
311        /** Gets file identifier for FITS file */
312        public File getFile(){
313            return file;
314      }
315    }
316    
317    
318    
319