View Javadoc
1   /*
2    * #%L
3    * nom.tam FITS library
4    * %%
5    * Copyright (C) 1996 - 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.IOException;
36  import java.lang.reflect.Array;
37  
38  import nom.tam.fits.FitsFactory;
39  import nom.tam.util.type.ElementType;
40  
41  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
42  
43  /**
44   * Decodes FITS-formatted binary data into Java arrays (<i>primarily for internal use</i>)
45   *
46   * @since 1.16
47   *
48   * @see   FitsEncoder
49   * @see   FitsInputStream
50   * @see   FitsFile
51   */
52  public class FitsDecoder extends InputDecoder {
53  
54      /**
55       * The FITS byte value for the binary representation of a boolean 'true' value
56       */
57      private static final byte FITS_TRUE = (byte) 'T';
58  
59      /**
60       * Instantiates a new decoder of FITS binary data to Java arrays. To be used by subclass constructors only.
61       */
62      protected FitsDecoder() {
63          super();
64      }
65  
66      /**
67       * Instantiates a new FITS binary data decoder for converting FITS data representations into Java arrays.
68       *
69       * @param i the FITS input.
70       */
71      public FitsDecoder(InputReader i) {
72          super(i);
73      }
74  
75      /**
76       * Gets the <code>boolean</code> equivalent for a FITS byte value representing a logical value. This call does not
77       * support <code>null</code> values, which are allowed by the FITS standard, but the similar
78       * {@link #booleanObjectFor(int)} does. FITS defines 'T' as true, 'F' as false, and 0 as null. However, prior
79       * versions of this library have used the value 1 for true, and 0 for false. Therefore, this implementation will
80       * recognise both 'T' and 1 as <code>true</code>, and will return <code>false</code> for all other byte values.
81       *
82       * @param  c The FITS byte that defines a boolean value
83       *
84       * @return   <code>true</code> if and only if the byte is the ASCII character 'T' or has the value of 1, otherwise
85       *               <code>false</code>.
86       *
87       * @see      #booleanObjectFor(int)
88       */
89      public static final boolean booleanFor(int c) {
90          return c == FITS_TRUE || c == 1;
91      }
92  
93      /**
94       * Gets the <code>boolean</code> equivalent for a FITS byte value representing a logical value. This call supports
95       * <code>null</code> values, which are allowed by the FITS standard. FITS defines 'T' as true, 'F' as false, and 0
96       * as null. Prior versions of this library have used the value 1 for true, and 0 for false. Therefore, this
97       * implementation will recognise both 'T' and 1 as <code>true</code>, but 0 will map to <code>null</code> and
98       * everything else will return <code>false</code>.
99       *
100      * @param  c The FITS byte that defines a boolean value
101      *
102      * @return   <code>true</code> if and only if the byte is the ASCII character 'T' or has the value of 1,
103      *               <code>null</code> it the byte is 0, otherwise <code>false</code>.
104      *
105      * @see      #booleanFor(int)
106      */
107     @SuppressFBWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "null values are explicitly allowed by FITS, so we want to support them.")
108     public static final Boolean booleanObjectFor(int c) {
109         if (c == 0) {
110             return null;
111         }
112         return booleanFor(c);
113     }
114 
115     /**
116      * @deprecated              (<i>for internal use</i>) Low-level reading/writing should be handled internally as
117      *                              arrays by this library only.
118      *
119      * @return                  the next boolean value from the input.
120      *
121      * @throws     EOFException if already at the end of file.
122      * @throws     IOException  if there was an IO error reading from the input.
123      */
124     @Deprecated
125     protected synchronized boolean readBoolean() throws EOFException, IOException {
126         return booleanFor(readByte());
127     }
128 
129     /**
130      * @deprecated              (<i>for internal use</i>) Low-level reading/writing should be handled internally as
131      *                              arrays by this library only.
132      *
133      * @return                  the next character value from the input.
134      *
135      * @throws     EOFException if already at the end of file.
136      * @throws     IOException  if there was an IO error reading from the input.
137      */
138     @Deprecated
139     protected synchronized char readChar() throws EOFException, IOException {
140         int b = FitsFactory.isUseUnicodeChars() ? readUnsignedShort() : read();
141         if (b < 0) {
142             throw new EOFException();
143         }
144         return (char) b;
145     }
146 
147     /**
148      * @deprecated             (<i>for internal use</i>) Low-level reading/writing should be handled internally as
149      *                             arrays by this library only.
150      *
151      * @return                 the next byte the input.
152      *
153      * @throws     IOException if there was an IO error reading from the input.
154      */
155     @Deprecated
156     protected final byte readByte() throws IOException {
157         int i = read();
158         if (i < 0) {
159             throw new EOFException();
160         }
161         return (byte) i;
162     }
163 
164     /**
165      * @deprecated             (<i>for internal use</i>) Low-level reading/writing should be handled internally as
166      *                             arrays by this library only.
167      *
168      * @return                 the next unsigned byte from the input, or -1 if there is no more bytes available.
169      *
170      * @throws     IOException if there was an IO error reading from the input, other than the end-of-file.
171      */
172     @Deprecated
173     protected synchronized int readUnsignedByte() throws IOException {
174         return read();
175     }
176 
177     /**
178      * @deprecated              (<i>for internal use</i>) Low-level reading/writing should be handled internally as
179      *                              arrays by this library only.
180      *
181      * @return                  the next 16-bit integer value from the input.
182      *
183      * @throws     EOFException if already at the end of file.
184      * @throws     IOException  if there was an IO error reading from the input.
185      */
186     @Deprecated
187     protected final short readShort() throws EOFException, IOException {
188         int i = readUnsignedShort();
189         if (i < 0) {
190             throw new EOFException();
191         }
192         return (short) i;
193     }
194 
195     /**
196      * @deprecated             (<i>for internal use</i>) Low-level reading/writing should be handled internally as
197      *                             arrays by this library only.
198      *
199      * @return                 the next unsigned 16-bit integer value from the input, or -1 if reached the end of stream
200      *
201      * @throws     IOException if there was an IO error reading from the input.
202      */
203     @Deprecated
204     protected synchronized int readUnsignedShort() throws IOException {
205         getInputBuffer().loadOne(Short.BYTES);
206         return getInputBuffer().getUnsignedShort();
207     }
208 
209     /**
210      * @deprecated              (<i>for internal use</i>) Low-level reading/writing should be handled internally as
211      *                              arrays by this library only.
212      *
213      * @return                  the next 32-bit integer value from the input.
214      *
215      * @throws     EOFException if already at the end of file.
216      * @throws     IOException  if there was an IO error reading from the input.
217      */
218     @Deprecated
219     protected synchronized int readInt() throws EOFException, IOException {
220         getInputBuffer().loadOne(Integer.BYTES);
221         return getInputBuffer().getInt();
222     }
223 
224     /**
225      * @deprecated              (<i>for internal use</i>) Low-level reading/writing should be handled internally as
226      *                              arrays by this library only.
227      *
228      * @return                  the next 64-bit integer value from the input.
229      *
230      * @throws     EOFException if already at the end of file.
231      * @throws     IOException  if there was an IO error reading from the input.
232      */
233     @Deprecated
234     protected synchronized long readLong() throws EOFException, IOException {
235         getInputBuffer().loadOne(Long.BYTES);
236         return getInputBuffer().getLong();
237     }
238 
239     /**
240      * @deprecated              (<i>for internal use</i>) Low-level reading/writing should be handled internally as
241      *                              arrays by this library only.
242      *
243      * @return                  the next single-precision (32-bit) floating point value from the input.
244      *
245      * @throws     EOFException if already at the end of file.
246      * @throws     IOException  if there was an IO error reading from the input.
247      */
248     @Deprecated
249     protected synchronized float readFloat() throws EOFException, IOException {
250         getInputBuffer().loadOne(Float.BYTES);
251         return getInputBuffer().getFloat();
252     }
253 
254     /**
255      * @deprecated              (<i>for internal use</i>) Low-level reading/writing should be handled internally as
256      *                              arrays by this library only.
257      *
258      * @return                  the next double-precision (64-bit) floating point value from the input.
259      *
260      * @throws     EOFException if already at the end of file.
261      * @throws     IOException  if there was an IO error reading from the input.
262      */
263     @Deprecated
264     protected synchronized double readDouble() throws EOFException, IOException {
265         getInputBuffer().loadOne(Double.BYTES);
266         return getInputBuffer().getDouble();
267     }
268 
269     /**
270      * @deprecated              (<i>for internal use</i>) Low-level reading/writing should be handled internally as
271      *                              arrays by this library only.
272      *
273      * @return                  the next line of 1-byte ASCII characters, terminated by a LF or EOF.
274      *
275      * @throws     EOFException if already at the end of file.
276      * @throws     IOException  if there was an IO error reading from the input.
277      */
278     @Deprecated
279     protected synchronized String readAsciiLine() throws EOFException, IOException {
280         StringBuffer str = new StringBuffer();
281         for (;;) {
282             int c = read();
283             if (c < 0) {
284                 if (str.length() > 0) {
285                     break;
286                 }
287                 throw new EOFException();
288             }
289             if (c == '\n') {
290                 break;
291             }
292             str.append((char) c);
293         }
294         return new String(str);
295     }
296 
297     /**
298      * See {@link ArrayDataInput#read(boolean[], int, int)} for the general contract of this method. In FITS,
299      * <code>true</code> values are represented by the ASCII byte for 'T', whereas <code>false</code> is represented by
300      * the ASCII byte for 'F'.
301      *
302      * @param  b            an array of boolean values.
303      * @param  start        the buffer index at which to start reading data
304      * @param  length       the total number of elements to read.
305      *
306      * @return              the number of bytes successfully read.
307      *
308      * @throws EOFException if already at the end of file.
309      * @throws IOException  if there was an IO error before, before requested number of bytes could be read
310      */
311     protected synchronized int read(boolean[] b, int start, int length) throws EOFException, IOException {
312         if (length == 0) {
313             return 0;
314         }
315 
316         byte[] ascii = new byte[length];
317         length = read(ascii, 0, length);
318 
319         if (length < 0) {
320             throw new EOFException();
321         }
322 
323         for (int i = 0; i < length; i++) {
324             b[start + i] = booleanFor(ascii[i]);
325         }
326 
327         return length;
328     }
329 
330     /**
331      * See {@link ArrayDataInput#read(Boolean[], int, int)} for the general contract of this method. In FITS,
332      * <code>true</code> values are represented by the ASCII byte for 'T', <code>false</code> is represented by the
333      * ASCII byte for 'F', while <code>null</code> values are represented by the value 0.
334      *
335      * @param  b            an array of boolean values.
336      * @param  start        the buffer index at which to start reading data
337      * @param  length       the total number of elements to read.
338      *
339      * @return              the number of bytes successfully read.
340      *
341      * @throws EOFException if already at the end of file.
342      * @throws IOException  if there was an IO error before, before requested number of bytes could be read
343      */
344     protected synchronized int read(Boolean[] b, int start, int length) throws EOFException, IOException {
345         if (length == 0) {
346             return 0;
347         }
348 
349         byte[] ascii = new byte[length];
350         length = read(ascii, 0, length);
351 
352         if (length < 0) {
353             throw new EOFException();
354         }
355 
356         for (int i = 0; i < length; i++) {
357             b[start + i] = booleanObjectFor(ascii[i]);
358         }
359 
360         return length;
361     }
362 
363     /**
364      * See {@link ArrayDataInput#read(char[], int, int)} for the general contract of this method. In FITS characters are
365      * usually represented as 1-byte ASCII, not as the 2-byte Java types. However, previous implementations if this
366      * library have erroneously written 2-byte characters into the FITS. For compatibility both the FITS standard
367      * -1-byte ASCII and the old 2-byte behaviour are supported, and can be selected via
368      * {@link FitsFactory#setUseUnicodeChars(boolean)}.
369      *
370      * @param  c            a character array.
371      * @param  start        the buffer index at which to start reading data
372      * @param  length       the total number of elements to read.
373      *
374      * @return              the number of bytes successfully read.
375      *
376      * @throws EOFException if already at the end of file.
377      * @throws IOException  if there was an IO error before, before requested number of bytes could be read
378      *
379      * @see                 FitsFactory#setUseUnicodeChars(boolean)
380      */
381     protected synchronized int read(char[] c, int start, int length) throws EOFException, IOException {
382         if (length == 0) {
383             return 0;
384         }
385 
386         if (ElementType.CHAR.size() == 1) {
387             byte[] ascii = new byte[length];
388             length = read(ascii, 0, length);
389 
390             if (length < 0) {
391                 throw new EOFException();
392             }
393 
394             for (int i = 0; i < length; i++) {
395                 c[start + i] = (char) (ascii[i] & FitsIO.BYTE_MASK);
396             }
397         } else {
398             getInputBuffer().loadBytes(length, Short.BYTES);
399             short[] s = new short[length];
400             length = getInputBuffer().get(s, 0, length);
401             for (int i = 0; i < length; i++) {
402                 c[start + i] = (char) (s[i] & FitsIO.SHORT_MASK);
403             }
404         }
405 
406         return length * ElementType.CHAR.size();
407     }
408 
409     /**
410      * See {@link ArrayDataInput#read(short[], int, int)} for a contract of this method.
411      *
412      * @param  s            an array of 16-bit integer values.
413      * @param  start        the buffer index at which to start reading data
414      * @param  length       the total number of elements to read.
415      *
416      * @return              the number of bytes successfully read.
417      *
418      * @throws EOFException if already at the end of file.
419      * @throws IOException  if there was an IO error before, before requested number of bytes could be read
420      */
421     protected synchronized int read(short[] s, int start, int length) throws EOFException, IOException {
422         getInputBuffer().loadBytes(length, Short.BYTES);
423         return getInputBuffer().get(s, start, length) * Short.BYTES;
424     }
425 
426     /**
427      * See {@link ArrayDataInput#read(int[], int, int)} for a contract of this method.
428      *
429      * @param  j            an array of 32-bit integer values.
430      * @param  start        the buffer index at which to start reading data
431      * @param  length       the total number of elements to read.
432      *
433      * @return              the number of bytes successfully read.
434      *
435      * @throws EOFException if already at the end of file.
436      * @throws IOException  if there was an IO error before, before requested number of bytes could be read
437      */
438     protected synchronized int read(int[] j, int start, int length) throws EOFException, IOException {
439         getInputBuffer().loadBytes(length, Integer.BYTES);
440         return getInputBuffer().get(j, start, length) * Integer.BYTES;
441     }
442 
443     /**
444      * See {@link ArrayDataInput#read(long[], int, int)} for a contract of this method.
445      *
446      * @param  l            an array of 64-bit integer values.
447      * @param  start        the buffer index at which to start reading data
448      * @param  length       the total number of elements to read.
449      *
450      * @return              the number of bytes successfully read.
451      *
452      * @throws EOFException if already at the end of file.
453      * @throws IOException  if there was an IO error before, before requested number of bytes could be read
454      */
455     protected synchronized int read(long[] l, int start, int length) throws EOFException, IOException {
456         getInputBuffer().loadBytes(length, Long.BYTES);
457         return getInputBuffer().get(l, start, length) * Long.BYTES;
458     }
459 
460     /**
461      * See {@link ArrayDataInput#read(float[], int, int)} for a contract of this method.
462      *
463      * @param  f            an array of single-precision (32-bit) floating point values.
464      * @param  start        the buffer index at which to start reading data
465      * @param  length       the total number of elements to read.
466      *
467      * @return              the number of bytes successfully read.
468      *
469      * @throws EOFException if already at the end of file.
470      * @throws IOException  if there was an IO error before, before requested number of bytes could be read
471      */
472     protected synchronized int read(float[] f, int start, int length) throws EOFException, IOException {
473         getInputBuffer().loadBytes(length, Float.BYTES);
474         return getInputBuffer().get(f, start, length) * Float.BYTES;
475     }
476 
477     /**
478      * See {@link ArrayDataInput#read(double[], int, int)} for a contract of this method.
479      *
480      * @param  d            an array of double-precision (64-bit) floating point values.
481      * @param  start        the buffer index at which to start reading data
482      * @param  length       the total number of elements to read.
483      *
484      * @return              the number of bytes successfully read.
485      *
486      * @throws EOFException if already at the end of file.
487      * @throws IOException  if there was an IO error before, before requested number of bytes could be read
488      */
489     protected synchronized int read(double[] d, int start, int length) throws EOFException, IOException {
490         getInputBuffer().loadBytes(length, Double.BYTES);
491         return getInputBuffer().get(d, start, length) * Double.BYTES;
492     }
493 
494     @Override
495     public synchronized long readArray(Object o) throws IOException, IllegalArgumentException {
496         if (o == null) {
497             return 0L;
498         }
499         if (!o.getClass().isArray()) {
500             throw new IllegalArgumentException("Not an array: " + o.getClass().getName());
501         }
502 
503         int length = Array.getLength(o);
504         if (length == 0) {
505             return 0L;
506         }
507 
508         // This is a 1-d array. Process it using our special
509         // functions.
510         if (o instanceof byte[]) {
511             readFully((byte[]) o, 0, length);
512             return length;
513         }
514         if (o instanceof boolean[]) {
515             return read((boolean[]) o, 0, length);
516         }
517         if (o instanceof char[]) {
518             return read((char[]) o, 0, length);
519         }
520         if (o instanceof short[]) {
521             return read((short[]) o, 0, length);
522         }
523         if (o instanceof int[]) {
524             return read((int[]) o, 0, length);
525         }
526         if (o instanceof float[]) {
527             return read((float[]) o, 0, length);
528         }
529         if (o instanceof long[]) {
530             return read((long[]) o, 0, length);
531         }
532         if (o instanceof double[]) {
533             return read((double[]) o, 0, length);
534         }
535         if (o instanceof Boolean[]) {
536             return read((Boolean[]) o, 0, length);
537         }
538 
539         Object[] array = (Object[]) o;
540         long count = 0L;
541 
542         // Process multidim arrays recursively.
543         for (int i = 0; i < length; i++) {
544             try {
545                 count += readArray(array[i]);
546             } catch (EOFException e) {
547                 return eofCheck(e, count, -1L);
548             }
549         }
550         return count;
551     }
552 
553 }