Begin versioning.
[fits.git] / src / nom / tam / fits / BinaryTableHDU.java
1 package nom.tam.fits;
2
3 /*
4  * Copyright: Thomas McGlynn 1997-1998.
5  * This code may be used for any purpose, non-commercial
6  * or commercial so long as this copyright notice is retained
7  * in the source code or included in or referred to in any
8  * derived software.
9  *
10  * Many thanks to David Glowacki (U. Wisconsin) for substantial
11  * improvements, enhancements and bug fixes.
12  */
13 import nom.tam.util.ArrayFuncs;
14 import nom.tam.util.*;
15 import java.io.IOException;
16 import java.lang.reflect.Array;
17 import java.io.ByteArrayOutputStream;
18 import java.io.ByteArrayInputStream;
19
20 /** FITS binary table header/data unit */
21 public class BinaryTableHDU
22         extends TableHDU {
23
24     private BinaryTable table;
25     /** The standard column keywords for a binary table. */
26     private String[] keyStems = {"TTYPE", "TFORM", "TUNIT", "TNULL", "TSCAL", "TZERO", "TDISP", "TDIM"};
27
28     public BinaryTableHDU(Header hdr, Data datum) {
29
30         super((TableData) datum);
31         myHeader = hdr;
32         myData = datum;
33         table = (BinaryTable) datum;
34
35     }
36
37     /** Create data from a binary table header.
38      * @param header the template specifying the binary table.
39      * @exception FitsException if there was a problem with the header.
40      */
41     public static Data manufactureData(Header header) throws FitsException {
42         return new BinaryTable(header);
43     }
44
45     public Data manufactureData() throws FitsException {
46         return manufactureData(myHeader);
47     }
48
49     /** Build a binary table HDU from the supplied data.
50      * @param table the array used to build the binary table.
51      * @exception FitsException if there was a problem with the data.
52      */
53     public static Header manufactureHeader(Data data) throws FitsException {
54         Header hdr = new Header();
55         data.fillHeader(hdr);
56         return hdr;
57     }
58
59     /** Encapsulate data in a BinaryTable data type */
60     public static Data encapsulate(Object o) throws FitsException {
61
62         if (o instanceof nom.tam.util.ColumnTable) {
63             return new BinaryTable((nom.tam.util.ColumnTable) o);
64         } else if (o instanceof Object[][]) {
65             return new BinaryTable((Object[][]) o);
66         } else if (o instanceof Object[]) {
67             return new BinaryTable((Object[]) o);
68         } else {
69             throw new FitsException("Unable to encapsulate object of type:"
70                     + o.getClass().getName() + " as BinaryTable");
71         }
72     }
73
74     /** Check that this is a valid binary table header.
75      * @param header to validate.
76      * @return <CODE>true</CODE> if this is a binary table header.
77      */
78     public static boolean isHeader(Header header) {
79         String xten = header.getStringValue("XTENSION");
80         if (xten == null) {
81             return false;
82         }
83         xten = xten.trim();
84         if (xten.equals("BINTABLE") || xten.equals("A3DTABLE")) {
85             return true;
86         } else {
87             return false;
88         }
89     }
90
91     /** Check that this HDU has a valid header.
92      * @return <CODE>true</CODE> if this HDU has a valid header.
93      */
94     public boolean isHeader() {
95         return isHeader(myHeader);
96     }
97
98     /* Check if this data object is consistent with a binary table.  There
99      * are three options:  a column table object, an Object[][], or an Object[].
100      * This routine doesn't check that the dimensions of arrays are properly
101      * consistent.
102      */
103     public static boolean isData(Object o) {
104
105         if (o instanceof nom.tam.util.ColumnTable || o instanceof Object[][]
106                 || o instanceof Object[]) {
107             return true;
108         } else {
109             return false;
110         }
111     }
112
113     /** Add a column without any associated header information.
114      *
115      * @param data The column data to be added.  Data should be an Object[] where
116      *             type of all of the constituents is identical.  The length
117      *             of data should match the other columns.  <b> Note:</b> It is
118      *             valid for data to be a 2 or higher dimensionality primitive
119      *             array.  In this case the column index is the first (in Java speak)
120      *             index of the array.  E.g., if called with int[30][20][10], the
121      *             number of rows in the table should be 30 and this column
122      *             will have elements which are 2-d integer arrays with TDIM = (10,20).
123      * @exception FitsException the column could not be added.
124      */
125     public int addColumn(Object data) throws FitsException {
126
127         int col = table.addColumn(data);
128         table.pointToColumn(getNCols() - 1, myHeader);
129         return col;
130     }
131
132     // Need to tell header about the Heap before writing.
133     public void write(ArrayDataOutput ado) throws FitsException {
134
135         int oldSize = myHeader.getIntValue("PCOUNT");
136         if (oldSize != table.getHeapSize()) {
137             myHeader.addValue("PCOUNT", table.getHeapSize(), "ntf::binarytablehdu:pcount:1");
138         }
139
140         if (myHeader.getIntValue("PCOUNT") == 0) {
141             myHeader.deleteKey("THEAP");
142         } else {
143             myHeader.getIntValue("TFIELDS");
144             int offset = myHeader.getIntValue("NAXIS1")
145                     * myHeader.getIntValue("NAXIS2")
146                     + table.getHeapOffset();
147             myHeader.addValue("THEAP", offset, "ntf::binarytablehdu:theap:1");
148         }
149
150         super.write(ado);
151     }
152
153     /**
154      * Convert a column in the table to complex.  Only tables with appropriate
155      * types and dimensionalities can be converted.  It is legal to call this on
156      * a column that is already complex.
157      *
158      * @param index  The 0-based index of the column to be converted.
159      * @return Whether the column can be converted
160      * @throws FitsException
161      */
162     public boolean setComplexColumn(int index) throws FitsException {
163         boolean status = false;
164         if (table.setComplexColumn(index)) {
165
166             // No problem with the data.  Make sure the header
167             // is right.
168
169             int[] dimens = table.getDimens()[index];
170             Class base = table.getBases()[index];
171
172             int dim = 1;
173             String tdim = "";
174             String sep = "";
175             // Don't loop over all values.
176             // The last is the [2] for the complex data.
177             for (int i = 0; i < dimens.length - 1; i += 1) {
178                 dim *= dimens[i];
179                 tdim = dimens[i] + sep + tdim;
180                 sep = ",";
181             }
182             String suffix = "C";  // For complex
183             // Update the TFORMn keyword.
184             if (base == double.class) {
185                 suffix = "M";
186             }
187
188             // Worry about variable length columns.
189             String prefix = "";
190             if (table.isVarCol(index)) {
191                 prefix = "P";
192                 dim = 1;
193                 if (table.isLongVary(index)) {
194                     prefix = "Q";
195                 }
196             }
197
198             // Now update the header.
199             myHeader.findCard("TFORM" + (index + 1));
200             HeaderCard hc = myHeader.nextCard();
201             String oldComment = hc.getComment();
202             if (oldComment == null) {
203                 oldComment = "Column converted to complex";
204             }
205             myHeader.addValue("TFORM" + (index + 1), dim + prefix + suffix, oldComment);
206             if (tdim.length() > 0) {
207                 myHeader.addValue("TDIM" + (index + 1), "(" + tdim + ")", "ntf::binarytablehdu:tdimN:1");
208             } else {
209                 // Just in case there used to be a TDIM card that's no longer needed.
210                 myHeader.removeCard("TDIM" + (index + 1));
211             }
212             status = true;
213         }
214         return status;
215     }
216
217     private void prtField(String type, String field) {
218         String val = myHeader.getStringValue(field);
219         if (val != null) {
220             System.out.print(type + '=' + val + "; ");
221         }
222     }
223
224     /** Print out some information about this HDU.
225      */
226     public void info() {
227
228         BinaryTable myData = (BinaryTable) this.myData;
229
230         System.out.println("  Binary Table");
231         System.out.println("      Header Information:");
232
233         int nhcol = myHeader.getIntValue("TFIELDS", -1);
234         int nrow = myHeader.getIntValue("NAXIS2", -1);
235         int rowsize = myHeader.getIntValue("NAXIS1", -1);
236
237         System.out.print("          " + nhcol + " fields");
238         System.out.println(", " + nrow + " rows of length " + rowsize);
239
240         for (int i = 1; i <= nhcol; i += 1) {
241             System.out.print("           " + i + ":");
242             prtField("Name", "TTYPE" + i);
243             prtField("Format", "TFORM" + i);
244             prtField("Dimens", "TDIM" + i);
245             System.out.println("");
246         }
247
248         System.out.println("      Data Information:");
249         if (myData == null
250                 || table.getNRows() == 0 || table.getNCols() == 0) {
251             System.out.println("         No data present");
252             if (table.getHeapSize() > 0) {
253                 System.out.println("         Heap size is: " + table.getHeapSize() + " bytes");
254             }
255         } else {
256
257             System.out.println("          Number of rows=" + table.getNRows());
258             System.out.println("          Number of columns=" + table.getNCols());
259             if (table.getHeapSize() > 0) {
260                 System.out.println("          Heap size is: " + table.getHeapSize() + " bytes");
261             }
262             Object[] cols = table.getFlatColumns();
263             for (int i = 0; i < cols.length; i += 1) {
264                 System.out.println("           " + i + ":" + ArrayFuncs.arrayDescription(cols[i]));
265             }
266         }
267     }
268
269     /** What are the standard column stems for a binary table?
270      */
271     public String[] columnKeyStems() {
272         return keyStems;
273     }
274 }