public ConeQueryRowSequence createQuerySequence(StarTable table) throws IOException { assert rowMap_ == null || rowMap_.length == table.getRowCount(); final RowSequence rseq = table.getRowSequence(); return new ConeQueryRowSequence() { long irow_ = -1; public boolean next() throws IOException { if (matchWorker_ != null && matchWorker_.cancelled_) { throw new IOException("Cancelled"); } boolean retval = rseq.next(); if (matchWorker_ != null && matchWorker_.cancelled_) { throw new IOException("Cancelled"); } if (retval) { irow_++; if (matchWorker_ != null) { matchWorker_.setInputRow((int) irow_); } } return retval; } public Object getCell(int icol) throws IOException { return rseq.getCell(icol); } public Object[] getRow() throws IOException { return rseq.getRow(); } public void close() throws IOException { rseq.close(); } public double getRa() throws IOException { return Math.toDegrees(getDoubleValue(raData_)); } public double getDec() throws IOException { return Math.toDegrees(getDoubleValue(decData_)); } public double getRadius() throws IOException { return Math.toDegrees(getDoubleValue(srData_)); } private double getDoubleValue(ColumnData cdata) throws IOException { if (cdata != null) { long jrow = rowMap_ == null ? irow_ : rowMap_[(int) irow_]; Object value = cdata.readValue(jrow); return value instanceof Number ? ((Number) value).doubleValue() : Double.NaN; } else { return Double.NaN; } } }; }
public void writeData(DataOutput strm) throws IOException { /* Work out the length of each row in bytes. */ int rowBytes = 0; int ncol = table.getColumnCount(); for (int icol = 0; icol < ncol; icol++) { ColumnWriter writer = colWriters[icol]; if (writer != null) { rowBytes += writer.getLength(); } } /* Write the data cells, delegating the item in each column to * the writer that knows how to handle it. */ long nWritten = 0L; RowSequence rseq = table.getRowSequence(); try { while (rseq.next()) { Object[] row = rseq.getRow(); for (int icol = 0; icol < ncol; icol++) { ColumnWriter writer = colWriters[icol]; if (writer != null) { writer.writeValue(strm, row[icol]); } } nWritten += rowBytes; } } finally { rseq.close(); } /* Write padding. */ int extra = (int) (nWritten % (long) 2880); if (extra > 0) { strm.write(new byte[2880 - extra]); } }
/** * Configures this serializer for use with a given table and column writer factory. Should be * called before this object is ready for use; in a constructor would be a good place. Calls * {@link #createColumnWriter}. * * @param table table to be written */ final void init(StarTable table) throws IOException { if (this.table != null) { throw new IllegalStateException("Table already initialised"); } this.table = table; /* Get table dimensions (though we may need to calculate the row * count directly later. */ int ncol = table.getColumnCount(); long nrow = table.getRowCount(); /* Store column infos. */ colInfos = Tables.getColumnInfos(table); /* Work out column shapes, and check if any are unknown (variable * last dimension). */ boolean hasVarShapes = false; boolean checkForNullableInts = false; int[][] shapes = new int[ncol][]; int[] maxChars = new int[ncol]; int[] maxElements = new int[ncol]; long[] totalElements = new long[ncol]; boolean[] useCols = new boolean[ncol]; boolean[] varShapes = new boolean[ncol]; boolean[] varChars = new boolean[ncol]; boolean[] varElementChars = new boolean[ncol]; boolean[] mayHaveNullableInts = new boolean[ncol]; Arrays.fill(useCols, true); boolean[] hasNulls = new boolean[ncol]; for (int icol = 0; icol < ncol; icol++) { ColumnInfo colinfo = colInfos[icol]; Class clazz = colinfo.getContentClass(); if (clazz.isArray()) { shapes[icol] = (int[]) colinfo.getShape().clone(); int[] shape = shapes[icol]; if (shape[shape.length - 1] < 0) { varShapes[icol] = true; hasVarShapes = true; } else { int nel = shape.length > 0 ? 1 : 0; for (int id = 0; id < shape.length; id++) { nel *= shape[id]; } assert nel >= 0; maxElements[icol] = nel; } if (clazz.getComponentType().equals(String.class)) { maxChars[icol] = colinfo.getElementSize(); if (maxChars[icol] <= 0) { varElementChars[icol] = true; hasVarShapes = true; } } } else if (clazz.equals(String.class)) { maxChars[icol] = colinfo.getElementSize(); if (maxChars[icol] <= 0) { varChars[icol] = true; hasVarShapes = true; } } else if (colinfo.isNullable() && (clazz == Byte.class || clazz == Short.class || clazz == Integer.class || clazz == Long.class)) { mayHaveNullableInts[icol] = true; /* Only set the flag which forces a first pass if we need * to work out whether nulls actually exist. If the * aux datum giving a null value exists we will use it in * any case, so finding out whether there are in fact null * values by scanning the data is not necessary. */ if (colinfo.getAuxDatumValue(Tables.NULL_VALUE_INFO, Number.class) != null) { hasNulls[icol] = true; } else { checkForNullableInts = true; } } } /* If necessary, make a first pass through the table data to * find out the maximum size of variable length fields and the length * of the table. */ if (hasVarShapes || checkForNullableInts || nrow < 0) { StringBuffer sbuf = new StringBuffer("First pass needed: "); if (hasVarShapes) { sbuf.append("(variable array shapes) "); } if (checkForNullableInts) { sbuf.append("(nullable ints) "); } if (nrow < 0) { sbuf.append("(unknown row count) "); } logger.config(sbuf.toString()); nrow = 0L; /* Get the maximum dimensions. */ RowSequence rseq = table.getRowSequence(); try { while (rseq.next()) { nrow++; for (int icol = 0; icol < ncol; icol++) { if (useCols[icol] && (varShapes[icol] || varChars[icol] || varElementChars[icol] || (mayHaveNullableInts[icol] && !hasNulls[icol]))) { Object cell = rseq.getCell(icol); if (cell == null) { if (mayHaveNullableInts[icol]) { hasNulls[icol] = true; } } else { if (varChars[icol]) { int leng = ((String) cell).length(); maxChars[icol] = Math.max(maxChars[icol], leng); } else if (varElementChars[icol]) { String[] svals = (String[]) cell; for (int i = 0; i < svals.length; i++) { maxChars[icol] = Math.max(maxChars[icol], svals[i].length()); } } if (varShapes[icol]) { int nel = Array.getLength(cell); maxElements[icol] = Math.max(maxElements[icol], nel); totalElements[icol] += nel; } } } } } } finally { rseq.close(); } /* In the case of variable string lengths and no non-null data * in any of the cells, maxChars could still be set negative. * Fix that here. */ for (int icol = 0; icol < ncol; icol++) { if (maxChars[icol] < 0) { maxChars[icol] = 0; } } /* Furthermore, zero length strings are probably a bad idea * for FITS output. Make sure that all output strings have * a length of at least 1. */ for (int icol = 0; icol < ncol; icol++) { if (maxChars[icol] == 0) { maxChars[icol] = 1; } } /* Work out the actual shapes for columns which have variable ones, * based on the shapes that we encountered in the rows. */ if (hasVarShapes) { for (int icol = 0; icol < ncol; icol++) { if (useCols[icol]) { if (varShapes[icol]) { int[] shape = shapes[icol]; int ndim = shape.length; assert shape[ndim - 1] <= 0; int nel = 1; for (int i = 0; i < ndim - 1; i++) { nel *= shape[i]; } shape[ndim - 1] = Math.max(1, (maxElements[icol] + nel - 1) / nel); } } } } } /* Store the row count, which we must have got by now. */ assert nrow >= 0; rowCount = nrow; /* We now have all the information we need about the table. * Construct and store a custom writer for each column which * knows about the characteristics of the column and how to * write values to the stream. For columns which can't be * written in FITS format store a null in the writers array * and log a message. */ colWriters = new ColumnWriter[ncol]; int rbytes = 0; for (int icol = 0; icol < ncol; icol++) { if (useCols[icol]) { ColumnInfo cinfo = colInfos[icol]; ColumnWriter writer = createColumnWriter( cinfo, shapes[icol], varShapes[icol], maxChars[icol], maxElements[icol], totalElements[icol], mayHaveNullableInts[icol] && hasNulls[icol]); if (writer == null) { logger.warning( "Ignoring column " + cinfo.getName() + " - don't know how to write to FITS"); } colWriters[icol] = writer; } } }