View Javadoc
1   package nom.tam.fits;
2   
3   import java.io.PrintStream;
4   
5   import nom.tam.fits.header.IFitsHeader;
6   import nom.tam.fits.header.Standard;
7   import nom.tam.util.ArrayDataOutput;
8   import nom.tam.util.ArrayFuncs;
9   import nom.tam.util.ColumnTable;
10  import nom.tam.util.Cursor;
11  
12  /*
13   * #%L
14   * nom.tam FITS library
15   * %%
16   * Copyright (C) 2004 - 2024 nom-tam-fits
17   * %%
18   * This is free and unencumbered software released into the public domain.
19   *
20   * Anyone is free to copy, modify, publish, use, compile, sell, or
21   * distribute this software, either in source code form or as a compiled
22   * binary, for any purpose, commercial or non-commercial, and by any
23   * means.
24   *
25   * In jurisdictions that recognize copyright laws, the author or authors
26   * of this software dedicate any and all copyright interest in the
27   * software to the public domain. We make this dedication for the benefit
28   * of the public at large and to the detriment of our heirs and
29   * successors. We intend this dedication to be an overt act of
30   * relinquishment in perpetuity of all present and future rights to this
31   * software under copyright law.
32   *
33   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
34   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
35   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
36   * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
37   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
38   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
39   * OTHER DEALINGS IN THE SOFTWARE.
40   * #L%
41   */
42  
43  import static nom.tam.fits.header.Standard.NAXIS1;
44  import static nom.tam.fits.header.Standard.NAXIS2;
45  import static nom.tam.fits.header.Standard.TDIMn;
46  import static nom.tam.fits.header.Standard.TDISPn;
47  import static nom.tam.fits.header.Standard.TFIELDS;
48  import static nom.tam.fits.header.Standard.TFORMn;
49  import static nom.tam.fits.header.Standard.TNULLn;
50  import static nom.tam.fits.header.Standard.TSCALn;
51  import static nom.tam.fits.header.Standard.TTYPEn;
52  import static nom.tam.fits.header.Standard.TUNITn;
53  import static nom.tam.fits.header.Standard.TZEROn;
54  import static nom.tam.fits.header.Standard.XTENSION;
55  
56  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
57  
58  /**
59   * Binary table header/data unit.
60   * 
61   * @see BinaryTable
62   * @see AsciiTableHDU
63   */
64  @SuppressWarnings("deprecation")
65  public class BinaryTableHDU extends TableHDU<BinaryTable> {
66  
67      /** The standard column keywords for a binary table. */
68      private static final IFitsHeader[] KEY_STEMS = {TTYPEn, TFORMn, TUNITn, TNULLn, TSCALn, TZEROn, TDISPn, TDIMn};
69  
70      /**
71       * Creates a new binary table HDU from the specified FITS header and associated table data
72       * 
73       * @deprecated       (<i>for internal use</i>) Its visibility should be reduced to package level in the future.
74       * 
75       * @param      hdr   the FITS header describing the data and any user-specific keywords
76       * @param      datum the corresponding data object
77       */
78      @Deprecated
79      public BinaryTableHDU(Header hdr, BinaryTable datum) {
80          super(hdr, datum);
81      }
82  
83      /**
84       * Wraps the specified table in an HDU, creating a header for it with the essential table description. Users may
85       * want to complete the table description with optional FITS keywords such as <code>TTYPEn</code>,
86       * <code>TUNITn</code> etc. It is strongly recommended that the table structure (rows or columns) isn't altered
87       * after the table is encompassed in an HDU, since there is no guarantee that the header description will be kept in
88       * sync.
89       * 
90       * @param  tab           the binary table to wrap into a new HDU
91       * 
92       * @return               A new HDU encompassing and describing the supplied table.
93       * 
94       * @throws FitsException if the table structure is invalid, and cannot be described in a header (should never really
95       *                           happen, but we keep the possibility open to it).
96       * 
97       * @see                  BinaryTable#toHDU()
98       * 
99       * @since                1.18
100      */
101     public static BinaryTableHDU wrap(BinaryTable tab) throws FitsException {
102         BinaryTableHDU hdu = new BinaryTableHDU(new Header(), tab);
103         tab.fillHeader(hdu.myHeader);
104         return hdu;
105     }
106 
107     @Override
108     protected final String getCanonicalXtension() {
109         return Standard.XTENSION_BINTABLE;
110     }
111 
112     /**
113      * @deprecated               (<i>for internal use</i>) Use {@link BinaryTable#fromColumnMajor(Object[])} or
114      *                               {@link BinaryTable#fromRowMajor(Object[][])} instead. Will reduce visibility in the
115      *                               future.
116      *
117      * @return                   Encapsulate data in a BinaryTable data type
118      *
119      * @param      o             data to encapsulate
120      *
121      * @throws     FitsException if the type of the data is not usable as data
122      */
123     @Deprecated
124     public static BinaryTable encapsulate(Object o) throws FitsException {
125         if (o instanceof ColumnTable) {
126             return new BinaryTable((ColumnTable<?>) o);
127         }
128         if (o instanceof Object[][]) {
129             return BinaryTable.fromRowMajor((Object[][]) o);
130         }
131         if (o instanceof Object[]) {
132             return BinaryTable.fromColumnMajor((Object[]) o);
133         }
134         throw new FitsException("Unable to encapsulate object of type:" + o.getClass().getName() + " as BinaryTable");
135     }
136 
137     /**
138      * Check if this data object is consistent with a binary table.
139      *
140      * @param      o a column table object, an Object[][], or an Object[]. This routine doesn't check that the
141      *                   dimensions of arrays are properly consistent.
142      * 
143      * @return       <code>true</code> if the data object can be represented as a FITS binary table, otherwise
144      *                   <code>false</code>.
145      *
146      * @deprecated   (<i>for internal use</i>) Will reduce visibility in the future
147      */
148     @SuppressFBWarnings(value = "HSM_HIDING_METHOD", justification = "deprecated existing method, kept for compatibility")
149     @Deprecated
150     public static boolean isData(Object o) {
151         return o instanceof nom.tam.util.ColumnTable || o instanceof Object[][] || o instanceof Object[];
152     }
153 
154     /**
155      * Check that this is a valid binary table header.
156      *
157      * @deprecated        (<i>for internal use</i>) Will reduce visibility in the future
158      *
159      * @param      header to validate.
160      *
161      * @return            <CODE>true</CODE> if this is a binary table header.
162      */
163     @SuppressFBWarnings(value = "HSM_HIDING_METHOD", justification = "deprecated existing method, kept for compatibility")
164     @Deprecated
165     public static boolean isHeader(Header header) {
166         String xten = header.getStringValue(XTENSION);
167         if (xten == null) {
168             return false;
169         }
170         xten = xten.trim();
171         return xten.equals(Standard.XTENSION_BINTABLE) || xten.equals("A3DTABLE");
172     }
173 
174     /**
175      * Prepares a data object into which the actual data can be read from an input subsequently or at a later time.
176      *
177      * @deprecated               (<i>for internal use</i>) Will reduce visibility in the future
178      *
179      * @param      header        The FITS header that describes the data
180      *
181      * @return                   A data object that support reading content from a stream.
182      *
183      * @throws     FitsException if the data could not be prepared to prescriotion.
184      */
185     @Deprecated
186     public static BinaryTable manufactureData(Header header) throws FitsException {
187         return new BinaryTable(header);
188     }
189 
190     /**
191      * @deprecated               (<i>for internal use</i>) Will reduce visibility in the future
192      *
193      * @return                   a newly created binary table HDU from the supplied data.
194      *
195      * @param      data          the data used to build the binary table. This is typically some kind of array of
196      *                               objects.
197      *
198      * @throws     FitsException if there was a problem with the data.
199      */
200     @Deprecated
201     public static Header manufactureHeader(Data data) throws FitsException {
202         Header hdr = new Header();
203         data.fillHeader(hdr);
204         return hdr;
205     }
206 
207     @Override
208     public int addColumn(Object data) throws FitsException {
209         int n = myData.addColumn(data);
210         myHeader.addValue(Standard.NAXISn.n(1), myData.getRowBytes());
211         Cursor<String, HeaderCard> c = myHeader.iterator();
212         c.end();
213         myData.fillForColumn(myHeader, c, n - 1);
214         return super.addColumn(data);
215     }
216 
217     /**
218      * For internal use. Returns the FITS header key stems to use for describing binary tables.
219      * 
220      * @return an array of standatd header colum knetwords stems.
221      */
222     protected static IFitsHeader[] binaryTableColumnKeyStems() {
223         return KEY_STEMS;
224     }
225 
226     @Override
227     protected IFitsHeader[] columnKeyStems() {
228         return BinaryTableHDU.KEY_STEMS;
229     }
230 
231     @Override
232     public void info(PrintStream stream) {
233         stream.println("  Binary Table");
234         stream.println("      Header Information:");
235 
236         int nhcol = myHeader.getIntValue(TFIELDS, -1);
237         int nrow = myHeader.getIntValue(NAXIS2, -1);
238         int rowsize = myHeader.getIntValue(NAXIS1, -1);
239 
240         stream.print("          " + nhcol + " fields");
241         stream.println(", " + nrow + " rows of length " + rowsize);
242 
243         for (int i = 1; i <= nhcol; i++) {
244             stream.print("           " + i + ":");
245             prtField(stream, "Name", TTYPEn.n(i).key());
246             prtField(stream, "Format", TFORMn.n(i).key());
247             prtField(stream, "Dimens", TDIMn.n(i).key());
248             stream.println("");
249         }
250 
251         stream.println("      Data Information:");
252         stream.println("          Number of rows=" + myData.getNRows());
253         stream.println("          Number of columns=" + myData.getNCols());
254         stream.println("          Heap size is: " + myData.getParameterSize() + " bytes");
255 
256         Object[] cols = myData.getFlatColumns();
257         for (int i = 0; i < cols.length; i++) {
258             stream.println("           " + i + ":" + ArrayFuncs.arrayDescription(cols[i]));
259         }
260     }
261 
262     /**
263      * Check that this HDU has a valid header.
264      *
265      * @deprecated (<i>for internal use</i>) Will reduce visibility in the future
266      *
267      * @return     <CODE>true</CODE> if this HDU has a valid header.
268      */
269     @Deprecated
270     public boolean isHeader() {
271         return isHeader(myHeader);
272     }
273 
274     private void prtField(PrintStream stream, String type, String field) {
275         String val = myHeader.getStringValue(field);
276         if (val != null) {
277             stream.print(type + '=' + val + "; ");
278         }
279     }
280 
281     /**
282      * Returns a copy of the column descriptor of a given column in this table
283      * 
284      * @param  col the zero-based column index
285      * 
286      * @return     a copy of the column's descriptor
287      * 
288      * @see        BinaryTable#getDescriptor(int)
289      * 
290      * @since      1.18
291      */
292     public BinaryTable.ColumnDesc getColumnDescriptor(int col) {
293         return myData.getDescriptor(col);
294     }
295 
296     @Override
297     public void setColumnName(int index, String name, String comment)
298             throws IndexOutOfBoundsException, HeaderCardException {
299         super.setColumnName(index, name, comment);
300         getColumnDescriptor(index).name(name);
301     }
302 
303     /**
304      * Converts a column from FITS logical values to bits. Null values (allowed in logical columns) will map to
305      * <code>false</code>. It is legal to call this on a column that is already containing bits.
306      *
307      * @param  col           The zero-based index of the column to be reset.
308      *
309      * @return               Whether the conversion was possible. *
310      * 
311      * @throws FitsException if the header could not be updated
312      * 
313      * @since                1.18
314      */
315     public final boolean convertToBits(int col) throws FitsException {
316         if (!myData.convertToBits(col)) {
317             return false;
318         }
319 
320         // Update TFORM keyword
321         myHeader.getCard(Standard.TFORMn.n(col + 1)).setValue(getColumnDescriptor(col).getTFORM());
322 
323         return true;
324     }
325 
326     /**
327      * Convert a column in the table to complex. Only tables with appropriate types and dimensionalities can be
328      * converted. It is legal to call this on a column that is already complex.
329      *
330      * @param  index         The zero-based index of the column to be converted.
331      *
332      * @return               Whether the column can be converted
333      *
334      * @throws FitsException if the header could not be updated
335      * 
336      * @see                  BinaryTableHDU#setComplexColumn(int)
337      */
338     public boolean setComplexColumn(int index) throws FitsException {
339         if (!myData.setComplexColumn(index)) {
340             return false;
341         }
342 
343         // Update TFORM keyword
344         myHeader.getCard(Standard.TFORMn.n(index + 1)).setValue(getColumnDescriptor(index).getTFORM());
345 
346         // Update or remove existing TDIM keyword
347         if (myHeader.containsKey(Standard.TDIMn.n(index + 1))) {
348             String tdim = getColumnDescriptor(index).getTDIM();
349             if (tdim != null) {
350                 myHeader.getCard(Standard.TDIMn.n(index + 1)).setValue(tdim);
351             } else {
352                 myHeader.deleteKey(Standard.TDIMn.n(index + 1));
353             }
354         }
355 
356         return true;
357     }
358 
359     // Need to tell header about the Heap before writing.
360     @Override
361     public void write(ArrayDataOutput out) throws FitsException {
362         myData.fillHeader(myHeader, false);
363         super.write(out);
364     }
365 }