Begin versioning.
[fits.git] / src / nom / tam / fits / TableHDU.java
1 package nom.tam.fits;
2
3 import java.util.Iterator;
4 import nom.tam.util.Cursor;
5
6 /** This class allows FITS binary and ASCII tables to
7  *  be accessed via a common interface.
8  *
9  *  Bug Fix: 3/28/01 to findColumn.
10  */
11 public abstract class TableHDU extends BasicHDU {
12
13     private TableData table;
14     private int currentColumn;
15
16     /** Create the TableHDU.  Note that this
17      *  will normally only be invoked by subclasses
18      *  in the FITS package.
19      * @param td  The data for the table.
20      */
21     TableHDU(TableData td) {
22         table = td;
23     }
24
25     /** Get a specific row of the table */
26     public Object[] getRow(int row) throws FitsException {
27         return table.getRow(row);
28     }
29
30     /** Get a specific column of the table where
31      *  the column name is specified using the TTYPEn keywords
32      *  in the header.
33      * @param colName  The name of the column to be extracted.
34      * @throws FitsException
35      */
36     public Object getColumn(String colName) throws FitsException {
37         return getColumn(findColumn(colName));
38     }
39
40     /** Get a specific column from the table using 0-based column
41      *  indexing.
42      */
43     public Object getColumn(int col) throws FitsException {
44         return table.getColumn(col);
45     }
46
47     /** Get all of the columns of the table.
48      */
49     public Object[] getColumns() throws FitsException {
50         Object[] result = new Object[getNCols()];
51         for (int i = 0; i < result.length; i += 1) {
52             result[i] = getColumn(i);
53         }
54         return result;
55     }
56
57     /** Get a specific element of the table using 0-based indices.
58      *
59      */
60     public Object getElement(int row, int col) throws FitsException {
61         return table.getElement(row, col);
62     }
63
64     /** Update a row within a table.
65      *
66      */
67     public void setRow(int row, Object[] newRow) throws FitsException {
68         table.setRow(row, newRow);
69     }
70
71     /** Update a column within a table.  The new column should have the
72      *  same format as the column being replaced.
73      */
74     public void setColumn(String colName, Object newCol) throws FitsException {
75         setColumn(findColumn(colName), newCol);
76     }
77
78     /** Update a column within a table.  The new column should have the same
79      *   format ast the column being replaced.
80      */
81     public void setColumn(int col, Object newCol) throws FitsException {
82         table.setColumn(col, newCol);
83     }
84
85     /** Update a single element within the table.
86      */
87     public void setElement(int row, int col, Object element) throws FitsException {
88         table.setElement(row, col, element);
89     }
90
91     /** Add a row to the end of the table. If this is the first row,
92      *  then this will add appropriate columns for each of the entries.
93      */
94     public int addRow(Object[] newRow) throws FitsException {
95
96         int row = table.addRow(newRow);
97         myHeader.addValue("NAXIS2", row, "ntf::tablehdu:naxis2:1");
98         return row;
99     }
100
101     /** Find the 0-based column index corresponding to a particular
102      *  column name.
103      */
104     public int findColumn(String colName) {
105
106         for (int i = 0; i < getNCols(); i += 1) {
107
108             String val = myHeader.getStringValue("TTYPE" + (i + 1));
109             if (val != null && val.trim().equals(colName)) {
110                 return i;
111             }
112         }
113         return -1;
114     }
115
116     /** Add a column to the table. */
117     public abstract int addColumn(Object data) throws FitsException;
118
119     /** Get the number of columns for this table
120      * @return The number of columns in the table.
121      */
122     public int getNCols() {
123         return table.getNCols();
124     }
125
126     /** Get the number of rows for this table
127      * @return The number of rows in the table.
128      */
129     public int getNRows() {
130         return table.getNRows();
131     }
132
133     /** Get the name of a column in the table.
134      * @param index The 0-based column index.
135      * @return The column name.
136      * @exception FitsException if an invalid index was requested.
137      */
138     public String getColumnName(int index) {
139
140         String ttype = myHeader.getStringValue("TTYPE" + (index + 1));
141         if (ttype != null) {
142             ttype = ttype.trim();
143         }
144         return ttype;
145     }
146
147     public void setColumnName(int index, String name, String comment)
148             throws FitsException {
149         setColumnMeta(index, "TTYPE", name, comment, true);
150     }
151
152     /** Specify column metadata for a given column in a way that
153      *  allows all of the column metadata for a given column
154      *  to be organized together.
155      *
156      * @param index   The 0-based index of the column
157      * @param key     The column key.  I.e., the keyword will be key+(index+1)
158      * @param value   The value to be placed in the header.
159      * @param comment The comment for the header
160      * @param after   Should the header card be after the current column metadata block
161      *                (true), or immediately before the TFORM card (false).
162      * @throws FitsException
163      */
164     public void setColumnMeta(int index, String key, String value, String comment, boolean after)
165             throws FitsException {
166         setCurrentColumn(index, after);
167         myHeader.addValue(key + (index + 1), value, comment);
168     }
169
170     /** Convenience method for getting column data.  Note that this works
171      *  only for metadata that returns a string value.  This is equivalent
172      *  to getStringValue(type+index);
173      */
174     public String getColumnMeta(int index, String type) {
175         return myHeader.getStringValue(type+(index+1));
176     }
177
178     public void setColumnMeta(int index, String key, String value, String comment)
179             throws FitsException {
180         setColumnMeta(index, key, value, comment, true);
181     }
182
183     public void setColumnMeta(int index, String key, long value, String comment, boolean after)
184             throws FitsException {
185         setCurrentColumn(index, after);
186         myHeader.addValue(key + (index + 1), value, comment);
187     }
188
189     public void setColumnMeta(int index, String key, double value, String comment, boolean after)
190             throws FitsException {
191         setCurrentColumn(index, after);
192         myHeader.addValue(key + (index + 1), value, comment);
193     }
194
195     public void setColumnMeta(int index, String key, boolean value, String comment, boolean after)
196             throws FitsException {
197         setCurrentColumn(index, after);
198         myHeader.addValue(key + (index + 1), value, comment);
199     }
200
201     /** Get the FITS type of a column in the table.
202      * @param index The 0-based index of the column.
203      * @return The FITS type.
204      * @exception FitsException if an invalid index was requested.
205      */
206     public String getColumnFormat(int index)
207             throws FitsException {
208         int flds = myHeader.getIntValue("TFIELDS", 0);
209         if (index < 0 || index >= flds) {
210             throw new FitsException("Bad column index " + index + " (only " + flds
211                     + " columns)");
212         }
213
214         return myHeader.getStringValue("TFORM" + (index + 1)).trim();
215     }
216
217     /** Set the cursor in the header to point after the
218      * metadata for the specified column
219      * @param col  The 0-based index of the column
220      */
221     public void setCurrentColumn(int col) {
222         setCurrentColumn(col, true);
223     }
224
225     /** Set the cursor in the header to point either before the
226      *  TFORM value or after the column metadat
227      * @param col    The 0-based index of the column
228      * @param after  True if the cursor should be placed after the existing column
229      * metadata or false if the cursor is to be placed before the TFORM value.
230      * If no corresponding TFORM is found, the cursoe will be placed at the end of
231      * current header.
232      */
233     public void setCurrentColumn(int col, boolean after) {
234         if (after) {
235             myHeader.positionAfterIndex("TFORM", col + 1);
236         } else {
237             String tform = "TFORM" + (col + 1);
238             myHeader.findCard(tform);
239         }
240     }
241
242     /**
243      * Remove all rows from the table starting at some specific index from the table.
244      * Inspired by a routine by R. Mathar but re-implemented using the DataTable and
245      * changes to AsciiTable so that it can be done easily for both Binary and ASCII tables.
246      * @param row the (0-based) index of the first row to be deleted.
247      * @throws FitsExcpetion if an error occurs.
248      */
249     public void deleteRows(final int row) throws FitsException {
250         deleteRows(row, getNRows() - row);
251     }
252
253     /**
254      * Remove a number of adjacent rows from the table.  This routine
255      * was inspired by code by R.Mathar but re-implemented using changes
256      * in the ColumnTable class abd AsciiTable so that we can do
257      * it for all FITS tables.
258      * @param firstRow the (0-based) index of the first row to be deleted.
259      *  This is zero-based indexing: 0<=firstrow< number of rows.
260      * @param nRow the total number of rows to be deleted.
261      * @throws FitsException  If an error occurs in the deletion.
262      */
263     public void deleteRows(final int firstRow, int nRow) throws FitsException {
264
265         // Just ignore invalid requests.
266         if (nRow <= 0 || firstRow >= getNRows() || nRow <= 0) {
267             return;
268         }
269
270         /* correct if more rows are requested than available */
271         if (nRow > getNRows() - firstRow) {
272             nRow = getNRows() - firstRow;
273         }
274
275         table.deleteRows(firstRow, nRow);
276         myHeader.setNaxis(2, getNRows());
277     }
278
279     /** Delete a set of columns from a table.
280      */
281     public void deleteColumnsIndexOne(int column, int len) throws FitsException {
282         deleteColumnsIndexZero(column - 1, len);
283     }
284
285     /** Delete a set of columns from a table.
286      */
287     public void deleteColumnsIndexZero(int column, int len) throws FitsException {
288         deleteColumnsIndexZero(column, len, columnKeyStems());
289     }
290
291     /** Delete a set of columns from a table.
292      *  @param column The one-indexed start column.
293      *  @param len    The number of columns to delete.
294      *  @param fields Stems for the header fields to be removed
295      *                for the table.
296      */
297     public void deleteColumnsIndexOne(int column, int len, String[] fields) throws FitsException {
298         deleteColumnsIndexZero(column - 1, len, fields);
299     }
300
301     /** Delete a set of columns from a table.
302      *  @param column The zero-indexed start column.
303      *  @param len    The number of columns to delete.
304      *  @param fields Stems for the header fields to be removed
305      *                for the table.
306      */
307     public void deleteColumnsIndexZero(int column, int len, String[] fields) throws FitsException {
308
309         if (column < 0 || len < 0 || column + len > getNCols()) {
310             throw new FitsException("Illegal columns deletion request- Start:" + column + " Len:" + len + " from table with " + getNCols() + " columns");
311         }
312
313         if (len == 0) {
314             return;
315         }
316
317         int ncol = getNCols();
318         table.deleteColumns(column, len);
319
320
321         // Get rid of the keywords for the deleted columns
322         for (int col = column; col < column + len; col += 1) {
323             for (int fld = 0; fld < fields.length; fld += 1) {
324                 String key = fields[fld] + (col + 1);
325                 myHeader.deleteKey(key);
326             }
327         }
328
329         // Shift the keywords for the columns after the deleted columns
330         for (int col = column + len; col < ncol; col += 1) {
331             for (int fld = 0; fld < fields.length; fld += 1) {
332                 String oldKey = fields[fld] + (col + 1);
333                 String newKey = fields[fld] + (col + 1 - len);
334                 if (myHeader.containsKey(oldKey)) {
335                     myHeader.replaceKey(oldKey, newKey);
336                 }
337             }
338         }
339         // Update the number of fields.
340         myHeader.addValue("TFIELDS", getNCols(), "ntf::tablehdu:tfields:1");
341
342         // Give the data sections a chance to update the header too.
343         table.updateAfterDelete(ncol, myHeader);
344     }
345
346     /** Get the stems of the keywords that are associated
347      *  with table columns.  Users can supplement this
348      *  with their own and call the appropriate deleteColumns fields.
349      */
350     public abstract String[] columnKeyStems();
351 }