View Javadoc
1   /*
2    * #%L
3    * nom.tam FITS library
4    * %%
5    * Copyright (C) 2004 - 2024 nom-tam-fits
6    * %%
7    * This is free and unencumbered software released into the public domain.
8    *
9    * Anyone is free to copy, modify, publish, use, compile, sell, or
10   * distribute this software, either in source code form or as a compiled
11   * binary, for any purpose, commercial or non-commercial, and by any
12   * means.
13   *
14   * In jurisdictions that recognize copyright laws, the author or authors
15   * of this software dedicate any and all copyright interest in the
16   * software to the public domain. We make this dedication for the benefit
17   * of the public at large and to the detriment of our heirs and
18   * successors. We intend this dedication to be an overt act of
19   * relinquishment in perpetuity of all present and future rights to this
20   * software under copyright law.
21   *
22   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25   * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
26   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
27   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28   * OTHER DEALINGS IN THE SOFTWARE.
29   * #L%
30   */
31  
32  package nom.tam.util;
33  
34  import java.io.EOFException;
35  import java.io.File;
36  import java.io.IOException;
37  import java.util.logging.Level;
38  import java.util.logging.Logger;
39  
40  /**
41   * For reading and writing FITS files. It provides buffered random access and efficient handling of arrays. Primitive
42   * arrays may be written using a single method call. Large buffers are used to minimize synchronization overheads since
43   * methods of this class are not synchronized.
44   * <p>
45   * Note that although this class supports most of the contract of RandomAccessFile it does not (and can not) extend that
46   * class since many of the methods of RandomAccessFile are final. In practice this method works much like the
47   * StreamFilter classes. All methods are implemented in this class but some are simply delegated to an underlying
48   * RandomAccessFile member.
49   * <p>
50   * Testing and timing routines are available in the nom.tam.util.test.BufferedFileTester class.
51   * <p>
52   * Prior versions of this class under the old <code>BufferedFile</code> name:
53   * <ul>
54   * <li>Version 1.1 -- October 12, 2000: Fixed handling of EOF in array reads so that a partial array will be returned
55   * when an EOF is detected. Excess bytes that cannot be used to construct array elements will be discarded (e.g., if
56   * there are 2 bytes left and the user is reading an int array).</li>
57   * <li>Version 1.2 -- December 8, 2002: Added getChannel method.</li>
58   * <li>Version 1.3 -- March 2, 2007: Added File based constructors.</li>
59   * <li>Version 1.4 -- July 20, 2009: Added support for &gt;2G Object reads. This is still a bit problematic in that we
60   * do not support primitive arrays larger than 2 GB/atomsize. However except in the case of bytes this is not currently
61   * a major issue.</li>
62   * </ul>
63   * <p>
64   * Version 2.0 -- Oct 30, 2021: New hierarchy for more digestible code. Improved buffering, and renamed from
65   * <code>BufferedFile</code> to the more appropriate name of <code>FitsFile</code>. Performance is 2-4 times better than
66   * before.
67   *
68   * @see   FitsInputStream
69   * @see   FitsOutputStream
70   *
71   * @since 1.16
72   */
73  @SuppressWarnings("deprecation")
74  public class FitsFile extends ArrayDataFile implements FitsOutput, RandomAccess {
75  
76      private static final Logger LOG = Logger.getLogger(FitsFile.class.getName());
77  
78      /**
79       * marker position
80       */
81      private long marker;
82  
83      /**
84       * Create a buffered file from a File descriptor
85       *
86       * @param  file        the file to open.
87       *
88       * @throws IOException if the file could not be opened
89       */
90      public FitsFile(File file) throws IOException {
91          this(file, "r", FitsIO.DEFAULT_BUFFER_SIZE);
92      }
93  
94      /**
95       * Create a buffered file from a File descriptor
96       *
97       * @param  file        the file to open.
98       * @param  mode        the mode to open the file in
99       *
100      * @throws IOException if the file could not be opened
101      */
102     public FitsFile(File file, String mode) throws IOException {
103         this(file, mode, FitsIO.DEFAULT_BUFFER_SIZE);
104     }
105 
106     /**
107      * Create a buffered file from a file descriptor
108      *
109      * @param  file        the file to open.
110      * @param  mode        the mode to open the file in
111      * @param  bufferSize  the dataBuffer.buffer size to use
112      *
113      * @throws IOException if the file could not be opened
114      */
115     public FitsFile(File file, String mode, int bufferSize) throws IOException {
116         super(file, mode, bufferSize);
117         setDecoder(new FitsDecoder(this));
118         setEncoder(new FitsEncoder(this));
119     }
120 
121     /**
122      * Create a buffered file from a random access data object.
123      *
124      * @param  src         random access data
125      * @param  bufferSize  the dataBuffer's buffer size to use
126      *
127      * @throws IOException if data could not be read
128      */
129     public FitsFile(RandomAccessFileIO src, int bufferSize) throws IOException {
130         super(src, bufferSize);
131         setDecoder(new FitsDecoder(this));
132         setEncoder(new FitsEncoder(this));
133     }
134 
135     /**
136      * Create a read-only buffered file
137      *
138      * @param  filename    the name of the file to open
139      *
140      * @throws IOException if the file could not be opened
141      */
142     public FitsFile(String filename) throws IOException {
143         this(filename, "r", FitsIO.DEFAULT_BUFFER_SIZE);
144     }
145 
146     /**
147      * Create a buffered file with the given mode.
148      *
149      * @param  filename    The file to be accessed.
150      * @param  mode        A string composed of "r" and "w" for read and write access.
151      *
152      * @throws IOException if the file could not be opened
153      */
154     public FitsFile(String filename, String mode) throws IOException {
155         this(filename, mode, FitsIO.DEFAULT_BUFFER_SIZE);
156     }
157 
158     /**
159      * Create a buffered file with the given mode and a specified dataBuffer.buffer size.
160      *
161      * @param  filename    The file to be accessed.
162      * @param  mode        A string composed of "r" and "w" indicating read or write access.
163      * @param  bufferSize  The dataBuffer.buffer size to be used. This should be substantially larger than 100 bytes and
164      *                         defaults to 32768 bytes in the other constructors.
165      *
166      * @throws IOException if the file could not be opened
167      */
168     public FitsFile(String filename, String mode, int bufferSize) throws IOException {
169         this(new File(filename), mode, bufferSize);
170     }
171 
172     @Override
173     public FitsEncoder getEncoder() {
174         return (FitsEncoder) super.getEncoder();
175     }
176 
177     @Override
178     public FitsDecoder getDecoder() {
179         return (FitsDecoder) super.getDecoder();
180     }
181 
182     @Override
183     public boolean isAtStart() {
184         return getFilePointer() == 0;
185     }
186 
187     @Override
188     public final int readUnsignedByte() throws IOException {
189         return getDecoder().readUnsignedByte();
190     }
191 
192     @Override
193     public final byte readByte() throws IOException {
194         return getDecoder().readByte();
195     }
196 
197     @Override
198     public boolean readBoolean() throws IOException {
199         return getDecoder().readBoolean();
200     }
201 
202     @Override
203     public char readChar() throws IOException {
204         return getDecoder().readChar();
205     }
206 
207     @Override
208     public final int readUnsignedShort() throws IOException {
209         return getDecoder().readUnsignedShort();
210     }
211 
212     @Override
213     public final short readShort() throws IOException {
214         return getDecoder().readShort();
215     }
216 
217     @Override
218     public final int readInt() throws IOException {
219         return getDecoder().readInt();
220     }
221 
222     @Override
223     public final long readLong() throws IOException {
224         return getDecoder().readLong();
225     }
226 
227     @Override
228     public final float readFloat() throws IOException {
229         return getDecoder().readFloat();
230     }
231 
232     @Override
233     public final double readDouble() throws IOException {
234         return getDecoder().readDouble();
235     }
236 
237     @Override
238     public String readLine() throws IOException {
239         return getDecoder().readAsciiLine();
240     }
241 
242     @Override
243     public int read(boolean[] b, int start, int length) throws IOException {
244         return getDecoder().read(b, start, length);
245     }
246 
247     @Override
248     public int read(Boolean[] buf, int offset, int size) throws IOException {
249         return getDecoder().read(buf, offset, size);
250     }
251 
252     @Override
253     public int read(char[] c, int start, int length) throws IOException {
254         return getDecoder().read(c, start, length);
255     }
256 
257     @Override
258     public int read(short[] s, int start, int length) throws IOException {
259         return getDecoder().read(s, start, length);
260     }
261 
262     @Override
263     public int read(int[] i, int start, int length) throws IOException {
264         return getDecoder().read(i, start, length);
265     }
266 
267     @Override
268     public int read(long[] l, int start, int length) throws IOException {
269         return getDecoder().read(l, start, length);
270     }
271 
272     @Override
273     public int read(float[] f, int start, int length) throws IOException {
274         return getDecoder().read(f, start, length);
275     }
276 
277     @Override
278     public int read(double[] d, int start, int length) throws IOException {
279         return getDecoder().read(d, start, length);
280     }
281 
282     @Deprecated
283     @Override
284     public final int readArray(Object o) throws IOException {
285         return (int) readLArray(o);
286     }
287 
288     @Override
289     public boolean markSupported() {
290         return true;
291     }
292 
293     @Override
294     public void mark(int readlimit) throws IOException {
295         marker = getFilePointer();
296         if (!hasAvailable(readlimit)) {
297             FitsFile.LOG.log(Level.FINE, "mark over file limit, so read as far as possible.");
298         }
299     }
300 
301     @Override
302     public void reset() throws IOException {
303         seek(marker);
304     }
305 
306     @Override
307     public int skipBytes(int toSkip) throws IOException {
308         return (int) skip(toSkip);
309     }
310 
311     @Override
312     public void skipAllBytes(long toSkip) throws EOFException, IOException {
313         long n = skip(toSkip);
314 
315         // Note that we allow negative skips...
316         if (n != toSkip) {
317             throw new EOFException("Skip reached file boundary at " + n + " of " + toSkip);
318         }
319     }
320 
321     @Override
322     public final void writeByte(int v) throws IOException {
323         getEncoder().writeByte(v);
324     }
325 
326     @Override
327     public void writeBoolean(boolean v) throws IOException {
328         getEncoder().writeBoolean(v);
329     }
330 
331     @Override
332     public void writeChar(int v) throws IOException {
333         getEncoder().writeChar(v);
334     }
335 
336     @Override
337     public final void writeShort(int v) throws IOException {
338         getEncoder().writeShort(v);
339     }
340 
341     @Override
342     public final void writeInt(int v) throws IOException {
343         getEncoder().writeInt(v);
344     }
345 
346     @Override
347     public final void writeLong(long v) throws IOException {
348         getEncoder().writeLong(v);
349     }
350 
351     @Override
352     public final void writeFloat(float v) throws IOException {
353         getEncoder().writeFloat(v);
354     }
355 
356     @Override
357     public final void writeDouble(double v) throws IOException {
358         getEncoder().writeDouble(v);
359     }
360 
361     @Override
362     public final void writeBytes(String s) throws IOException {
363         getEncoder().writeBytes(s);
364     }
365 
366     @Override
367     public final void writeChars(String s) throws IOException {
368         getEncoder().writeChars(s);
369     }
370 
371     @Override
372     public void write(boolean[] b, int start, int length) throws IOException {
373         getEncoder().write(b, start, length);
374     }
375 
376     @Override
377     public void write(Boolean[] buf, int offset, int size) throws IOException {
378         getEncoder().write(buf, offset, size);
379     }
380 
381     @Override
382     public void write(char[] c, int start, int length) throws IOException {
383         getEncoder().write(c, start, length);
384     }
385 
386     @Override
387     public void write(short[] s, int start, int length) throws IOException {
388         getEncoder().write(s, start, length);
389     }
390 
391     @Override
392     public void write(int[] i, int start, int length) throws IOException {
393         getEncoder().write(i, start, length);
394     }
395 
396     @Override
397     public void write(long[] l, int start, int length) throws IOException {
398         getEncoder().write(l, start, length);
399     }
400 
401     @Override
402     public void write(float[] f, int start, int length) throws IOException {
403         getEncoder().write(f, start, length);
404     }
405 
406     @Override
407     public void write(double[] d, int start, int length) throws IOException {
408         getEncoder().write(d, start, length);
409     }
410 
411     @Override
412     public void write(String[] s, int start, int length) throws IOException {
413         getEncoder().write(s, start, length);
414     }
415 }