package nom.tam.fits; /* * Copyright: Thomas McGlynn 1997-1998. * This code may be used for any purpose, non-commercial * or commercial so long as this copyright notice is retained * in the source code or included in or referred to in any * derived software. * * Many thanks to David Glowacki (U. Wisconsin) for substantial * improvements, enhancements and bug fixes. */ import nom.tam.util.ArrayFuncs; import nom.tam.util.BufferedDataInputStream; /** FITS binary table header/data unit */ public class BinaryTableHDU extends TableHDU { /** Create a binary table header/data unit. * @param header the template specifying the binary table. * @exception FitsException if there was a problem with the header. */ public BinaryTableHDU(Header header) throws FitsException { super(header); if (!isHeader()) { throw new BadHeaderException("Not a valid binary table header"); } } /** Build a binary table HDU from the supplied data. * @param table the array used to build the binary table. * @exception FitsException if there was a problem with the data. */ public BinaryTableHDU(Object[][] table) throws FitsException { super(null); if (table == null) { myData = new BinaryTable(); } else { myData = new BinaryTable(table); } myHeader = BinaryTableHeaderParser.pointToTable((BinaryTable)myData); setColumnStrings(); } /** Build an empty binary table HDU. * @exception FitsException if there was a problem building the empty HDU. */ public BinaryTableHDU() throws FitsException { this((Object[][] )null); } /** Check that this is a valid binary table header. * @param header to validate. * @return <CODE>true</CODE> if this is a binary table header. */ public static boolean isHeader(Header header) { String card0 = header.getCard(0); // Note that characters after the first 8 aren't significant. // BINTABLE may be followed by one or more blanks. return (card0 != null && card0.startsWith("XTENSION= 'BINTABLE'")); } /** Check that this HDU has a valid header. * @return <CODE>true</CODE> if this HDU has a valid header. */ public boolean isHeader() { return isHeader(myHeader); } /** Set the default base keys which are expected to be associated * with a binary table. * The user can add to these by calling addColumnString directly. */ protected void setColumnStrings() { addColumnString("TTYPE"); addColumnString("TFORM"); addColumnString("TDIM"); addColumnString("TSCAL"); addColumnString("TZERO"); } /** Add a column without any associated header information. * * @param data The column data to be added. Data should be an Object[] where * type of all of the constituents is identical. The length * of data should match the other columns. <b> Note:</b> It is * valid for data to be a 2 or higher dimensionality primitive * array. In this case the column index is the first (in Java speak) * index of the array. E.g., if called with int[30][20][10], the * number of rows in the table should be 30 and this column * will have elements which are 2-d integer arrays with TDIM = (10,20). * @exception FitsException the column could not be added. */ public void addColumn(Object[] data) throws FitsException { BinaryTable myData = (BinaryTable) this.myData; myData.addColumn(data); // Make sure we can point to an appropriate place in the Header. int ncol = myData.getNcol(); if (ncol > 1) { int lastMark = -2; for (int j=0; j<columnStrings.size(); j += 1) { String key = (String)columnStrings.elementAt(j) + (ncol-1); myHeader.findKey(key); if (myHeader.getMark() > lastMark) { lastMark = myHeader.getMark(); } } myHeader.setMark(lastMark); } else { myHeader.findKey("TFIELDS"); int lastMark=myHeader.getMark(); int j=1; while (true) { String card = myHeader.getCard(lastMark+j); if (card == null) { myHeader.unsetMark(); break; } else if ( !(card.substring(0,8).equals("COMMENT ") || card.substring(0,8).equals(" "))) { myHeader.setMark(lastMark+j); break; } j += 1; } } BinaryTableHeaderParser.addColumn(ncol-1, data, myHeader); } /** Find the column which has the given name (i.e., TTYPE) * @param name The desired name. * @return The Fits index of the column (first column = 1); */ public int findColumn(String name) { for (int i=1; i <= myHeader.getIntValue("TFIELDS", 0); i += 1) { String tform = myHeader.getStringValue("TTYPE"+i); if (tform != null && tform.equals(name)) { return i-1; } } return -1; } /** Get the header and data information for a given column. * @param name The name (TTYPE) of column desired. * @return A Column object with the desired information or * null if the column could not be found. * @exception FitsException if <CODE>colNumber</CODE> could not be deleted. */ public Column getColumn(String name) throws FitsException { int col = findColumn(name); if (col < 0) { return null; } return getColumn(col); } /** Get the header and data associated with the given column. * @param colNumber The Fits (first=1) index of the desired column. * @return The associated information. * @exception FitsException if <CODE>colNumber</CODE> could not be found. */ public Column getColumn(int colNumber) throws FitsException { Column thisCol = new Column(); Object[] col = (Object[])((BinaryTable)myData).getColumn(colNumber); thisCol.setData(col); for (int i=0; i<columnStrings.size(); i += 1) { String card = myHeader.findKey((String)columnStrings.elementAt(i)+colNumber); if (card != null) { thisCol.addKey(card); } } return thisCol; } /** Add a column to tabular data. * @param col The column to be added. It should have the same * dimension as all of the other columns. * @exception FitsException if <CODE>col</CODE> could not be added. */ public void addColumn(Column col) throws FitsException { BinaryTable myData = (BinaryTable) this.myData; addColumn(col.getData()); int ncol = myData.getNcol(); String[] keys = col.getKeys(ncol); // Now add the pointers that were stored in this column. // Set the mark to the TFORMn keyword before we start. // Note that this will override the TFORM value for // the variable length column we had. myHeader.deleteKey("TDIMS"+ncol); myHeader.getStringValue("TFORM"+ncol); for (int i=0; i<keys.length; i += 1) { if (keys[i].substring(0,5).equals("TFORM") ) { HeaderCard card = new HeaderCard(keys[i]); myHeader.addStringValue(card.getKey(), card.getValue(), card.getComment()); } else { myHeader.addLine(keys[i]); } } } /** Create a variable column from the supplied data. * @param data The column of data to be added. It should have the same * dimension as all of the other columns. * @exception FitsException if column could not be added. */ public Column makeVarColumn(Object [] data) throws FitsException { return makeVarColumn(data, null, null); } /** Create a variable column from the supplied data. * @param data The column of data to be added. It should have the same * dimension as all of the other columns. * @param type The Fits type for this column (S, L, B, etc.) * @exception FitsException if column could not be added. */ public Column makeVarColumn(Object[] data, String type) throws FitsException { return makeVarColumn(data, type, null); } /** Create a variable column from the supplied data. * @param data The column of data to be added. It should have the same * dimension as all of the other columns. * @param type The Fits type for this column (S, L, B, etc.) * @param keys The list of keys for this column (may be null). * @exception FitsException if column could not be added. */ public Column makeVarColumn(Object[] data, String type, String [] keys) throws FitsException { Class baseClass = ArrayFuncs.getBaseClass(data); char classChar; // Byte type data can have several kinds of data encoded. if (baseClass == Byte.TYPE) { if (type != null && type.equals("S")) { classChar = 'S'; } else if (type != null && type.equals("L")) { classChar = 'L'; } else { classChar = 'B'; } } else if (baseClass == Integer.TYPE) { classChar = 'J'; } else if (baseClass == Short.TYPE) { classChar = 'I'; } else if (baseClass == Float.TYPE) { if (type != null && type.equals("C")) { classChar = 'C'; } else { classChar = 'E'; } } else if (baseClass == Double.TYPE) { if (type != null && type.equals("M")) { classChar = 'M'; } else { classChar = 'D'; } } else { throw new FitsException("Invalid Base class for variable column"); } Column varColumn = ((BinaryTable)myData).addVarData(data); varColumn.addKey(myHeader.formatFields( "TFORM", "'1P"+classChar+" '", "VariableLength Column")); // Adjust the PCOUNT variable to indicate the existence of a heap. myHeader.addIntValue("PCOUNT", ((BinaryTable)myData).getHeapSize(), "Size of Heap Area"); if (keys != null) { for (int i=0; i<keys.length; i += 1) { varColumn.addKey(keys[i]); } } return varColumn; } /** Return a variable column. * @param name The name of the column to fetch. * @return either null if <CODE>name</CODE> was not found, or an * array of data (as an Object). * @exception FitsException if the column could not be found or returned. */ public Object getVarData(String name) throws FitsException { int colNum = findColumn(name); if (colNum < 0) { return null; } return getVarData(colNum); } /** Return a variable column. * @param col The column number to fetch. * @return an array of data (as an Object). * @exception FitsException if <CODE>col</CODE> was not a valid column * number, was not a variable column, or had * an invalid Fits type. */ public Object getVarData(int col) throws FitsException { String tform = myHeader.getStringValue("TFORM"+(col+1)); if (tform == null) { throw new FitsException("TFORM not found for column(0 indexed):"+col); } char typeChar; Class baseClass; if (tform.substring(0,2).equals("1P") ) { typeChar = tform.charAt(2); } else if (tform.charAt(0) == 'P') { typeChar = tform.charAt(1); } else { throw new FitsException("Requested column does not seem to be variable: TFORM="+tform); } boolean complex = false; switch (typeChar) { case 'L': case 'B': case 'S': baseClass = Byte.TYPE; break; case 'I': baseClass = Short.TYPE; break; case 'J': baseClass = Integer.TYPE; break; case 'K': baseClass = Long.TYPE; break; case 'E': baseClass = Float.TYPE; break; case 'D': baseClass = Double.TYPE; break; case 'C': complex = true; baseClass = Float.TYPE; break; case 'M': complex = true; baseClass = Double.TYPE; break; default: throw new FitsException("Unable to understand variable column format:"+tform); } return ((BinaryTable)myData).getVarData(col, baseClass, complex); } /** Create a Data object to correspond to the header description. * @return An unfilled Data object which can be used to read * in the data for this HDU. * @exception FitsException if the binary table could not be created. */ public Data manufactureData() throws FitsException { setColumnStrings(); return new BinaryTable(myHeader); } /** Get the number of columns for this table * @return The number of columns in the table. */ public int getNumColumns() { return myHeader.getIntValue("TFIELDS", 0); } /** Get the number of rows for this table * @return The number of rows in the table. */ public int getNumRows() { return myHeader.getIntValue("NAXIS2", 0); } /** Get the name of a column in the table. * @return The column name. * @exception FitsException if an invalid index was requested. */ public String getColumnName(int index) throws FitsException { int flds = myHeader.getIntValue("TFIELDS", 0); if (index < 0 || index >= flds) { throw new FitsException("Bad column index " + index + " (only " + flds + " columns)"); } return getTrimmedString("TTYPE" + (index + 1)); } /** Get the FITS type of a column in the table. * @return The FITS type. * @exception FitsException if an invalid index was requested. */ public String getColumnFITSType(int index) throws FitsException { int flds = myHeader.getIntValue("TFIELDS", 0); if (index < 0 || index >= flds) { throw new FitsException("Bad column index " + index + " (only " + flds + " columns)"); } return getTrimmedString("TFORM" + (index + 1)); } /** Print out some information about this HDU. */ public void info() { BinaryTable myData = (BinaryTable) this.myData; System.out.println(" Binary Table"); System.out.println(" Header Information:"); int nhcol = myHeader.getIntValue("TFIELDS", -1); int nrow = myHeader.getIntValue("NAXIS2", -1); int rowsize = myHeader.getIntValue("NAXIS1", -1); System.out.print(" "+nhcol+" fields"); System.out.println(", "+nrow+" rows of length "+rowsize); for (int i=1; i <= nhcol; i += 1) { System.out.print(" "+i+":"); checkField("TTYPE"+i); checkField("TFORM"+i); checkField("TDIM"+i); System.out.println(" "); } System.out.println(" Data Information:"); if (myData == null || myData.getNrow() == 0 || myData.getNcol() == 0) { System.out.println(" No data present"); if (myData.getHeapSize() > 0) { System.out.println(" Heap size is: "+myData.getHeapSize()+" bytes"); } } else { System.out.println(" Number of rows="+myData.getNrow()); System.out.println(" Number of columns="+myData.getNcol()); if (myData.getHeapSize() > 0) { System.out.println(" Heap size is: "+myData.getHeapSize()+" bytes"); } int[][] dimens = myData.getDimens(); char[] types = myData.getTypes(); for (int i=0; i<myData.getNcol(); i += 1) { System.out.print(" "+(i+1)+ ":"+types[i] + " ["); char comma= ' '; for (int dim=0; dim < dimens[i].length; dim += 1) { System.out.print(""+comma+dimens[i][dim]); comma = ','; } System.out.println(" ]"); } } } }