fits-1.10.0/0000775000175000017500000000000012050472242012742 5ustar frothmaifrothmaifits-1.10.0/NOTE.v110.00000664000175000017500000000207012050472223014254 0ustar frothmaifrothmaiV1.10 October 25, 2012 No functional changes to the FITS code are included in this release. All internal documentation has been updated to reflect that this library is now available in the public domain. The following notice is included in each source file. This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) The code is available in the public domain and may be copied, modified and used by anyone in any fashion for any purpose without restriction. No warranty regarding correctness or performance of this code is given or implied. Users may contact the author if they have questions or concerns. The author would like to thank many who have contributed suggestions, enhancements and bug fixes including: David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, Booth Hartley and Jason Weiss. I apologize to any contributors whose names may have been inadvertently omitted. Tom McGlynn fits-1.10.0/nom/0000775000175000017500000000000011566662560013552 5ustar frothmaifrothmaifits-1.10.0/nom/tam/0000775000175000017500000000000011566662570014334 5ustar frothmaifrothmaifits-1.10.0/nom/tam/util/0000775000175000017500000000000011566662570015311 5ustar frothmaifrothmaifits-1.10.0/nom/tam/util/TruncationException.java0000664000175000017500000000202712031033644022140 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ public class TruncationException extends Exception { public TruncationException() { super(); } public TruncationException(String msg) { super(msg); } } fits-1.10.0/nom/tam/util/RandomAccess.java0000664000175000017500000000252212031033644020475 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** These packages define the methods which indicate that * an i/o stream may be accessed in arbitrary order. * The method signatures are taken from RandomAccessFile * though that class does not implement this interface. */ public interface RandomAccess extends ArrayDataInput { /** Move to a specified location in the stream. */ public void seek(long offsetFromStart) throws java.io.IOException; /** Get the current position in the stream */ public long getFilePointer(); } fits-1.10.0/nom/tam/util/ByteFormatter.java0000664000175000017500000007076712031033644020742 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This class provides mechanisms for * efficiently formatting numbers and Strings. * Data is appended to existing byte arrays. Note * that the formatting of real or double values * may differ slightly (in the last bit) from * the standard Java packages since this routines * are optimized for speed rather than accuracy. *

* The methods in this class create no objects. *

* If a number cannot fit into the requested space * the truncateOnOverlow flag controls whether the * formatter will attempt to append it using the * available length in the output (a la C or Perl style * formats). If this flag is set, or if the number * cannot fit into space left in the buffer it is 'truncated' * and the requested space is filled with a truncation fill * character. A TruncationException may be thrown if the truncationThrow * flag is set. *

* This class does not explicitly support separate methods * for formatting reals in exponential notation. Real numbers * near one are by default formatted in decimal notation while * numbers with large (or very negative) exponents are formatted * in exponential notation. By setting the limits at which these * transitions take place the user can force either exponential or * decimal notation. * */ public final class ByteFormatter { /** Internal buffers used in formatting fields */ private byte[] tbuf1 = new byte[32]; private byte[] tbuf2 = new byte[32]; private static final double ilog10 = 1. / Math.log(10); /** Should we truncate overflows or just run over limit */ private boolean truncateOnOverflow = true; /** What do we use to fill when we cannot print the number? */ private byte truncationFill = (byte) '*'; // Default is often used in Fortran /** Throw exception on truncations */ private boolean truncationThrow = true; /** Should we right align? */ private boolean align = false; /** Minimum magnitude to print in non-scientific notation. */ double simpleMin = 1.e-3; /** Maximum magnitude to print in non-scientific notation. */ double simpleMax = 1.e6; /** Powers of 10. We overextend on both sides. * These should perhaps be tabulated rather than * computed though it may be faster to calculate * them than to read in the extra bytes in the class file. */ private static final double tenpow[]; /** What index of tenpow is 10^0 */ private static final int zeropow; static { // Static initializer int min = (int) Math.floor((int) (Math.log(Double.MIN_VALUE) * ilog10)); int max = (int) Math.floor((int) (Math.log(Double.MAX_VALUE) * ilog10)); max += 1; tenpow = new double[(max - min) + 1]; for (int i = 0; i < tenpow.length; i += 1) { tenpow[i] = Math.pow(10, i + min); } zeropow = -min; } /** Digits. We could handle other bases * by extending or truncating this list and changing * the division by 10 (and it's factors) at various * locations. */ private static final byte[] digits = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9'}; /** Set the truncation behavior. * @param val If set to true (the default) then do not * exceed the requested length. If a number cannot * be sensibly formatted, the truncation fill character * may be inserted. */ public void setTruncateOnOverflow(boolean val) { truncateOnOverflow = val; } /** Should truncations cause a truncation overflow? */ public void setTruncationThrow(boolean throwException) { truncationThrow = throwException; } /** Set the truncation fill character. * @param val The character to be used in subsequent truncations. */ public void setTruncationFill(char val) { truncationFill = (byte) val; } /** Set the alignment flag. * @param val Should numbers be right aligned? */ public void setAlign(boolean val) { align = val; } /** Set the range of real numbers that will be formatted in * non-scientific notation, i.e., .00001 rather than 1.0e-5. * The sign of the number is ignored. * @param min The minimum value for non-scientific notation. * @param max The maximum value for non-scientific notation. */ public void setSimpleRange(double min, double max) { simpleMin = min; simpleMax = max; } /** Format an int into an array. * @param val The int to be formatted. * @param array The array in which to place the result. * @return The number of characters used. */ public int format(int val, byte[] array) throws TruncationException { return format(val, array, 0, array.length); } /** Format an int into an existing array. * @param val Integer to be formatted * @param buf Buffer in which result is to be stored * @param off Offset within buffer * @param len Maximum length of integer * @return offset of next unused character in input buffer. */ public int format(int val, byte[] buf, int off, int len) throws TruncationException { // Special case if (val == Integer.MIN_VALUE) { if (len > 10 || (!truncateOnOverflow && buf.length - off > 10)) { return format("-2147483648", buf, off, len); } else { truncationFiller(buf, off, len); return off + len; } } int pos = Math.abs(val); // First count the number of characters in the result. // Otherwise we need to use an intermediary buffer. int ndig = 1; int dmax = 10; while (ndig < 10 && pos >= dmax) { ndig += 1; dmax *= 10; } if (val < 0) { ndig += 1; } // Truncate if necessary. if ((truncateOnOverflow && ndig > len) || ndig > buf.length - off) { truncationFiller(buf, off, len); return off + len; } // Right justify if requested. if (align) { off = alignFill(buf, off, len - ndig); } // Now insert the actual characters we want -- backwards // We use a do{} while() to handle the case of 0. off += ndig; int xoff = off - 1; do { buf[xoff] = digits[pos % 10]; xoff -= 1; pos /= 10; } while (pos > 0); if (val < 0) { buf[xoff] = (byte) '-'; } return off; } /** Format a long into an array. * @param val The long to be formatted. * @param array The array in which to place the result. * @return The number of characters used. */ public int format(long val, byte[] array) throws TruncationException { return format(val, array, 0, array.length); } /** Format a long into an existing array. * @param val Long to be formatted * @param buf Buffer in which result is to be stored * @param off Offset within buffer * @param len Maximum length of integer * @return offset of next unused character in input buffer. */ public int format(long val, byte[] buf, int off, int len) throws TruncationException { // Special case if (val == Long.MIN_VALUE) { if (len > 19 || (!truncateOnOverflow && buf.length - off > 19)) { return format("-9223372036854775808", buf, off, len); } else { truncationFiller(buf, off, len); return off + len; } } long pos = Math.abs(val); // First count the number of characters in the result. // Otherwise we need to use an intermediary buffer. int ndig = 1; long dmax = 10; // Might be faster to try to do this partially in ints while (ndig < 19 && pos >= dmax) { ndig += 1; dmax *= 10; } if (val < 0) { ndig += 1; } // Truncate if necessary. if ((truncateOnOverflow && ndig > len) || ndig > buf.length - off) { truncationFiller(buf, off, len); return off + len; } // Right justify if requested. if (align) { off = alignFill(buf, off, len - ndig); } // Now insert the actual characters we want -- backwards. off += ndig; int xoff = off - 1; buf[xoff] = (byte) '0'; boolean last = (pos == 0); while (!last) { // Work on ints rather than longs. int giga = (int) (pos % 1000000000L); pos /= 1000000000L; last = (pos == 0); for (int i = 0; i < 9; i += 1) { buf[xoff] = digits[giga % 10]; xoff -= 1; giga /= 10; if (last && giga == 0) { break; } } } if (val < 0) { buf[xoff] = (byte) '-'; } return off; } /** Format a boolean into an existing array. */ public int format(boolean val, byte[] array) { return format(val, array, 0, array.length); } /** Format a boolean into an existing array * @param val The boolean to be formatted * @param array The buffer in which to format the data. * @param off The starting offset within the buffer. * @param len The maximum number of characters to use * use in formatting the number. * @return Offset of next available character in buffer. */ public int format(boolean val, byte[] array, int off, int len) { if (align && len > 1) { off = alignFill(array, off, len - 1); } if (len > 0) { if (val) { array[off] = (byte) 'T'; } else { array[off] = (byte) 'F'; } off += 1; } return off; } /** Insert a string at the beginning of an array */ public int format(String val, byte[] array) { return format(val, array, 0, array.length); } /** Insert a String into an existing character array. * If the String is longer than len, then only the * the initial len characters will be inserted. * @param val The string to be inserted. A null string * will insert len spaces. * @param array The buffer in which to insert the string. * @param off The starting offset to insert the string. * @param len The maximum number of characters to insert. * @return Offset of next available character in buffer. */ public int format(String val, byte[] array, int off, int len) { if (val == null) { for (int i = 0; i < len; i += 1) { array[off + i] = (byte) ' '; } return off + len; } int slen = val.length(); if ((truncateOnOverflow && slen > len) || (slen > array.length - off)) { val = val.substring(0, len); slen = len; } if (align && (len > slen)) { off = alignFill(array, off, len - slen); } /** We should probably require ASCII here, but for the nonce we do not [TAM 5/11] */ System.arraycopy(val.getBytes(), 0, array, off, slen); return off + slen; } /** Format a float into an array. * @param val The float to be formatted. * @param array The array in which to place the result. * @return The number of characters used. */ public int format(float val, byte[] array) throws TruncationException { return format(val, array, 0, array.length); } /** Format a float into an existing byteacter array. *

* This is hard to do exactly right... The JDK code does * stuff with rational arithmetic and so forth. * We use a much simpler algorithm which may give * an answer off in the lowest order bit. * Since this is pure Java, it should still be consistent * from machine to machine. *

* Recall that the binary representation of * the float is of the form d = 0.bbbbbbbb x 2n * where there are up to 24 binary digits in the binary * fraction (including the assumed leading 1 bit * for normalized numbers). * We find a value m such that 10m d is between * 224 and >232. * This product will be exactly convertible to an int * with no loss of precision. Getting the * decimal representation for that is trivial (see formatInteger). * This is a decimal mantissa and we have an exponent (-m). * All we have to do is manipulate the decimal point * to where we want to see it. Errors can * arise due to roundoff in the scaling multiplication, but * should be very small. * * @param val Float to be formatted * @param buf Buffer in which result is to be stored * @param off Offset within buffer * @param len Maximum length of field * @return Offset of next character in buffer. */ public int format(float val, byte[] buf, int off, int len) throws TruncationException { float pos = (float) Math.abs(val); int minlen, actlen; // Special cases if (pos == 0.) { return format("0.0", buf, off, len); } else if (Float.isNaN(val)) { return format("NaN", buf, off, len); } else if (Float.isInfinite(val)) { if (val > 0) { return format("Infinity", buf, off, len); } else { return format("-Infinity", buf, off, len); } } int power = (int) Math.floor((Math.log(pos) * ilog10)); int shift = 8 - power; float scale; float scale2 = 1; // Scale the number so that we get a number ~ n x 10^8. if (shift < 30) { scale = (float) tenpow[shift + zeropow]; } else { // Can get overflow if the original number is // very small, so we break out the shift // into two multipliers. scale2 = (float) tenpow[30 + zeropow]; scale = (float) tenpow[shift - 30 + zeropow]; } pos = (pos * scale) * scale2; // Parse the float bits. int bits = Float.floatToIntBits(pos); // The exponent should be a little more than 23 int exp = ((bits & 0x7F800000) >> 23) - 127; int numb = (bits & 0x007FFFFF); if (exp > -127) { // Normalized.... numb |= (0x00800000); } else { // Denormalized exp += 1; } // Multiple this number by the excess of the exponent // over 24. This completes the conversion of float to int // (<<= did not work on Alpha TruUnix) numb = numb << (exp - 23L); // Get a decimal mantissa. boolean oldAlign = align; align = false; int ndig = format(numb, tbuf1, 0, 32); align = oldAlign; // Now format the float. return combineReal(val, buf, off, len, tbuf1, ndig, shift); } /** Format a double into an array. * @param val The double to be formatted. * @param array The array in which to place the result. * @return The number of characters used. */ public int format(double val, byte[] array) throws TruncationException { return format(val, array, 0, array.length); } /** Format a double into an existing character array. *

* This is hard to do exactly right... The JDK code does * stuff with rational arithmetic and so forth. * We use a much simpler algorithm which may give * an answer off in the lowest order bit. * Since this is pure Java, it should still be consistent * from machine to machine. *

* Recall that the binary representation of * the double is of the form d = 0.bbbbbbbb x 2n * where there are up to 53 binary digits in the binary * fraction (including the assumed leading 1 bit * for normalized numbers). * We find a value m such that 10m d is between * 253 and >263. * This product will be exactly convertible to a long * with no loss of precision. Getting the * decimal representation for that is trivial (see formatLong). * This is a decimal mantissa and we have an exponent (-m). * All we have to do is manipulate the decimal point * to where we want to see it. Errors can * arise due to roundoff in the scaling multiplication, but * should be no more than a single bit. * * @param val Double to be formatted * @param buf Buffer in which result is to be stored * @param off Offset within buffer * @param len Maximum length of integer * @return offset of next unused character in input buffer. */ public int format(double val, byte[] buf, int off, int len) throws TruncationException { double pos = Math.abs(val); int minlen, actlen; // Special cases -- It is OK if these get truncated. if (pos == 0.) { return format("0.0", buf, off, len); } else if (Double.isNaN(val)) { return format("NaN", buf, off, len); } else if (Double.isInfinite(val)) { if (val > 0) { return format("Infinity", buf, off, len); } else { return format("-Infinity", buf, off, len); } } int power = (int) (Math.log(pos) * ilog10); int shift = 17 - power; double scale; double scale2 = 1; // Scale the number so that we get a number ~ n x 10^17. if (shift < 200) { scale = tenpow[shift + zeropow]; } else { // Can get overflow if the original number is // very small, so we break out the shift // into two multipliers. scale2 = tenpow[200 + zeropow]; scale = tenpow[shift - 200 + zeropow]; } pos = (pos * scale) * scale2; // Parse the double bits. long bits = Double.doubleToLongBits(pos); // The exponent should be a little more than 52. int exp = (int) (((bits & 0x7FF0000000000000L) >> 52) - 1023); long numb = (bits & 0x000FFFFFFFFFFFFFL); if (exp > -1023) { // Normalized.... numb |= (0x0010000000000000L); } else { // Denormalized exp += 1; } // Multiple this number by the excess of the exponent // over 52. This completes the conversion of double to long. numb = numb << (exp - 52); // Get a decimal mantissa. boolean oldAlign = align; align = false; int ndig = format(numb, tbuf1, 0, 32); align = oldAlign; // Now format the double. return combineReal(val, buf, off, len, tbuf1, ndig, shift); } /** This method formats a double given * a decimal mantissa and exponent information. * @param val The original number * @param buf Output buffer * @param off Offset into buffer * @param len Maximum number of characters to use in buffer. * @param mant A decimal mantissa for the number. * @param lmant The number of characters in the mantissa * @param shift The exponent of the power of 10 that * we shifted val to get the given mantissa. * @return Offset of next available character in buffer. */ int combineReal(double val, byte[] buf, int off, int len, byte[] mant, int lmant, int shift) throws TruncationException { // First get the minimum size for the number double pos = Math.abs(val); boolean simple = false; int minSize; int maxSize; if (pos >= simpleMin && pos <= simpleMax) { simple = true; } int exp = lmant - shift - 1; int lexp = 0; if (!simple) { boolean oldAlign = align; align = false; lexp = format(exp, tbuf2, 0, 32); align = oldAlign; minSize = lexp + 2; // e.g., 2e-12 maxSize = lexp + lmant + 2; // add in "." and e } else { if (exp >= 0) { minSize = exp + 1; // e.g. 32 // Special case. E.g., 99.9 has // minumum size of 3. int i; for (i = 0; i < lmant && i <= exp; i += 1) { if (mant[i] != (byte) '9') { break; } } if (i > exp && i < lmant && mant[i] >= (byte) '5') { minSize += 1; } maxSize = lmant + 1; // Add in "." if (maxSize <= minSize) { // Very large numbers. maxSize = minSize + 1; } } else { minSize = 2; maxSize = 1 + Math.abs(exp) + lmant; } } if (val < 0) { minSize += 1; maxSize += 1; } // Can the number fit? if ((truncateOnOverflow && minSize > len) || (minSize > buf.length - off)) { truncationFiller(buf, off, len); return off + len; } // Do we need to align it? if (maxSize < len && align) { int nal = len - maxSize; off = alignFill(buf, off, nal); len -= nal; } int off0 = off; // Now begin filling in the buffer. if (val < 0) { buf[off] = (byte) '-'; off += 1; len -= 1; } if (simple) { return Math.abs(mantissa(mant, lmant, exp, simple, buf, off, len)); } else { off = mantissa(mant, lmant, 0, simple, buf, off, len - lexp - 1); if (off < 0) { off = -off; len -= off; // Handle the expanded exponent by filling if (exp == 9 || exp == 99) { // Cannot fit... if (off + len == minSize) { truncationFiller(buf, off, len); return off + len; } else { // Steal a character from the mantissa. off -= 1; } } exp += 1; lexp = format(exp, tbuf2, 0, 32); } buf[off] = (byte) 'E'; off += 1; System.arraycopy(tbuf2, 0, buf, off, lexp); return off + lexp; } } /** Write the mantissa of the number. This method addresses * the subtleties involved in rounding numbers. */ int mantissa(byte[] mant, int lmant, int exp, boolean simple, byte[] buf, int off, int len) { // Save in case we need to extend the number. int off0 = off; int pos = 0; if (exp < 0) { buf[off] = (byte) '0'; len -= 1; off += 1; if (len > 0) { buf[off] = (byte) '.'; off += 1; len -= 1; } // Leading 0s in small numbers. int cexp = exp; while (cexp < -1 && len > 0) { buf[off] = (byte) '0'; cexp += 1; off += 1; len -= 1; } } else { // Print out all digits to the left of the decimal. while (exp >= 0 && pos < lmant) { buf[off] = mant[pos]; off += 1; pos += 1; len -= 1; exp -= 1; } // Trust we have enough space for this. for (int i = 0; i <= exp; i += 1) { buf[off] = (byte) '0'; off += 1; len -= 1; } // Add in a decimal if we have space. if (len > 0) { buf[off] = (byte) '.'; len -= 1; off += 1; } } // Now handle the digits to the right of the decimal. while (len > 0 && pos < lmant) { buf[off] = mant[pos]; off += 1; exp -= 1; len -= 1; pos += 1; } // Now handle rounding. if (pos < lmant && mant[pos] >= (byte) '5') { int i; // Increment to the left until we find a non-9 for (i = off - 1; i >= off0; i -= 1) { if (buf[i] == (byte) '.' || buf[i] == (byte) '-') { continue; } if (buf[i] == (byte) '9') { buf[i] = (byte) '0'; } else { buf[i] += 1; break; } } // Now we handle 99.99 case. This can cause problems // in two cases. If we are not using scientific notation // then we may want to convert 99.9 to 100., i.e., // we need to move the decimal point. If there is no // decimal point, then we must not be truncating on overflow // but we should be allowed to write it to the // next character (i.e., we are not at the end of buf). // // If we are printing in scientific notation, then we want // to convert 9.99 to 1.00, i.e. we do not move the decimal. // However we need to signal that the exponent should be // incremented by one. // // We cannot have aligned the number, since that requires // the full precision number to fit within the requested // length, and we would have printed out the entire // mantissa (i.e., pos >= lmant) if (i < off0) { buf[off0] = (byte) '1'; boolean foundDecimal = false; for (i = off0 + 1; i < off; i += 1) { if (buf[i] == (byte) '.') { foundDecimal = true; if (simple) { buf[i] = (byte) '0'; i += 1; if (i < off) { buf[i] = (byte) '.'; } } break; } } if (simple && !foundDecimal) { buf[off + 1] = (byte) '0'; // 99 went to 100 off += 1; } off = -off; // Signal to change exponent if necessary. } } return off; } /** Fill the buffer with truncation characters. After filling * the buffer, a TruncationException will be thrown if the * appropriate flag is set. */ void truncationFiller(byte[] buffer, int offset, int length) throws TruncationException { for (int i = offset; i < offset + length; i += 1) { buffer[i] = truncationFill; } if (truncationThrow) { throw new TruncationException(); } return; } /** Fill the buffer with blanks to align * a field. */ public int alignFill(byte[] buffer, int offset, int len) { for (int i = offset; i < offset + len; i += 1) { buffer[i] = (byte) ' '; } return offset + len; } } fits-1.10.0/nom/tam/util/ByteParser.java0000664000175000017500000004011112031033644020207 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import java.io.UnsupportedEncodingException; import java.util.logging.Level; import java.util.logging.Logger; /** This class provides routines * for efficient parsing of data stored in a byte array. * This routine is optimized (in theory at least!) for efficiency * rather than accuracy. The values read in for doubles or floats * may differ in the last bit or so from the standard input * utilities, especially in the case where a float is specified * as a very long string of digits (substantially longer than * the precision of the type). *

* The get methods generally are available with or without a length * parameter specified. When a length parameter is specified only * the bytes with the specified range from the current offset will * be search for the number. If no length is specified, the entire * buffer from the current offset will be searched. *

* The getString method returns a string with leading and trailing * white space left intact. For all other get calls, leading * white space is ignored. If fillFields is set, then the get * methods check that only white space follows valid data and a * FormatException is thrown if that is not the case. If * fillFields is not set and valid data is found, then the * methods return having read as much as possible. E.g., for * the sequence "T123.258E13", a getBoolean, getInteger and * getFloat call would return true, 123, and 2.58e12 when * called in succession. * */ public class ByteParser { /** Array being parsed */ private byte[] input; /** Current offset into input. */ private int offset; /** Length of last parsed value */ private int numberLength; /** Did we find a sign last time we checked? */ private boolean foundSign; /** Do we fill up fields? */ private boolean fillFields = false; /** Construct a parser. * @param input The byte array to be parsed. * Note that the array can be re-used by * refilling its contents and resetting the offset. */ public ByteParser(byte[] input) { this.input = input; this.offset = 0; } /** Set the buffer for the parser */ public void setBuffer(byte[] buf) { this.input = buf; this.offset = 0; } /** Get the buffer being used by the parser */ public byte[] getBuffer() { return input; } /** Set the offset into the array. * @param offset The desired offset from the beginning * of the array. */ public void setOffset(int offset) { this.offset = offset; } /** Do we require a field to completely fill up the specified * length (with optional leading and trailing white space. @param flag Is filling required? */ public void setFillFields(boolean flag) { fillFields = flag; } /** Get the current offset @return The current offset within the buffer. */ public int getOffset() { return offset; } /** Get the number of characters used to parse the previous * number (or the length of the previous String returned). */ public int getNumberLength() { return numberLength; } /** Read in the buffer until a double is read. This will read * the entire buffer if fillFields is set. * @return The value found. */ public double getDouble() throws FormatException { return getDouble(input.length - offset); } /** Look for a double in the buffer. * Leading spaces are ignored. * @param length The maximum number of characters * used to parse this number. If fillFields * is specified then exactly only whitespace may follow * a valid double value. */ public double getDouble(int length) throws FormatException { int startOffset = offset; boolean error = true; double number = 0; int i = 0; // Skip initial blanks. length -= skipWhite(length); if (length == 0) { numberLength = offset - startOffset; return 0; } double mantissaSign = checkSign(); if (foundSign) { length -= 1; } // Look for the special strings NaN, Inf, if (length >= 3 && (input[offset] == 'n' || input[offset] == 'N') && (input[offset + 1] == 'a' || input[offset + 1] == 'A') && (input[offset + 2] == 'n' || input[offset + 2] == 'N')) { number = Double.NaN; length -= 3; offset += 3; // Look for the longer string first then try the shorter. } else if (length >= 8 && (input[offset] == 'i' || input[offset] == 'I') && (input[offset + 1] == 'n' || input[offset + 1] == 'N') && (input[offset + 2] == 'f' || input[offset + 2] == 'F') && (input[offset + 3] == 'i' || input[offset + 3] == 'I') && (input[offset + 4] == 'n' || input[offset + 4] == 'N') && (input[offset + 5] == 'i' || input[offset + 5] == 'I') && (input[offset + 6] == 't' || input[offset + 6] == 'T') && (input[offset + 7] == 'y' || input[offset + 7] == 'Y')) { number = Double.POSITIVE_INFINITY; length -= 8; offset += 8; } else if (length >= 3 && (input[offset] == 'i' || input[offset] == 'I') && (input[offset + 1] == 'n' || input[offset + 1] == 'N') && (input[offset + 2] == 'f' || input[offset + 2] == 'F')) { number = Double.POSITIVE_INFINITY; length -= 3; offset += 3; } else { number = getBareInteger(length); // This will update offset length -= numberLength; // Set by getBareInteger if (numberLength > 0) { error = false; } // Check for fractional values after decimal if (length > 0 && input[offset] == '.') { offset += 1; length -= 1; double numerator = getBareInteger(length); if (numerator > 0) { number += numerator / Math.pow(10., numberLength); } length -= numberLength; if (numberLength > 0) { error = false; } } if (error) { offset = startOffset; numberLength = 0; throw new FormatException("Invalid real field"); } // Look for an exponent if (length > 0) { // Our Fortran heritage means that we allow 'D' for the exponent indicator. if (input[offset] == 'e' || input[offset] == 'E' || input[offset] == 'd' || input[offset] == 'D') { offset += 1; length -= 1; if (length > 0) { int sign = checkSign(); if (foundSign) { length -= 1; } int exponent = (int) getBareInteger(length); // For very small numbers we try to miminize // effects of denormalization. if (exponent * sign > -300) { number *= Math.pow(10., exponent * sign); } else { number = 1.e-300 * (number * Math.pow(10., exponent * sign + 300)); } length -= numberLength; } } } } if (fillFields && length > 0) { if (isWhite(length)) { offset += length; } else { numberLength = 0; offset = startOffset; throw new FormatException("Non-blanks following real."); } } numberLength = offset - startOffset; return mantissaSign * number; } /** Get a floating point value from the buffer. (see getDouble(int()) */ public float getFloat() throws FormatException { return (float) getDouble(input.length - offset); } /** Get a floating point value in a region of the buffer */ public float getFloat(int length) throws FormatException { return (float) getDouble(length); } /** Convert a region of the buffer to an integer */ public int getInt(int length) throws FormatException { int startOffset = offset; length -= skipWhite(length); if (length == 0) { numberLength = offset - startOffset; return 0; } int number = 0; boolean error = true; int sign = checkSign(); if (foundSign) { length -= 1; } while (length > 0 && input[offset] >= '0' && input[offset] <= '9') { number = number * 10 + input[offset] - '0'; offset += 1; length -= 1; error = false; } if (error) { numberLength = 0; offset = startOffset; throw new FormatException("Invalid Integer"); } if (length > 0 && fillFields) { if (isWhite(length)) { offset += length; } else { numberLength = 0; offset = startOffset; throw new FormatException("Non-white following integer"); } } numberLength = offset - startOffset; return sign * number; } /** Look for an integer at the beginning of the buffer */ public int getInt() throws FormatException { return getInt(input.length - offset); } /** Look for a long in a specified region of the buffer */ public long getLong(int length) throws FormatException { int startOffset = offset; // Skip white space. length -= skipWhite(length); if (length == 0) { numberLength = offset - startOffset; return 0; } long number = 0; boolean error = true; long sign = checkSign(); if (foundSign) { length -= 1; } while (length > 0 && input[offset] >= '0' && input[offset] <= '9') { number = number * 10 + input[offset] - '0'; error = false; offset += 1; length -= 1; } if (error) { numberLength = 0; offset = startOffset; throw new FormatException("Invalid long number"); } if (length > 0 && fillFields) { if (isWhite(length)) { offset += length; } else { offset = startOffset; numberLength = 0; throw new FormatException("Non-white following long"); } } numberLength = offset - startOffset; return sign * number; } /** Get a string * @param length The length of the string. */ public String getString(int length) { String s = AsciiFuncs.asciiString(input, offset, length); offset += length; numberLength = length; return s; } /** Get a boolean value from the beginning of the buffer */ public boolean getBoolean() throws FormatException { return getBoolean(input.length - offset); } /** Get a boolean value from a specified region of the buffer */ public boolean getBoolean(int length) throws FormatException { int startOffset = offset; length -= skipWhite(length); if (length == 0) { throw new FormatException("Blank boolean field"); } boolean value = false; if (input[offset] == 'T' || input[offset] == 't') { value = true; } else if (input[offset] != 'F' && input[offset] != 'f') { numberLength = 0; offset = startOffset; throw new FormatException("Invalid boolean value"); } offset += 1; length -= 1; if (fillFields && length > 0) { if (isWhite(length)) { offset += length; } else { numberLength = 0; offset = startOffset; throw new FormatException("Non-white following boolean"); } } numberLength = offset - startOffset; return value; } /** Skip bytes in the buffer */ public void skip(int nBytes) { offset += nBytes; } /** Get the integer value starting at the current position. * This routine returns a double rather than an int/long * to enable it to read very long integers (with reduced * precision) such as 111111111111111111111111111111111111111111. * Note that this routine does set numberLength. * * @param length The maximum number of characters to use. */ private double getBareInteger(int length) { int startOffset = offset; double number = 0; while (length > 0 && input[offset] >= '0' && input[offset] <= '9') { number *= 10; number += input[offset] - '0'; offset += 1; length -= 1; } numberLength = offset - startOffset; return number; } /** Skip white space. This routine skips with space in * the input and returns the number of character skipped. * White space is defined as ' ', '\t', '\n' or '\r' * * @param length The maximum number of characters to skip. */ public int skipWhite(int length) { int i; for (i = 0; i < length; i += 1) { if (input[offset + i] != ' ' && input[offset + i] != '\t' && input[offset + i] != '\n' && input[offset + i] != '\r') { break; } } offset += i; return i; } /** Find the sign for a number . * This routine looks for a sign (+/-) at the current location * and return +1/-1 if one is found, or +1 if not. * The foundSign boolean is set if a sign is found and offset is * incremented. */ private int checkSign() { foundSign = false; if (input[offset] == '+') { foundSign = true; offset += 1; return 1; } else if (input[offset] == '-') { foundSign = true; offset += 1; return -1; } return 1; } /** Is a region blank? * @param length The length of the region to be tested */ private boolean isWhite(int length) { int oldOffset = offset; boolean value = skipWhite(length) == length; offset = oldOffset; return value; } } fits-1.10.0/nom/tam/util/ArrayFuncs.java0000664000175000017500000012615612031033644020222 0ustar frothmaifrothmai// Member of the utility package. // Modified July 20, 2009 to handle very large arrays // in some contexts. package nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import java.lang.reflect.*; import java.util.Arrays; /** This is a package of static functions which perform * computations on arrays. Generally these routines attempt * to complete without throwing errors by ignoring data * they cannot understand. */ public class ArrayFuncs implements PrimitiveInfo { /** Compute the size of an object. Note that this only handles * arrays or scalars of the primitive objects and Strings. It * returns 0 for any object array element it does not understand. * * @param o The object whose size is desired. * @deprecated May silently underestimate the size * if the size > 2 GB. */ public static int computeSize(Object o) { return (int) computeLSize(o); } public static long computeLSize(Object o) { if (o == null) { return 0; } long size = 0; String classname = o.getClass().getName(); if (classname.substring(0, 2).equals("[[")) { for (int i = 0; i < ((Object[]) o).length; i += 1) { size += computeLSize(((Object[]) o)[i]); } return size; } if (classname.charAt(0) == '[' && classname.charAt(1) != 'L') { char c = classname.charAt(1); for (int i = 0; i < PrimitiveInfo.suffixes.length; i += 1) { if (c == PrimitiveInfo.suffixes[i]) { return (long) (Array.getLength(o)) * PrimitiveInfo.sizes[i]; } } return 0; } // Do we have a non-primitive array? if (classname.charAt(0) == '[') { int len = 0; for (int i = 0; i < Array.getLength(o); i += 1) { len += computeLSize(Array.get(o, i)); } return len; } // Now a few special scalar objects. if (classname.substring(0, 10).equals("java.lang.")) { classname = classname.substring(10, classname.length()); if (classname.equals("Integer") || classname.equals("Float")) { return 4; } else if (classname.equals("Double") || classname.equals("Long")) { return 8; } else if (classname.equals("Short") || classname.equals("Char")) { return 2; } else if (classname.equals("Byte") || classname.equals("Boolean")) { return 1; } else if (classname.equals("String")) { return ((String) o).length(); } else { return 0; } } return 0; } /** Count the number of elements in an array. * @deprecated May silently underestimate * size if number is > 2 G. */ public static int nElements(Object o) { return (int) nLElements(o); } /** Count the number of elements in an array. * @deprecated May silently underestimate * size if number is > 2 G. */ public static long nLElements(Object o) { if (o == null) { return 0; } String classname = o.getClass().getName(); if (classname.charAt(1) == '[') { int count = 0; for (int i = 0; i < ((Object[]) o).length; i += 1) { count += nLElements(((Object[]) o)[i]); } return count; } else if (classname.charAt(0) == '[') { return Array.getLength(o); } else { return 1; } } /** Try to create a deep clone of an Array or a standard clone of a scalar. * The object may comprise arrays of * any primitive type or any Object type which implements Cloneable. * However, if the Object is some kind of collection, e.g., a Vector * then only a shallow copy of that object is made. I.e., deep refers * only to arrays. * * @param o The object to be copied. */ public static Object deepClone(Object o) { if (o == null) { return null; } String classname = o.getClass().getName(); // Is this an array? if (classname.charAt(0) != '[') { return genericClone(o); } // Check if this is a 1D primitive array. if (classname.charAt(1) != '[' && classname.charAt(1) != 'L') { try { // Some compilers (SuperCede, e.g.) still // think you have to catch this... if (false) { throw new CloneNotSupportedException(); } switch (classname.charAt(1)) { case 'B': return ((byte[]) o).clone(); case 'Z': return ((boolean[]) o).clone(); case 'C': return ((char[]) o).clone(); case 'S': return ((short[]) o).clone(); case 'I': return ((int[]) o).clone(); case 'J': return ((long[]) o).clone(); case 'F': return ((float[]) o).clone(); case 'D': return ((double[]) o).clone(); default: System.err.println("Unknown primtive array class:" + classname); return null; } } catch (CloneNotSupportedException e) { } } // Get the base type. int ndim = 1; while (classname.charAt(ndim) == '[') { ndim += 1; } Class baseClass; if (classname.charAt(ndim) != 'L') { baseClass = getBaseClass(o); } else { try { baseClass = Class.forName(classname.substring(ndim + 1, classname.length() - 1)); } catch (ClassNotFoundException e) { System.err.println("Internal error: class definition inconsistency: " + classname); return null; } } // Allocate the array but make all but the first dimension 0. int[] dims = new int[ndim]; dims[0] = Array.getLength(o); for (int i = 1; i < ndim; i += 1) { dims[i] = 0; } Object copy = ArrayFuncs.newInstance(baseClass, dims); // Now fill in the next level down by recursion. for (int i = 0; i < dims[0]; i += 1) { Array.set(copy, i, deepClone(Array.get(o, i))); } return copy; } /** Clone an Object if possible. * * This method returns an Object which is a clone of the * input object. It checks if the method implements the * Cloneable interface and then uses reflection to invoke * the clone method. This can't be done directly since * as far as the compiler is concerned the clone method for * Object is protected and someone could implement Cloneable but * leave the clone method protected. The cloning can fail in a * variety of ways which are trapped so that it returns null instead. * This method will generally create a shallow clone. If you * wish a deep copy of an array the method deepClone should be used. * * @param o The object to be cloned. */ public static Object genericClone(Object o) { if (!(o instanceof Cloneable)) { return null; } Class[] argTypes = new Class[0]; Object[] args = new Object[0]; Class type = o.getClass(); try { return type.getMethod("clone", argTypes).invoke(o, args); } catch (Exception e) { if (type.isArray()) { return deepClone(o); } // Implements cloneable, but does not // apparently make clone public. return null; } } /** Copy one array into another. * This function copies the contents of one array * into a previously allocated array. * The arrays must agree in type and size. * @param original The array to be copied. * @param copy The array to be copied into. This * array must already be fully allocated. */ public static void copyArray(Object original, Object copy) { String oname = original.getClass().getName(); String cname = copy.getClass().getName(); if (!oname.equals(cname)) { return; } if (oname.charAt(0) != '[') { return; } if (oname.charAt(1) == '[') { Object[] x = (Object[]) original; Object[] y = (Object[]) copy; if (x.length != y.length) { return; } for (int i = 0; i < x.length; i += 1) { copyArray(x, y); } } int len = Array.getLength(original); System.arraycopy(original, 0, copy, 0, len); } /** Find the dimensions of an object. * * This method returns an integer array with the dimensions * of the object o which should usually be an array. * * It returns an array of dimension 0 for scalar objects * and it returns -1 for dimension which have not been allocated, * e.g., int[][][] x = new int[100][][]; should return [100,-1,-1]. * * @param o The object to get the dimensions of. */ public static int[] getDimensions(Object o) { if (o == null) { return null; } String classname = o.getClass().getName(); int ndim = 0; while (classname.charAt(ndim) == '[') { ndim += 1; } int[] dimens = new int[ndim]; for (int i = 0; i < ndim; i += 1) { dimens[i] = -1; // So that we can distinguish a null from a 0 length. } for (int i = 0; i < ndim; i += 1) { dimens[i] = java.lang.reflect.Array.getLength(o); if (dimens[i] == 0) { return dimens; } if (i != ndim - 1) { o = ((Object[]) o)[0]; if (o == null) { return dimens; } } } return dimens; } /** This routine returns the base array of a multi-dimensional * array. I.e., a one-d array of whatever the array is composed * of. Note that arrays are not guaranteed to be rectangular, * so this returns o[0][0].... */ public static Object getBaseArray(Object o) { String cname = o.getClass().getName(); if (cname.charAt(1) == '[') { return getBaseArray(((Object[]) o)[0]); } else { return o; } } /** This routine returns the base class of an object. This is just * the class of the object for non-arrays. */ public static Class getBaseClass(Object o) { if (o == null) { return Void.TYPE; } String className = o.getClass().getName(); int dims = 0; while (className.charAt(dims) == '[') { dims += 1; } if (dims == 0) { return o.getClass(); } char c = className.charAt(dims); for (int i = 0; i < PrimitiveInfo.suffixes.length; i += 1) { if (c == PrimitiveInfo.suffixes[i]) { return PrimitiveInfo.classes[i]; } } if (c == 'L') { try { return Class.forName(className.substring(dims + 1, className.length() - 1)); } catch (ClassNotFoundException e) { return null; } } return null; } /** This routine returns the size of the base element of an array. * @param o The array object whose base length is desired. * @return the size of the object in bytes, 0 if null, or * -1 if not a primitive array. */ public static int getBaseLength(Object o) { if (o == null) { return 0; } String className = o.getClass().getName(); int dims = 0; while (className.charAt(dims) == '[') { dims += 1; } if (dims == 0) { return -1; } char c = className.charAt(dims); for (int i = 0; i < PrimitiveInfo.suffixes.length; i += 1) { if (c == PrimitiveInfo.suffixes[i]) { return PrimitiveInfo.sizes[i]; } } return -1; } /** Create an array and populate it with a test pattern. * * @param baseType The base type of the array. This is expected to * be a numeric type, but this is not checked. * @param dims The desired dimensions. * @return An array object populated with a simple test pattern. */ public static Object generateArray(Class baseType, int[] dims) { // Generate an array and populate it with a test pattern of // data. Object x = ArrayFuncs.newInstance(baseType, dims); testPattern(x, (byte) 0); return x; } /** Just create a simple pattern cycling through valid byte values. * We use bytes because they can be cast to any other numeric type. * @param o The array in which the test pattern is to be set. * @param start The value for the first element. */ public static byte testPattern(Object o, byte start) { int[] dims = getDimensions(o); if (dims.length > 1) { for (int i = 0; i < ((Object[]) o).length; i += 1) { start = testPattern(((Object[]) o)[i], start); } } else if (dims.length == 1) { for (int i = 0; i < dims[0]; i += 1) { java.lang.reflect.Array.setByte(o, i, start); start += 1; } } return start; } /** Generate a description of an array (presumed rectangular). * @param o The array to be described. */ public static String arrayDescription(Object o) { Class base = getBaseClass(o); if (base == Void.TYPE) { return "NULL"; } int[] dims = getDimensions(o); StringBuffer desc = new StringBuffer(); // Note that all instances Class describing a given class are // the same so we can use == here. boolean found = false; for (int i = 0; i < PrimitiveInfo.classes.length; i += 1) { if (base == PrimitiveInfo.classes[i]) { found = true; desc.append(PrimitiveInfo.types[i]); break; } } if (!found) { desc.append(base.getName()); } if (dims != null) { desc.append("["); for (int i = 0; i < dims.length; i += 1) { desc.append("" + dims[i]); if (i < dims.length - 1) { desc.append("]["); } } desc.append("]"); } return new String(desc); } /** Examine the structure of an array in detail. * @param o The array to be examined. */ public static void examinePrimitiveArray(Object o) { String className = o.getClass().getName(); // If we have a two-d array, or if the array is a one-d array // of Objects, then recurse over the next dimension. We handle // Object specially because each element could itself be an array. if (className.substring(0, 2).equals("[[") || className.equals("[Ljava.lang.Object;")) { System.out.println("["); for (int i = 0; i < ((Object[]) o).length; i += 1) { examinePrimitiveArray(((Object[]) o)[i]); } System.out.print("]"); } else if (className.charAt(0) != '[') { System.out.println(className); } else { System.out.println("[" + java.lang.reflect.Array.getLength(o) + "]" + className.substring(1)); } } /** Given an array of arbitrary dimensionality return * the array flattened into a single dimension. * @param input The input array. */ public static Object flatten(Object input) { int[] dimens = getDimensions(input); if (dimens.length <= 1) { return input; } int size = 1; for (int i = 0; i < dimens.length; i += 1) { size *= dimens[i]; } Object flat = ArrayFuncs.newInstance(getBaseClass(input), size); if (size == 0) { return flat; } int offset = 0; doFlatten(input, flat, offset); return flat; } /** This routine does the actually flattening of multi-dimensional * arrays. * @param input The input array to be flattened. * @param output The flattened array. * @param offset The current offset within the output array. * @return The number of elements within the array. */ protected static int doFlatten(Object input, Object output, int offset) { String classname = input.getClass().getName(); if (classname.charAt(0) != '[') { throw new RuntimeException("Attempt to flatten non-array"); } int size = Array.getLength(input); if (classname.charAt(1) != '[') { System.arraycopy(input, 0, output, offset, size); return size; } int total = 0; Object[] xx = (Object[]) input; for (int i = 0; i < size; i += 1) { int len = doFlatten(xx[i], output, offset + total); total += len; } return total; } /** Curl an input array up into a multi-dimensional array. * * @param input The one dimensional array to be curled. * @param dimens The desired dimensions * @return The curled array. */ public static Object curl(Object input, int[] dimens) { if (input == null) { return null; } String classname = input.getClass().getName(); if (classname.charAt(0) != '[' || classname.charAt(1) == '[') { throw new RuntimeException("Attempt to curl non-1D array"); } int size = Array.getLength(input); int test = 1; for (int i = 0; i < dimens.length; i += 1) { test *= dimens[i]; } if (test != size) { throw new RuntimeException("Curled array does not fit desired dimensions"); } Class base = getBaseClass(input); Object newArray = ArrayFuncs.newInstance(base, dimens); int offset = 0; doCurl(input, newArray, dimens, offset); return newArray; } /** Do the curling of the 1-d to multi-d array. * @param input The 1-d array to be curled. * @param output The multi-dimensional array to be filled. * @param dimens The desired output dimensions. * @param offset The current offset in the input array. * @return The number of elements curled. */ protected static int doCurl(Object input, Object output, int[] dimens, int offset) { if (dimens.length == 1) { System.arraycopy(input, offset, output, 0, dimens[0]); return dimens[0]; } int total = 0; int[] xdimens = new int[dimens.length - 1]; for (int i = 1; i < dimens.length; i += 1) { xdimens[i - 1] = dimens[i]; } for (int i = 0; i < dimens[0]; i += 1) { total += doCurl(input, ((Object[]) output)[i], xdimens, offset + total); } return total; } /** Create an array of a type given by new type with * the dimensionality given in array. * @param array A possibly multidimensional array to be converted. * @param newType The desired output type. This should be one of the * class descriptors for primitive numeric data, e.g., double.type. */ public static Object mimicArray(Object array, Class newType) { String classname = array.getClass().getName(); if (classname.charAt(0) != '[') { return null; } int dims = 1; while (classname.charAt(dims) == '[') { dims += 1; } Object mimic; if (dims > 1) { Object[] xarray = (Object[]) array; int[] dimens = new int[dims]; dimens[0] = xarray.length; // Leave other dimensions at 0. mimic = ArrayFuncs.newInstance(newType, dimens); for (int i = 0; i < xarray.length; i += 1) { Object temp = mimicArray(xarray[i], newType); ((Object[]) mimic)[i] = temp; } } else { mimic = ArrayFuncs.newInstance(newType, Array.getLength(array)); } return mimic; } /** Convert an array to a specified type. This method supports conversions * only among the primitive numeric types. * @param array A possibly multidimensional array to be converted. * @param newType The desired output type. This should be one of the * class descriptors for primitive numeric data, e.g., double.type. * @param reuse If set, and the requested type is the same as the * original, then the original is returned. */ public static Object convertArray(Object array, Class newType, boolean reuse) { if (getBaseClass(array) == newType && reuse) { return array; } else { return convertArray(array, newType); } } /** Convert an array to a specified type. This method supports conversions * only among the primitive numeric types. * @param array A possibly multidimensional array to be converted. * @param newType The desired output type. This should be one of the * class descriptors for primitive numeric data, e.g., double.type. */ public static Object convertArray(Object array, Class newType) { /* We break this up into two steps so that users * can reuse an array many times and only allocate a * new array when needed. */ /* First create the full new array. */ Object mimic = mimicArray(array, newType); if (mimic == null) { return mimic; } /* Now copy the info into the new array */ copyInto(array, mimic); return mimic; } /** Copy an array into an array of a different type. * The dimensions and dimensionalities of the two * arrays should be the same. * @param array The original array. * @param mimic The array mimicking the original. */ public static void copyInto(Object array, Object mimic) { String classname = array.getClass().getName(); if (classname.charAt(0) != '[') { return; } /* Do multidimensional arrays recursively */ if (classname.charAt(1) == '[') { for (int i = 0; i < ((Object[]) array).length; i += 1) { copyInto(((Object[]) array)[i], ((Object[]) mimic)[i]); } } else { byte[] xbarr; short[] xsarr; char[] xcarr; int[] xiarr; long[] xlarr; float[] xfarr; double[] xdarr; Class base = getBaseClass(array); Class newType = getBaseClass(mimic); if (base == byte.class) { byte[] barr = (byte[]) array; if (newType == byte.class) { System.arraycopy(array, 0, mimic, 0, barr.length); } else if (newType == short.class) { xsarr = (short[]) mimic; for (int i = 0; i < barr.length; i += 1) { xsarr[i] = barr[i]; } } else if (newType == char.class) { xcarr = (char[]) mimic; for (int i = 0; i < barr.length; i += 1) { xcarr[i] = (char) barr[i]; } } else if (newType == int.class) { xiarr = (int[]) mimic; for (int i = 0; i < barr.length; i += 1) { xiarr[i] = barr[i]; } } else if (newType == long.class) { xlarr = (long[]) mimic; for (int i = 0; i < barr.length; i += 1) { xlarr[i] = barr[i]; } } else if (newType == float.class) { xfarr = (float[]) mimic; for (int i = 0; i < barr.length; i += 1) { xfarr[i] = barr[i]; } } else if (newType == double.class) { xdarr = (double[]) mimic; for (int i = 0; i < barr.length; i += 1) { xdarr[i] = barr[i]; } } } else if (base == short.class) { short[] sarr = (short[]) array; if (newType == byte.class) { xbarr = (byte[]) mimic; for (int i = 0; i < sarr.length; i += 1) { xbarr[i] = (byte) sarr[i]; } } else if (newType == short.class) { System.arraycopy(array, 0, mimic, 0, sarr.length); } else if (newType == char.class) { xcarr = (char[]) mimic; for (int i = 0; i < sarr.length; i += 1) { xcarr[i] = (char) sarr[i]; } } else if (newType == int.class) { xiarr = (int[]) mimic; for (int i = 0; i < sarr.length; i += 1) { xiarr[i] = sarr[i]; } } else if (newType == long.class) { xlarr = (long[]) mimic; for (int i = 0; i < sarr.length; i += 1) { xlarr[i] = sarr[i]; } } else if (newType == float.class) { xfarr = (float[]) mimic; for (int i = 0; i < sarr.length; i += 1) { xfarr[i] = sarr[i]; } } else if (newType == double.class) { xdarr = (double[]) mimic; for (int i = 0; i < sarr.length; i += 1) { xdarr[i] = sarr[i]; } } } else if (base == char.class) { char[] carr = (char[]) array; if (newType == byte.class) { xbarr = (byte[]) mimic; for (int i = 0; i < carr.length; i += 1) { xbarr[i] = (byte) carr[i]; } } else if (newType == short.class) { xsarr = (short[]) mimic; for (int i = 0; i < carr.length; i += 1) { xsarr[i] = (short) carr[i]; } } else if (newType == char.class) { System.arraycopy(array, 0, mimic, 0, carr.length); } else if (newType == int.class) { xiarr = (int[]) mimic; for (int i = 0; i < carr.length; i += 1) { xiarr[i] = carr[i]; } } else if (newType == long.class) { xlarr = (long[]) mimic; for (int i = 0; i < carr.length; i += 1) { xlarr[i] = carr[i]; } } else if (newType == float.class) { xfarr = (float[]) mimic; for (int i = 0; i < carr.length; i += 1) { xfarr[i] = carr[i]; } } else if (newType == double.class) { xdarr = (double[]) mimic; for (int i = 0; i < carr.length; i += 1) { xdarr[i] = carr[i]; } } } else if (base == int.class) { int[] iarr = (int[]) array; if (newType == byte.class) { xbarr = (byte[]) mimic; for (int i = 0; i < iarr.length; i += 1) { xbarr[i] = (byte) iarr[i]; } } else if (newType == short.class) { xsarr = (short[]) mimic; for (int i = 0; i < iarr.length; i += 1) { xsarr[i] = (short) iarr[i]; } } else if (newType == char.class) { xcarr = (char[]) mimic; for (int i = 0; i < iarr.length; i += 1) { xcarr[i] = (char) iarr[i]; } } else if (newType == int.class) { System.arraycopy(array, 0, mimic, 0, iarr.length); } else if (newType == long.class) { xlarr = (long[]) mimic; for (int i = 0; i < iarr.length; i += 1) { xlarr[i] = iarr[i]; } } else if (newType == float.class) { xfarr = (float[]) mimic; for (int i = 0; i < iarr.length; i += 1) { xfarr[i] = iarr[i]; } } else if (newType == double.class) { xdarr = (double[]) mimic; for (int i = 0; i < iarr.length; i += 1) { xdarr[i] = iarr[i]; } } } else if (base == long.class) { long[] larr = (long[]) array; if (newType == byte.class) { xbarr = (byte[]) mimic; for (int i = 0; i < larr.length; i += 1) { xbarr[i] = (byte) larr[i]; } } else if (newType == short.class) { xsarr = (short[]) mimic; for (int i = 0; i < larr.length; i += 1) { xsarr[i] = (short) larr[i]; } } else if (newType == char.class) { xcarr = (char[]) mimic; for (int i = 0; i < larr.length; i += 1) { xcarr[i] = (char) larr[i]; } } else if (newType == int.class) { xiarr = (int[]) mimic; for (int i = 0; i < larr.length; i += 1) { xiarr[i] = (int) larr[i]; } } else if (newType == long.class) { System.arraycopy(array, 0, mimic, 0, larr.length); } else if (newType == float.class) { xfarr = (float[]) mimic; for (int i = 0; i < larr.length; i += 1) { xfarr[i] = (float) larr[i]; } } else if (newType == double.class) { xdarr = (double[]) mimic; for (int i = 0; i < larr.length; i += 1) { xdarr[i] = (double) larr[i]; } } } else if (base == float.class) { float[] farr = (float[]) array; if (newType == byte.class) { xbarr = (byte[]) mimic; for (int i = 0; i < farr.length; i += 1) { xbarr[i] = (byte) farr[i]; } } else if (newType == short.class) { xsarr = (short[]) mimic; for (int i = 0; i < farr.length; i += 1) { xsarr[i] = (short) farr[i]; } } else if (newType == char.class) { xcarr = (char[]) mimic; for (int i = 0; i < farr.length; i += 1) { xcarr[i] = (char) farr[i]; } } else if (newType == int.class) { xiarr = (int[]) mimic; for (int i = 0; i < farr.length; i += 1) { xiarr[i] = (int) farr[i]; } } else if (newType == long.class) { xlarr = (long[]) mimic; for (int i = 0; i < farr.length; i += 1) { xlarr[i] = (long) farr[i]; } } else if (newType == float.class) { System.arraycopy(array, 0, mimic, 0, farr.length); } else if (newType == double.class) { xdarr = (double[]) mimic; for (int i = 0; i < farr.length; i += 1) { xdarr[i] = farr[i]; } } } else if (base == double.class) { double[] darr = (double[]) array; if (newType == byte.class) { xbarr = (byte[]) mimic; for (int i = 0; i < darr.length; i += 1) { xbarr[i] = (byte) darr[i]; } } else if (newType == short.class) { xsarr = (short[]) mimic; for (int i = 0; i < darr.length; i += 1) { xsarr[i] = (short) darr[i]; } } else if (newType == char.class) { xcarr = (char[]) mimic; for (int i = 0; i < darr.length; i += 1) { xcarr[i] = (char) darr[i]; } } else if (newType == int.class) { xiarr = (int[]) mimic; for (int i = 0; i < darr.length; i += 1) { xiarr[i] = (int) darr[i]; } } else if (newType == long.class) { xlarr = (long[]) mimic; for (int i = 0; i < darr.length; i += 1) { xlarr[i] = (long) darr[i]; } } else if (newType == float.class) { xfarr = (float[]) mimic; for (int i = 0; i < darr.length; i += 1) { xfarr[i] = (float) darr[i]; } } else if (newType == double.class) { System.arraycopy(array, 0, mimic, 0, darr.length); } } } return; } /** Allocate an array dynamically. The Array.newInstance method * does not throw an error when there is insufficient memory * and silently returns a null. * @param cl The class of the array. * @param dim The dimension of the array. * @return The allocated array. * @throws An OutOfMemoryError if insufficient space is available. */ public static Object newInstance(Class cl, int dim) { Object o = Array.newInstance(cl, dim); if (o == null) { String desc = cl + "[" + dim + "]"; throw new OutOfMemoryError("Unable to allocate array: " + desc); } return o; } /** Allocate an array dynamically. The Array.newInstance method * does not throw an error and silently returns a null. * * @param cl The class of the array. * @param dims The dimensions of the array. * @return The allocated array. * @throws An OutOfMemoryError if insufficient space is available. */ public static Object newInstance(Class cl, int[] dims) { if (dims.length == 0) { // Treat a scalar as a 1-d array of length 1 dims = new int[]{1}; } Object o = Array.newInstance(cl, dims); if (o == null) { String desc = cl + "["; String comma = ""; for (int i = 0; i < dims.length; i += 1) { desc += comma + dims[i]; comma = ","; } desc += "]"; throw new OutOfMemoryError("Unable to allocate array: " + desc); } return o; } /** Are two objects equal? Arrays have the standard object equals * method which only returns true if the two object are the same. * This method returns true if every element of the arrays match. * The inputs may be of any dimensionality. The dimensionality * and dimensions of the arrays must match as well as any elements. * If the elements are non-primitive. non-array objects, then the * equals method is called for each element. * If both elements are multi-dimensional arrays, then * the method recurses. */ public static boolean arrayEquals(Object x, Object y) { return arrayEquals(x, y, 0, 0); } /** Are two objects equal? Arrays have the standard object equals * method which only returns true if the two object are the same. * This method returns true if every element of the arrays match. * The inputs may be of any dimensionality. The dimensionality * and dimensions of the arrays must match as well as any elements. * If the elements are non-primitive. non-array objects, then the * equals method is called for each element. * If both elements are multi-dimensional arrays, then * the method recurses. */ public static boolean arrayEquals(Object x, Object y, double tolf, double told) { // Handle the special cases first. // We treat null == null so that two object arrays // can match if they have matching null elements. if (x == null && y == null) { return true; } if (x == null || y == null) { return false; } Class xClass = x.getClass(); Class yClass = y.getClass(); if (xClass != yClass) { return false; } if (!xClass.isArray()) { return x.equals(y); } else { if (xClass.equals(int[].class)) { return Arrays.equals((int[]) x, (int[]) y); } else if (xClass.equals(double[].class)) { if (told == 0) { return Arrays.equals((double[]) x, (double[]) y); } else { return doubleArrayEquals((double[]) x, (double[]) y, told); } } else if (xClass.equals(long[].class)) { return Arrays.equals((long[]) x, (long[]) y); } else if (xClass.equals(float[].class)) { if (tolf == 0) { return Arrays.equals((float[]) x, (float[]) y); } else { return floatArrayEquals((float[]) x, (float[]) y, (float) tolf); } } else if (xClass.equals(byte[].class)) { return Arrays.equals((byte[]) x, (byte[]) y); } else if (xClass.equals(short[].class)) { return Arrays.equals((short[]) x, (short[]) y); } else if (xClass.equals(char[].class)) { return Arrays.equals((char[]) x, (char[]) y); } else if (xClass.equals(boolean[].class)) { return Arrays.equals((boolean[]) x, (boolean[]) y); } else { // Non-primitive and multidimensional arrays can be // cast to Object[] Object[] xo = (Object[]) x; Object[] yo = (Object[]) y; if (xo.length != yo.length) { return false; } for (int i = 0; i < xo.length; i += 1) { if (!arrayEquals(xo[i], yo[i], tolf, told)) { return false; } } return true; } } } /** Compare two double arrays using a given tolerance */ public static boolean doubleArrayEquals(double[] x, double[] y, double tol) { for (int i = 0; i < x.length; i += 1) { if (x[i] == 0) { return y[i] == 0; } if (Math.abs((y[i] - x[i]) / x[i]) > tol) { return false; } } return true; } /** Compare two float arrays using a given tolerance */ public static boolean floatArrayEquals(float[] x, float[] y, float tol) { for (int i = 0; i < x.length; i += 1) { if (x[i] == 0) { return y[i] == 0; } if (Math.abs((y[i] - x[i]) / x[i]) > tol) { return false; } } return true; } /** Dump an array on the given print steam */ public static void dumpArray(java.io.PrintStream p, Object arr) { // Get the dimensionality and then dump. if (arr == null) { p.print("null "); } else { Class nm = arr.getClass(); if (nm.isArray()) { p.print("["); for (int i = 0; i < java.lang.reflect.Array.getLength(arr); i += 1) { dumpArray(p, java.lang.reflect.Array.get(arr, i)); } p.print("]\n"); } else { p.print(" " + arr.toString() + " "); } } } } fits-1.10.0/nom/tam/util/PrimitiveInfo.java0000664000175000017500000000421312031033644020716 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This interface collects some information about Java primitives. */ public interface PrimitiveInfo { /** Suffixes used for the classnames for primitive arrays. */ char[] suffixes = new char[]{'B', 'S', 'C', 'I', 'J', 'F', 'D', 'Z'}; /** Classes of the primitives. These should be in widening order * (char is as always a problem). */ Class[] classes = new Class[]{ byte.class, short.class, char.class, int.class, long.class, float.class, double.class, boolean.class}; /** Is this a numeric class */ boolean[] isNumeric = new boolean[]{true, true, true, true, true, true, true, false}; /** Full names */ String[] types = new String[]{ "byte", "short", "char", "int", "long", "float", "double", "boolean" }; /** Sizes */ int[] sizes = new int[]{1, 2, 2, 4, 8, 4, 8, 1}; /** Index of first element of above arrays referring to a numeric type */ int FIRST_NUMERIC = 0; /** Index of last element of above arrays referring to a numeric type */ int LAST_NUMERIC = 6; int BYTE_INDEX = 0; int SHORT_INDEX = 1; int CHAR_INDEX = 2; int INT_INDEX = 3; int LONG_INDEX = 4; int FLOAT_INDEX = 5; int DOUBLE_INDEX = 6; int BOOLEAN_INDEX = 7; } fits-1.10.0/nom/tam/util/ColumnTable.java0000664000175000017500000006714712031033644020356 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import java.io.*; import java.lang.reflect.Array; /** A data table is conventionally considered to consist of rows and * columns, where the structure within each column is constant, but * different columns may have different structures. I.e., structurally * columns may differ but rows are identical. * Typically tabular data is usually stored in row order which can * make it extremely difficult to access efficiently using Java. * This class provides efficient * access to data which is stored in row order and allows users to * get and set the elements of the table. * The table can consist only of arrays of primitive types. * Data stored in column order can * be efficiently read and written using the * BufferedDataXputStream classes. * * The table is represented entirely as a set of one-dimensional primitive * arrays. For a given column, a row consists of some number of * contiguous elements of the array. Each column is required to have * the same number of rows. */ public class ColumnTable implements DataTable { /** The columns to be read/written */ private Object[] arrays; /** The number of elements in a row for each column */ private int[] sizes; /** The number of rows */ private int nrow; /** The number or rows to read/write in one I/O. */ private int chunk; /** The size of a row in bytes */ private int rowSize; /** The base type of each row (using the second character * of the [x class names of the arrays. */ private char[] types; private Class[] bases; // The following arrays are used to avoid having to check // casts during the I/O loops. // They point to elements of arrays. private byte[][] bytePointers; private short[][] shortPointers; private int[][] intPointers; private long[][] longPointers; private float[][] floatPointers; private double[][] doublePointers; private char[][] charPointers; private boolean[][] booleanPointers; /** Create the object after checking consistency. * @param arrays An array of one-d primitive arrays. * @param sizes The number of elements in each row * for the corresponding column */ public ColumnTable(Object[] arrays, int[] sizes) throws TableException { setup(arrays, sizes); } /** Actually perform the initialization. */ protected void setup(Object[] arrays, int[] sizes) throws TableException { checkArrayConsistency(arrays, sizes); getNumberOfRows(); initializePointers(); } /** Get the number of rows in the table. */ public int getNRows() { return nrow; } /** Get the number of columns in the table. */ public int getNCols() { return arrays.length; } /** Get a particular column. * @param col The column desired. * @return an object containing the column data desired. * This will be an instance of a 1-d primitive array. */ public Object getColumn(int col) { return arrays[col]; } /** * Set the values in a particular column. The new values must match the old in * length but not necessarily in type. * * @param col * The column to modify. * @param newColumn * The new column data. This should be a primitive array. * @exception TableException * Thrown when the new data is not commenserable with information * in the table. */ public void setColumn(int col, Object newColumn) throws TableException { boolean reset = newColumn.getClass() != arrays[col].getClass() || Array.getLength(newColumn) != Array.getLength(arrays[col]); arrays[col] = newColumn; if (reset) { setup(arrays, sizes); } else { // This is required, because otherwise the typed pointer may point to the old // array, which has been replaced by newColumn. Added by Jeroen de Jong, 1 Aug 2006 initializePointers(); } } /** Add a column */ public void addColumn(Object newColumn, int size) throws TableException { String classname = newColumn.getClass().getName(); nrow = checkColumnConsistency(newColumn, classname, nrow, size); rowSize += nrow * ArrayFuncs.getBaseLength(newColumn); getNumberOfRows(); int ncol = arrays.length; Object[] newArrays = new Object[ncol + 1]; int[] newSizes = new int[ncol + 1]; Class[] newBases = new Class[ncol + 1]; char[] newTypes = new char[ncol + 1]; System.arraycopy(arrays, 0, newArrays, 0, ncol); System.arraycopy(sizes, 0, newSizes, 0, ncol); System.arraycopy(bases, 0, newBases, 0, ncol); System.arraycopy(types, 0, newTypes, 0, ncol); arrays = newArrays; sizes = newSizes; bases = newBases; types = newTypes; arrays[ncol] = newColumn; sizes[ncol] = size; bases[ncol] = ArrayFuncs.getBaseClass(newColumn); types[ncol] = classname.charAt(1); addPointer(newColumn); } /** Add a row to the table. This method is very inefficient * for adding multiple rows and should be avoided if possible. */ public void addRow(Object[] row) throws TableException { if (arrays.length == 0) { for (int i = 0; i < row.length; i += 1) { addColumn(row[i], Array.getLength(row[i])); } } else { if (row.length != arrays.length) { throw new TableException("Row length mismatch"); } for (int i = 0; i < row.length; i += 1) { if (row[i].getClass() != arrays[i].getClass() || Array.getLength(row[i]) != sizes[i]) { throw new TableException("Row column mismatch at column:" + i); } Object xarray = ArrayFuncs.newInstance(bases[i], (nrow + 1) * sizes[i]); System.arraycopy(arrays[i], 0, xarray, 0, nrow * sizes[i]); System.arraycopy(row[i], 0, xarray, nrow * sizes[i], sizes[i]); arrays[i] = xarray; } initializePointers(); nrow += 1; } } /** Get a element of the table. * @param row The row desired. * @param col The column desired. * @return A primitive array containing the information. Note * that an array will be returned even if the element * is a scalar. */ public Object getElement(int row, int col) { Object x = ArrayFuncs.newInstance(bases[col], sizes[col]); System.arraycopy(arrays[col], sizes[col] * row, x, 0, sizes[col]); return x; } /** Modify an element of the table. * @param row The row containing the element. * @param col The column containing the element. * @param x The new datum. This should be 1-d primitive * array. * @exception TableException Thrown when the new data * is not of the same type as * the data it replaces. */ public void setElement(int row, int col, Object x) throws TableException { String classname = x.getClass().getName(); if (!classname.equals("[" + types[col])) { throw new TableException("setElement: Incompatible element type"); } if (Array.getLength(x) != sizes[col]) { throw new TableException("setElement: Incompatible element size"); } System.arraycopy(x, 0, arrays[col], sizes[col] * row, sizes[col]); } /** Get a row of data. * @param row The row desired. * @return An array of objects each containing a primitive array. */ public Object getRow(int row) { Object[] x = new Object[arrays.length]; for (int col = 0; col < arrays.length; col += 1) { x[col] = getElement(row, col); } return x; } /** Modify a row of data. * @param row The row to be modified. * @param x The data to be modified. This should be an * array of objects. It is described as an Object * here since other table implementations may * use other methods to store the data (e.g., @see nom.tam.util.ColumnTable) */ public void setRow(int row, Object x) throws TableException { if (!(x instanceof Object[])) { throw new TableException("setRow: Incompatible row"); } for (int col = 0; col < arrays.length; col += 1) { setElement(row, col, ((Object[]) x)[col]); } } /** Check that the columns and sizes are consistent. * Inconsistencies include: *

* @param arrays The arrays defining the columns. * @param sizes The number of elements in each row for the column. */ protected void checkArrayConsistency(Object[] arrays, int[] sizes) throws TableException { // This routine throws an error if it detects an inconsistency // between the arrays being read in. // First check that the lengths of the two arrays are the same. if (arrays.length != sizes.length) { throw new TableException("readArraysAsColumns: Incompatible arrays and sizes."); } // Now check that that we fill up all of the arrays exactly. int ratio = 0; int rowSize = 0; this.types = new char[arrays.length]; this.bases = new Class[arrays.length]; // Check for a null table. boolean nullTable = true; for (int i = 0; i < arrays.length; i += 1) { String classname = arrays[i].getClass().getName(); ratio = checkColumnConsistency(arrays[i], classname, ratio, sizes[i]); rowSize += sizes[i] * ArrayFuncs.getBaseLength(arrays[i]); types[i] = classname.charAt(1); bases[i] = ArrayFuncs.getBaseClass(arrays[i]); } this.nrow = ratio; this.rowSize = rowSize; this.arrays = arrays; this.sizes = sizes; } private int checkColumnConsistency(Object data, String classname, int ratio, int size) throws TableException { if (classname.charAt(0) != '[' || classname.length() != 2) { throw new TableException("Non-primitive array for column"); } int thisSize = Array.getLength(data); if ((thisSize == 0 && size != 0 && ratio != 0) || (thisSize != 0 && size == 0)) { throw new TableException("Size mismatch in column: " + thisSize + " != " + size); } // The row size must evenly divide the size of the array. if (size != 0 && thisSize % size != 0) { throw new TableException("Row size does not divide array for column"); } // Finally the ratio of sizes must be the same for all columns -- this // is the number of rows in the table. int thisRatio = 0; if (size > 0) { thisRatio = thisSize / size; if (ratio != 0 && (thisRatio != ratio)) { throw new TableException("Different number of rows in different columns"); } } if (thisRatio > 0) { return thisRatio; } else { return ratio; } } /** Calculate the number of rows to read/write at a time. */ protected void getNumberOfRows() { int bufSize = 65536; // If a row is larger than bufSize, then read one row at a time. if (rowSize == 0) { this.chunk = 0; } else if (rowSize > bufSize) { this.chunk = 1; // If the entire set is not too big, just read it all. } else if (bufSize / rowSize >= nrow) { this.chunk = nrow; } else { this.chunk = bufSize / rowSize + 1; } } /** Set the pointer arrays for the eight primitive types * to point to the appropriate elements of arrays. */ protected void initializePointers() { int nbyte, nshort, nint, nlong, nfloat, ndouble, nchar, nboolean; // Count how many of each type we have. nbyte = 0; nshort = 0; nint = 0; nlong = 0; nfloat = 0; ndouble = 0; nchar = 0; nboolean = 0; for (int col = 0; col < arrays.length; col += 1) { switch (types[col]) { case 'B': nbyte += 1; break; case 'S': nshort += 1; break; case 'I': nint += 1; break; case 'J': nlong += 1; break; case 'F': nfloat += 1; break; case 'D': ndouble += 1; break; case 'C': nchar += 1; break; case 'Z': nboolean += 1; break; } } // Allocate the pointer arrays. Note that many will be // zero-length. bytePointers = new byte[nbyte][]; shortPointers = new short[nshort][]; intPointers = new int[nint][]; longPointers = new long[nlong][]; floatPointers = new float[nfloat][]; doublePointers = new double[ndouble][]; charPointers = new char[nchar][]; booleanPointers = new boolean[nboolean][]; // Now set the pointers. nbyte = 0; nshort = 0; nint = 0; nlong = 0; nfloat = 0; ndouble = 0; nchar = 0; nboolean = 0; for (int col = 0; col < arrays.length; col += 1) { switch (types[col]) { case 'B': bytePointers[nbyte] = (byte[]) arrays[col]; nbyte += 1; break; case 'S': shortPointers[nshort] = (short[]) arrays[col]; nshort += 1; break; case 'I': intPointers[nint] = (int[]) arrays[col]; nint += 1; break; case 'J': longPointers[nlong] = (long[]) arrays[col]; nlong += 1; break; case 'F': floatPointers[nfloat] = (float[]) arrays[col]; nfloat += 1; break; case 'D': doublePointers[ndouble] = (double[]) arrays[col]; ndouble += 1; break; case 'C': charPointers[nchar] = (char[]) arrays[col]; nchar += 1; break; case 'Z': booleanPointers[nboolean] = (boolean[]) arrays[col]; nboolean += 1; break; } } } // Add a pointer in the pointer lists. protected void addPointer(Object data) throws TableException { String classname = data.getClass().getName(); char type = classname.charAt(1); switch (type) { case 'B': { byte[][] xb = new byte[bytePointers.length + 1][]; System.arraycopy(bytePointers, 0, xb, 0, bytePointers.length); xb[bytePointers.length] = (byte[]) data; bytePointers = xb; break; } case 'Z': { boolean[][] xb = new boolean[booleanPointers.length + 1][]; System.arraycopy(booleanPointers, 0, xb, 0, booleanPointers.length); xb[booleanPointers.length] = (boolean[]) data; booleanPointers = xb; break; } case 'S': { short[][] xb = new short[shortPointers.length + 1][]; System.arraycopy(shortPointers, 0, xb, 0, shortPointers.length); xb[shortPointers.length] = (short[]) data; shortPointers = xb; break; } case 'C': { char[][] xb = new char[charPointers.length + 1][]; System.arraycopy(charPointers, 0, xb, 0, charPointers.length); xb[charPointers.length] = (char[]) data; charPointers = xb; break; } case 'I': { int[][] xb = new int[intPointers.length + 1][]; System.arraycopy(intPointers, 0, xb, 0, intPointers.length); xb[intPointers.length] = (int[]) data; intPointers = xb; break; } case 'J': { long[][] xb = new long[longPointers.length + 1][]; System.arraycopy(longPointers, 0, xb, 0, longPointers.length); xb[longPointers.length] = (long[]) data; longPointers = xb; break; } case 'F': { float[][] xb = new float[floatPointers.length + 1][]; System.arraycopy(floatPointers, 0, xb, 0, floatPointers.length); xb[floatPointers.length] = (float[]) data; floatPointers = xb; break; } case 'D': { double[][] xb = new double[doublePointers.length + 1][]; System.arraycopy(doublePointers, 0, xb, 0, doublePointers.length); xb[doublePointers.length] = (double[]) data; doublePointers = xb; break; } default: throw new TableException("Invalid type for added column:" + classname); } } /** Read a table. * @param is The input stream to read from. */ public int read(ArrayDataInput is) throws IOException { int currRow = 0; // While we have not finished reading the table.. for (int row = 0; row < nrow; row += 1) { int ibyte = 0; int ishort = 0; int iint = 0; int ilong = 0; int ichar = 0; int ifloat = 0; int idouble = 0; int iboolean = 0; // Loop over the columns within the row. for (int col = 0; col < arrays.length; col += 1) { int arrOffset = sizes[col] * row; int size = sizes[col]; switch (types[col]) { // In anticpated order of use. case 'I': int[] ia = intPointers[iint]; iint += 1; is.read(ia, arrOffset, size); break; case 'S': short[] s = shortPointers[ishort]; ishort += 1; is.read(s, arrOffset, size); break; case 'B': byte[] b = bytePointers[ibyte]; ibyte += 1; is.read(b, arrOffset, size); break; case 'F': float[] f = floatPointers[ifloat]; ifloat += 1; is.read(f, arrOffset, size); break; case 'D': double[] d = doublePointers[idouble]; idouble += 1; is.read(d, arrOffset, size); break; case 'C': char[] c = charPointers[ichar]; ichar += 1; is.read(c, arrOffset, size); break; case 'J': long[] l = longPointers[ilong]; ilong += 1; is.read(l, arrOffset, size); break; case 'Z': boolean[] bool = booleanPointers[iboolean]; iboolean += 1; is.read(bool, arrOffset, size); break; } } } // All done if we get here... return rowSize * nrow; } /** Write a table. * @param os the output stream to write to. */ public int write(ArrayDataOutput os) throws IOException { if (rowSize == 0) { return 0; } for (int row = 0; row < nrow; row += 1) { int ibyte = 0; int ishort = 0; int iint = 0; int ilong = 0; int ichar = 0; int ifloat = 0; int idouble = 0; int iboolean = 0; // Loop over the columns within the row. for (int col = 0; col < arrays.length; col += 1) { int arrOffset = sizes[col] * row; int size = sizes[col]; switch (types[col]) { // In anticpated order of use. case 'I': int[] ia = intPointers[iint]; iint += 1; os.write(ia, arrOffset, size); break; case 'S': short[] s = shortPointers[ishort]; ishort += 1; os.write(s, arrOffset, size); break; case 'B': byte[] b = bytePointers[ibyte]; ibyte += 1; os.write(b, arrOffset, size); break; case 'F': float[] f = floatPointers[ifloat]; ifloat += 1; os.write(f, arrOffset, size); break; case 'D': double[] d = doublePointers[idouble]; idouble += 1; os.write(d, arrOffset, size); break; case 'C': char[] c = charPointers[ichar]; ichar += 1; os.write(c, arrOffset, size); break; case 'J': long[] l = longPointers[ilong]; ilong += 1; os.write(l, arrOffset, size); break; case 'Z': boolean[] bool = booleanPointers[iboolean]; iboolean += 1; os.write(bool, arrOffset, size); break; } } } // All done if we get here... return rowSize * nrow; } /** Get the base classes of the columns. * @return An array of Class objects, one for each column. */ public Class[] getBases() { return bases; } /** Get the characters describing the base classes of the columns. * @return An array of char's, one for each column. */ public char[] getTypes() { return types; } /** Get the actual data arrays */ public Object[] getColumns() { return arrays; } public int[] getSizes() { return sizes; } /** Delete a row from the table. * @param row The row (0-indexed) to be deleted. */ public void deleteRow(int row) throws TableException { deleteRows(row, 1); } /** Delete a contiguous set of rows from the table. * @param row The row (0-indexed) to be deleted. * @param length The number of rows to be deleted. * @throws TableException if the request goes outside * the boundaries of the table or if the length is negative. */ public void deleteRows(int row, int length) throws TableException { if (row < 0 || length < 0 || row + length > nrow) { throw new TableException("Invalid request to delete rows start: " + row + " length:" + length + " for table with " + nrow + " rows."); } if (length == 0) { return; } for (int col = 0; col < arrays.length; col += 1) { int sz = sizes[col]; int newSize = sz * (nrow - length); Object newArr = ArrayFuncs.newInstance(bases[col], newSize); // Copy whatever comes before the deletion System.arraycopy(arrays[col], 0, newArr, 0, row * sz); // Copy whatever comes after the deletion System.arraycopy(arrays[col], (row + length) * sz, newArr, row * sz, (nrow - row - length) * sz); arrays[col] = newArr; } nrow -= length; initializePointers(); } /** Delete a contiguous set of columns from the table. * @param start The first column (0-indexed) to be deleted. * @param len The number of columns to be deleted. * @throws TableException if the request goes outside * the boundaries of the table or if the length is negative. */ public int deleteColumns(int start, int len) throws TableException { int ncol = arrays.length; if (start < 0 || len < 0 || start + len > ncol) { throw new TableException("Invalid request to delete columns start: " + start + " length:" + len + " for table with " + ncol + " columns."); } if (len == 0) { return rowSize; } for (int i = start; i < start + len; i += 1) { rowSize -= sizes[i] * ArrayFuncs.getBaseLength(arrays[i]); } int ocol = ncol; ncol -= len; Object[] newArrays = new Object[ncol]; int[] newSizes = new int[ncol]; Class[] newBases = new Class[ncol]; char[] newTypes = new char[ncol]; System.arraycopy(arrays, 0, newArrays, 0, start); System.arraycopy(sizes, 0, newSizes, 0, start); System.arraycopy(bases, 0, newBases, 0, start); System.arraycopy(types, 0, newTypes, 0, start); int rem = ocol - (start + len); System.arraycopy(arrays, start + len, newArrays, start, rem); System.arraycopy(sizes, start + len, newSizes, start, rem); System.arraycopy(bases, start + len, newBases, start, rem); System.arraycopy(types, start + len, newTypes, start, rem); arrays = newArrays; sizes = newSizes; bases = newBases; types = newTypes; initializePointers(); return rowSize; } } fits-1.10.0/nom/tam/util/ArrayDataInput.java0000664000175000017500000000602112031033644021021 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import java.io.IOException; public interface ArrayDataInput extends java.io.DataInput { /** Read a generic (possibly multidimensional) primitive array. * An Object[] array is also a legal argument if each element * of the array is a legal. *

* The ArrayDataInput classes do not support String input since * it is unclear how one would read in an Array of strings. * @param o A [multidimensional] primitive (or Object) array. * @deprecated See readLArray(Object o). */ public int readArray(Object o) throws IOException; /** Read an array. This version works even if the * underlying data is more than 2 Gigabytes. */ public long readLArray(Object o) throws IOException; /* Read a complete primitive array */ public int read(byte[] buf) throws IOException; public int read(boolean[] buf) throws IOException; public int read(short[] buf) throws IOException; public int read(char[] buf) throws IOException; public int read(int[] buf) throws IOException; public int read(long[] buf) throws IOException; public int read(float[] buf) throws IOException; public int read(double[] buf) throws IOException; /* Read a segment of a primitive array. */ public int read(byte[] buf, int offset, int size) throws IOException; public int read(boolean[] buf, int offset, int size) throws IOException; public int read(char[] buf, int offset, int size) throws IOException; public int read(short[] buf, int offset, int size) throws IOException; public int read(int[] buf, int offset, int size) throws IOException; public int read(long[] buf, int offset, int size) throws IOException; public int read(float[] buf, int offset, int size) throws IOException; public int read(double[] buf, int offset, int size) throws IOException; /* Skip (forward) in a file */ public long skip(long distance) throws IOException; /* Skip and require that the data be there. */ public long skipBytes(long toSkip) throws IOException; /* Close the file. */ public void close() throws IOException; } fits-1.10.0/nom/tam/util/AsciiFuncs.java0000664000175000017500000000404312031033644020162 0ustar frothmaifrothmai/* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /* * This class provides conversions to ASCII strings without breaking * compatibility with Java 1.5. */ package nom.tam.util; import java.io.UnsupportedEncodingException; import java.util.logging.Level; import java.util.logging.Logger; /** * * @author tmcglynn */ public class AsciiFuncs { public final static String ASCII = "US-ASCII"; /** Convert to ASCII or return null if not compatible */ public static String asciiString(byte[] buf) { return asciiString(buf, 0, buf.length); } /** Convert to ASCII or return null if not compatible */ public static String asciiString(byte[] buf, int start, int len) { try { return new String(buf, start, len, ASCII); } catch (java.io.UnsupportedEncodingException e) { // Shouldn't happen System.err.println("AsciiFuncs.asciiString error finding ASCII encoding"); return null; } } /** Convert an ASCII string to bytes */ public static byte[] getBytes(String in) { try { return in.getBytes(ASCII); } catch (UnsupportedEncodingException ex) { System.err.println("Unable to find ASCII encoding"); return null; } } } fits-1.10.0/nom/tam/util/test/0000775000175000017500000000000011566662570016270 5ustar frothmaifrothmaifits-1.10.0/nom/tam/util/test/ArrayFuncs2Test.java0000664000175000017500000001774012031031530022111 0ustar frothmaifrothmaipackage nom.tam.util.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; import nom.tam.util.ArrayFuncs; public class ArrayFuncs2Test { /** Test and demonstrate the ArrayFuncs methods. */ @Test public void test() { int[][][] test1 = new int[10][9][8]; boolean[][] test2 = new boolean[4][]; test2[0] = new boolean[5]; test2[1] = new boolean[4]; test2[2] = new boolean[3]; test2[3] = new boolean[2]; double[][] test3 = new double[10][20]; StringBuffer[][] test4 = new StringBuffer[3][2]; assertEquals("getBaseClass()", int.class, ArrayFuncs.getBaseClass(test1)); assertEquals("getBaseLength()", 4, ArrayFuncs.getBaseLength(test1)); assertEquals("computeSize()", 4 * 8 * 9 * 10, ArrayFuncs.computeSize(test1)); assertEquals("computeLSize()", 4 * 8 * 9 * 10L, ArrayFuncs.computeLSize(test1)); assertEquals("getBaseClass(boolean)", boolean.class, ArrayFuncs.getBaseClass(test2)); assertEquals("getBaseLength(boolean)", 1, ArrayFuncs.getBaseLength(test2)); assertEquals("computeSize() not rect", 1 * (2 + 3 + 4 + 5), ArrayFuncs.computeSize(test2)); assertEquals("computeLSize() not rect", 1L * (2 + 3 + 4 + 5), ArrayFuncs.computeLSize(test2)); assertEquals("getBaseClass(double)", double.class, ArrayFuncs.getBaseClass(test3)); assertEquals("getBaseLength(double)", 8, ArrayFuncs.getBaseLength(test3)); assertEquals("computeSize(double)", 8 * 10 * 20, ArrayFuncs.computeSize(test3)); assertEquals("computeLSize(double)", 8 * 10 * 20L, ArrayFuncs.computeLSize(test3)); assertEquals("getBaseClass(StrBuf)", StringBuffer.class, ArrayFuncs.getBaseClass(test4)); assertEquals("getBaseLength(StrBuf)", -1, ArrayFuncs.getBaseLength(test4)); assertEquals("computeSize(StrBuf)", 0, ArrayFuncs.computeSize(test4)); assertEquals("computeLSize(StrBuf)", 0L, ArrayFuncs.computeLSize(test4)); Object[] agg = new Object[4]; agg[0] = test1; agg[1] = test2; agg[2] = test3; agg[3] = test4; assertEquals("getBaseClass(Object[])", Object.class, ArrayFuncs.getBaseClass(agg)); assertEquals("getBaseLength(Object[])", -1, ArrayFuncs.getBaseLength(agg)); // Add up all the primitive arrays and ignore the objects. assertEquals("computeSize(Object[])", 2880 + 14 + 1600 + 0, ArrayFuncs.computeSize(agg)); assertEquals("computeLSize(Object[])", 2880L + 14 + 1600 + 0, ArrayFuncs.computeLSize(agg)); // Try allocating a very large array. This is likely to fail // in the allocation step, so don't consider that to be a failure. try { float[][] data = new float[10000][30000]; long expect = 10000L * 30000 * 4; assertEquals("computLSize(big)", ArrayFuncs.computeLSize(data), expect); } catch (Error e) { System.out.println("Unable to allocate large array. Test skipped"); } for (int i = 0; i < test1.length; i += 1) { for (int j = 0; j < test1[i].length; j += 1) { for (int k = 0; k < test1[i][j].length; k += 1) { test1[i][j][k] = i + j + k; } } } int[][][] test5 = (int[][][]) ArrayFuncs.deepClone(test1); assertEquals("deepClone()", true, ArrayFuncs.arrayEquals(test1, test5)); test5[1][1][1] = -3; assertEquals("arrayEquals()", false, ArrayFuncs.arrayEquals(test1, test5)); int[] dimsOrig = ArrayFuncs.getDimensions(test1); int[] test6 = (int[]) ArrayFuncs.flatten(test1); int[] dims = ArrayFuncs.getDimensions(test6); assertEquals("getDimensions()", 3, dimsOrig.length); assertEquals("getDimensions()", 10, dimsOrig[0]); assertEquals("getDimensions()", 9, dimsOrig[1]); assertEquals("getDimensions()", 8, dimsOrig[2]); assertEquals("flatten()", 1, dims.length); int[] newdims = {8, 9, 10}; int[][][] test7 = (int[][][]) ArrayFuncs.curl(test6, newdims); int[] dimsAfter = ArrayFuncs.getDimensions(test7); assertEquals("curl()", 3, dimsAfter.length); assertEquals("getDimensions()", 8, dimsAfter[0]); assertEquals("getDimensions()", 9, dimsAfter[1]); assertEquals("getDimensions()", 10, dimsAfter[2]); byte[][][] xtest1 = (byte[][][]) ArrayFuncs.convertArray(test1, byte.class); assertEquals("convertArray(toByte)", byte.class, ArrayFuncs.getBaseClass(xtest1)); assertEquals("convertArray(tobyte)", test1[3][3][3], (int) xtest1[3][3][3]); double[][][] xtest2 = (double[][][]) ArrayFuncs.convertArray(test1, double.class); assertEquals("convertArray(toByte)", double.class, ArrayFuncs.getBaseClass(xtest2)); assertEquals("convertArray(tobyte)", test1[3][3][3], (int) xtest2[3][3][3]); int[] xtest3 = (int[]) ArrayFuncs.newInstance(int.class, 20); int[] xtd = ArrayFuncs.getDimensions(xtest3); assertEquals("newInstance(vector)", 1, xtd.length); assertEquals("newInstance(vector)", 20, xtd[0]); double[][][][] xtest4 = (double[][][][]) ArrayFuncs.newInstance(double.class, new int[]{5, 4, 3, 2}); xtd = ArrayFuncs.getDimensions(xtest4); assertEquals("newInstance(tensor)", 4, xtd.length); assertEquals("newInstance(tensor)", 5, xtd[0]); assertEquals("newInstance(tensor)", 4, xtd[1]); assertEquals("newInstance(tensor)", 3, xtd[2]); assertEquals("newInstance(tensor)", 2, xtd[3]); assertEquals("nElements()", 120, ArrayFuncs.nElements(xtest4)); assertEquals("nLElements()", 120L, ArrayFuncs.nLElements(xtest4)); ArrayFuncs.testPattern(xtest4, (byte) -1); assertEquals("testPattern()", (double) -1, xtest4[0][0][0][0], 0); assertEquals("testPattern()", (double) 118, xtest4[4][3][2][1], 0); double[] xtest4x = (double[]) ArrayFuncs.getBaseArray(xtest4); assertEquals("getBaseArray()", 2, xtest4x.length); double[] x = {1, 2, 3, 4, 5}; double[] y = new double[x.length]; for (int i = 0; i < y.length; i += 1) { y[i] = x[i] + 1.E-10; } assertEquals("eqTest", false, ArrayFuncs.arrayEquals(x, y)); assertEquals("eqTest2", true, ArrayFuncs.arrayEquals(x, y, 0., 1.e-9)); assertEquals("eqTest3", true, ArrayFuncs.arrayEquals(x, y, 1.E-5, 1.e-9)); assertEquals("eqTest4", false, ArrayFuncs.arrayEquals(x, y, 0., 1.e-11)); assertEquals("eqTest5", false, ArrayFuncs.arrayEquals(x, y, 1.E-5, 0.)); float[] fx = {1, 2, 3, 4, 5}; float[] fy = new float[fx.length]; for (int i = 0; i < fy.length; i += 1) { fy[i] = fx[i] + 1.E-5F; } assertEquals("eqTest6", false, ArrayFuncs.arrayEquals(fx, fy)); assertEquals("eqTest7", true, ArrayFuncs.arrayEquals(fx, fy, 1.E-4, 0.)); assertEquals("eqTest8", false, ArrayFuncs.arrayEquals(fx, fy, 1.E-6, 0.)); assertEquals("eqTest9", false, ArrayFuncs.arrayEquals(fx, fy, 0., 0.)); assertEquals("eqTest10", false, ArrayFuncs.arrayEquals(fx, fy, 0., 1.E-4)); } } fits-1.10.0/nom/tam/util/test/BufferedFileTester.java0000664000175000017500000007655212031031530022631 0ustar frothmaifrothmai/* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ package nom.tam.util.test; import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; import nom.tam.util.*; import nom.tam.util.ArrayFuncs; import java.io.*; /** This class provides runs tests of the * BufferedI/O classes: BufferedFile, BufferedDataInputStream * and BufferedDataOutputStream. A limited comparison * to the standard I/O classes can also be made. *

* Input and output of all primitive scalar and array types is * tested, however input and output of String data is not. * Users may choose to test the BufferedFile class, the * BufferedDataXPUT classes array methods, the BufferedDataXPUT * classes using the methods of DataXput, the traditional * I/O classes, or any combination thereof. */ public class BufferedFileTester { /** Usage: java nom.tam.util.test.BufferedFileTester file [dim [iter [flags]]] * where * file is the file to be read and written. * dim is the dimension of the arrays to be written. * iter is the number of times each array is written. * flags a string indicating what I/O to test * O -- test old I/O (RandomAccessFile and standard streams) * R -- BufferedFile (i.e., random access) * S -- BufferedDataXPutStream * X -- BufferedDataXPutStream using standard methods */ public static void main(String[] args) throws Exception { String filename = args[0]; int dim = 1000; if (args.length > 1) { dim = Integer.parseInt(args[1]); } int iter = 1; if (args.length > 2) { iter = Integer.parseInt(args[2]); } System.out.println("Allocating arrays."); double[] db = new double[dim]; float[] fl = new float[dim]; int[] in = new int[dim]; long[] ln = new long[dim]; short[] sh = new short[dim]; byte[] by = new byte[dim]; char[] ch = new char[dim]; boolean[] bl = new boolean[dim]; System.out.println("Initializing arrays -- may take a while"); int sign = 1; for (int i = 0; i < dim; i += 1) { double x = sign * Math.pow(10., 20 * Math.random() - 10); db[i] = x; fl[i] = (float) x; if (Math.abs(x) < 1) { x = 1 / x; } in[i] = (int) x; ln[i] = (long) x; sh[i] = (short) x; by[i] = (byte) x; ch[i] = (char) x; bl[i] = x > 0; sign = -sign; } // Ensure special values are tested. by[0] = Byte.MIN_VALUE; by[1] = Byte.MAX_VALUE; by[2] = 0; ch[0] = Character.MIN_VALUE; ch[1] = Character.MAX_VALUE; ch[2] = 0; sh[0] = Short.MAX_VALUE; sh[1] = Short.MIN_VALUE; sh[0] = 0; in[0] = Integer.MAX_VALUE; in[1] = Integer.MIN_VALUE; in[2] = 0; ln[0] = Long.MIN_VALUE; ln[1] = Long.MAX_VALUE; ln[2] = 0; fl[0] = Float.MIN_VALUE; fl[1] = Float.MAX_VALUE; fl[2] = Float.POSITIVE_INFINITY; fl[3] = Float.NEGATIVE_INFINITY; fl[4] = Float.NaN; fl[5] = 0; db[0] = Double.MIN_VALUE; db[1] = Double.MAX_VALUE; db[2] = Double.POSITIVE_INFINITY; db[3] = Double.NEGATIVE_INFINITY; db[4] = Double.NaN; db[5] = 0; double[] db2 = new double[dim]; float[] fl2 = new float[dim]; int[] in2 = new int[dim]; long[] ln2 = new long[dim]; short[] sh2 = new short[dim]; byte[] by2 = new byte[dim]; char[] ch2 = new char[dim]; boolean[] bl2 = new boolean[dim]; int[][][][] multi = new int[10][10][10][10]; int[][][][] multi2 = new int[10][10][10][10]; for (int i = 0; i < 10; i += 1) { multi[i][i][i][i] = i; } if (args.length < 4 || args[3].indexOf('O') >= 0) { standardFileTest(filename, iter, in, in2); standardStreamTest(filename, iter, in, in2); } if (args.length < 4 || args[3].indexOf('X') >= 0) { buffStreamSimpleTest(filename, iter, in, in2); } if (args.length < 4 || args[3].indexOf('R') >= 0) { bufferedFileTest(filename, iter, db, db2, fl, fl2, ln, ln2, in, in2, sh, sh2, ch, ch2, by, by2, bl, bl2, multi, multi2); } if (args.length < 4 || args[3].indexOf('S') >= 0) { bufferedStreamTest(filename, iter, db, db2, fl, fl2, ln, ln2, in, in2, sh, sh2, ch, ch2, by, by2, bl, bl2, multi, multi2); } } public static void standardFileTest(String filename, int iter, int[] in, int[] in2) throws Exception { System.out.println("Standard I/O library: java.io.RandomAccessFile"); RandomAccessFile f = new RandomAccessFile(filename, "rw"); int dim = in.length; resetTime(); f.seek(0); for (int j = 0; j < iter; j += 1) { for (int i = 0; i < dim; i += 1) { f.writeInt(in[i]); } } System.out.println(" RAF Int write: " + (4 * dim * iter) / (1000 * deltaTime())); f.seek(0); resetTime(); for (int j = 0; j < iter; j += 1) { for (int i = 0; i < dim; i += 1) { in2[i] = f.readInt(); } } System.out.println(" RAF Int read: " + (4 * dim * iter) / (1000 * deltaTime())); synchronized (f) { f.seek(0); for (int j = 0; j < iter; j += 1) { for (int i = 0; i < dim; i += 1) { f.writeInt(in[i]); } } System.out.println(" SyncRAF Int write: " + (4 * dim * iter) / (1000 * deltaTime())); f.seek(0); resetTime(); for (int j = 0; j < iter; j += 1) { for (int i = 0; i < dim; i += 1) { in2[i] = f.readInt(); } } } System.out.println(" SyncRAF Int read: " + (4 * dim * iter) / (1000 * deltaTime())); } public static void standardStreamTest(String filename, int iter, int[] in, int[] in2) throws Exception { System.out.println("Standard I/O library: java.io.DataXXputStream"); System.out.println(" layered atop a BufferedXXputStream"); DataOutputStream f = new DataOutputStream(new BufferedOutputStream( new FileOutputStream(filename), 32768)); resetTime(); int dim = in.length; for (int j = 0; j < iter; j += 1) { for (int i = 0; i < dim; i += 1) { f.writeInt(in[i]); } } f.flush(); f.close(); System.out.println(" DIS Int write: " + (4 * dim * iter) / (1000 * deltaTime())); DataInputStream is = new DataInputStream(new BufferedInputStream( new FileInputStream(filename), 32768)); resetTime(); for (int j = 0; j < iter; j += 1) { for (int i = 0; i < dim; i += 1) { in2[i] = is.readInt(); } } System.out.println(" DIS Int read: " + (4 * dim * iter) / (1000 * deltaTime())); f = new DataOutputStream(new BufferedOutputStream( new FileOutputStream(filename), 32768)); resetTime(); dim = in.length; synchronized (f) { for (int j = 0; j < iter; j += 1) { for (int i = 0; i < dim; i += 1) { f.writeInt(in[i]); } } f.flush(); f.close(); System.out.println(" DIS Int write: " + (4 * dim * iter) / (1000 * deltaTime())); is = new DataInputStream(new BufferedInputStream( new FileInputStream(filename), 32768)); resetTime(); for (int j = 0; j < iter; j += 1) { for (int i = 0; i < dim; i += 1) { in2[i] = is.readInt(); } } } System.out.println(" DIS Int read: " + (4 * dim * iter) / (1000 * deltaTime())); } public static void buffStreamSimpleTest(String filename, int iter, int[] in, int[] in2) throws Exception { System.out.println("New libraries: nom.tam.BufferedDataXXputStream"); System.out.println(" Using non-array I/O"); BufferedDataOutputStream f = new BufferedDataOutputStream( new FileOutputStream(filename), 32768); resetTime(); int dim = in.length; for (int j = 0; j < iter; j += 1) { for (int i = 0; i < dim; i += 1) { f.writeInt(in[i]); } } f.flush(); f.close(); System.out.println(" BDS Int write: " + (4 * dim * iter) / (1000 * deltaTime())); BufferedDataInputStream is = new BufferedDataInputStream(new BufferedInputStream( new FileInputStream(filename), 32768)); resetTime(); for (int j = 0; j < iter; j += 1) { for (int i = 0; i < dim; i += 1) { in2[i] = is.readInt(); } } System.out.println(" BDS Int read: " + (4 * dim * iter) / (1000 * deltaTime())); } public static void bufferedStreamTest(String filename, int iter, double[] db, double[] db2, float[] fl, float[] fl2, long[] ln, long[] ln2, int[] in, int[] in2, short[] sh, short[] sh2, char[] ch, char[] ch2, byte[] by, byte[] by2, boolean[] bl, boolean[] bl2, int[][][][] multi, int[][][][] multi2) throws Exception { int dim = db.length; double ds = Math.random() - 0.5; double ds2; float fs = (float) (Math.random() - 0.5); float fs2; int is = (int) (1000000 * (Math.random() - 500000)); int is2; long ls = (long) (100000000000L * (Math.random() - 50000000000L)); long ls2; short ss = (short) (60000 * (Math.random() - 30000)); short ss2; char cs = (char) (60000 * Math.random()); char cs2; byte bs = (byte) (256 * Math.random() - 128); byte bs2; boolean bls = (Math.random() > 0.5); boolean bls2; System.out.println("New libraries: nom.tam.util.BufferedDataXXputStream"); System.out.println(" Using array I/O methods"); { BufferedDataOutputStream f = new BufferedDataOutputStream(new FileOutputStream(filename)); resetTime(); for (int i = 0; i < iter; i += 1) { f.writeArray(db); } System.out.println(" BDS Dbl write: " + (8 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray(fl); } System.out.println(" BDS Flt write: " + (4 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray(in); } System.out.println(" BDS Int write: " + (4 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray(ln); } System.out.println(" BDS Lng write: " + (8 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray(sh); } System.out.println(" BDS Sht write: " + (2 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray(ch); } System.out.println(" BDS Chr write: " + (2 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray((byte[]) by); } System.out.println(" BDS Byt write: " + (1 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray(bl); } System.out.println(" BDS Boo write: " + (1 * dim * iter) / (1000 * deltaTime())); f.writeByte(bs); f.writeChar(cs); f.writeShort(ss); f.writeInt(is); f.writeLong(ls); f.writeFloat(fs); f.writeDouble(ds); f.writeBoolean(bls); f.writeArray(multi); f.flush(); f.close(); } { BufferedDataInputStream f = new BufferedDataInputStream(new FileInputStream(filename)); resetTime(); for (int i = 0; i < iter; i += 1) { f.readLArray(db2); } System.out.println(" BDS Dbl read: " + (8 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray(fl2); } System.out.println(" BDS Flt read: " + (4 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray(in2); } System.out.println(" BDS Int read: " + (4 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray(ln2); } System.out.println(" BDS Lng read: " + (8 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray(sh2); } System.out.println(" BDS Sht read: " + (2 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray(ch2); } System.out.println(" BDS Chr read: " + (2 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray((byte[]) by2); } System.out.println(" BDS Byt read: " + (1 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray(bl2); } System.out.println(" BDS Boo read: " + (1 * dim * iter) / (1000 * deltaTime())); bs2 = f.readByte(); cs2 = f.readChar(); ss2 = f.readShort(); is2 = f.readInt(); ls2 = f.readLong(); fs2 = f.readFloat(); ds2 = f.readDouble(); bls2 = f.readBoolean(); for (int i = 0; i < 10; i += 1) { multi2[i][i][i][i] = 0; } // Now read only pieces of the multidimensional array. for (int i = 0; i < 5; i += 1) { System.out.println("Multiread:" + i); // Skip the odd initial indices and // read the evens. f.skipBytes(4000); f.readLArray(multi2[2 * i + 1]); } f.close(); } System.out.println("Stream Verification:"); System.out.println(" An error should be reported for double and float NaN's"); System.out.println(" Arrays:"); for (int i = 0; i < dim; i += 1) { if (db[i] != db2[i]) { System.out.println(" Double error at " + i + " " + db[i] + " " + db2[i]); } if (fl[i] != fl2[i]) { System.out.println(" Float error at " + i + " " + fl[i] + " " + fl2[i]); } if (in[i] != in2[i]) { System.out.println(" Int error at " + i + " " + in[i] + " " + in2[i]); } if (ln[i] != ln2[i]) { System.out.println(" Long error at " + i + " " + ln[i] + " " + ln2[i]); } if (sh[i] != sh2[i]) { System.out.println(" Short error at " + i + " " + sh[i] + " " + sh2[i]); } if (ch[i] != ch2[i]) { System.out.println(" Char error at " + i + " " + (int) ch[i] + " " + (int) ch2[i]); } if (by[i] != by2[i]) { System.out.println(" Byte error at " + i + " " + by[i] + " " + by2[i]); } if (bl[i] != bl2[i]) { System.out.println(" Bool error at " + i + " " + bl[i] + " " + bl2[i]); } } System.out.println(" Scalars:"); // Check the scalars. if (bls != bls2) { System.out.println(" Bool Scalar mismatch:" + bls + " " + bls2); } if (bs != bs2) { System.out.println(" Byte Scalar mismatch:" + bs + " " + bs2); } if (cs != cs2) { System.out.println(" Char Scalar mismatch:" + (int) cs + " " + (int) cs2); } if (ss != ss2) { System.out.println(" Short Scalar mismatch:" + ss + " " + ss2); } if (is != is2) { System.out.println(" Int Scalar mismatch:" + is + " " + is2); } if (ls != ls2) { System.out.println(" Long Scalar mismatch:" + ls + " " + ls2); } if (fs != fs2) { System.out.println(" Float Scalar mismatch:" + fs + " " + fs2); } if (ds != ds2) { System.out.println(" Double Scalar mismatch:" + ds + " " + ds2); } System.out.println(" Multi: odd rows should match"); for (int i = 0; i < 10; i += 1) { System.out.println(" " + i + " " + multi[i][i][i][i] + " " + multi2[i][i][i][i]); } System.out.println("Done BufferedStream Tests"); } public static void bufferedFileTest(String filename, int iter, double[] db, double[] db2, float[] fl, float[] fl2, long[] ln, long[] ln2, int[] in, int[] in2, short[] sh, short[] sh2, char[] ch, char[] ch2, byte[] by, byte[] by2, boolean[] bl, boolean[] bl2, int[][][][] multi, int[][][][] multi2) throws Exception { int dim = db.length; double ds = Math.random() - 0.5; double ds2; float fs = (float) (Math.random() - 0.5); float fs2; int is = (int) (1000000 * (Math.random() - 500000)); int is2; long ls = (long) (100000000000L * (Math.random() - 50000000000L)); long ls2; short ss = (short) (60000 * (Math.random() - 30000)); short ss2; char cs = (char) (60000 * Math.random()); char cs2; byte bs = (byte) (256 * Math.random() - 128); byte bs2; boolean bls = (Math.random() > 0.5); boolean bls2; System.out.println("New libraries: nom.tam.util.BufferedFile"); System.out.println(" Using array I/O methods."); BufferedFile f = new BufferedFile(filename, "rw"); resetTime(); for (int i = 0; i < iter; i += 1) { f.writeArray(db); } System.out.println(" BF Dbl write: " + (8 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray(fl); } System.out.println(" BF Flt write: " + (4 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray(in); } System.out.println(" BF Int write: " + (4 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray(ln); } System.out.println(" BF Lng write: " + (8 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray(sh); } System.out.println(" BF Sht write: " + (2 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray(ch); } System.out.println(" BF Chr write: " + (2 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray(by); } System.out.println(" BF Byt write: " + (1 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.writeArray(bl); } System.out.println(" BF Boo write: " + (1 * dim * iter) / (1000 * deltaTime())); f.writeByte(bs); f.writeChar(cs); f.writeShort(ss); f.writeInt(is); f.writeLong(ls); f.writeFloat(fs); f.writeDouble(ds); f.writeBoolean(bls); f.writeArray(multi); f.seek(0); resetTime(); for (int i = 0; i < iter; i += 1) { f.readLArray(db2); } System.out.println(" BF Dbl read: " + (8 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray(fl2); } System.out.println(" BF Flt read: " + (4 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray(in2); } System.out.println(" BF Int read: " + (4 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray(ln2); } System.out.println(" BF Lng read: " + (8 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray(sh2); } System.out.println(" BF Sht read: " + (2 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray(ch2); } System.out.println(" BF Chr read: " + (2 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray(by2); } System.out.println(" BF Byt read: " + (1 * dim * iter) / (1000 * deltaTime())); for (int i = 0; i < iter; i += 1) { f.readLArray(bl2); } System.out.println(" BF Boo read: " + (1 * dim * iter) / (1000 * deltaTime())); bs2 = f.readByte(); cs2 = f.readChar(); ss2 = f.readShort(); is2 = f.readInt(); ls2 = f.readLong(); fs2 = f.readFloat(); ds2 = f.readDouble(); bls2 = f.readBoolean(); // Now read only pieces of the multidimensional array. for (int i = 0; i < 5; i += 1) { // Skip the odd initial indices and // read the evens. f.skipBytes(4000); f.readLArray(multi2[2 * i + 1]); } System.out.println("BufferedFile Verification:"); System.out.println(" An error should be reported for double and float NaN's"); System.out.println(" Arrays:"); for (int i = 0; i < dim; i += 1) { if (db[i] != db2[i]) { System.out.println(" Double error at " + i + " " + db[i] + " " + db2[i]); } if (fl[i] != fl2[i]) { System.out.println(" Float error at " + i + " " + fl[i] + " " + fl2[i]); } if (in[i] != in2[i]) { System.out.println(" Int error at " + i + " " + in[i] + " " + in2[i]); } if (ln[i] != ln2[i]) { System.out.println(" Long error at " + i + " " + ln[i] + " " + ln2[i]); } if (sh[i] != sh2[i]) { System.out.println(" Short error at " + i + " " + sh[i] + " " + sh2[i]); } if (ch[i] != ch2[i]) { System.out.println(" Char error at " + i + " " + (int) ch[i] + " " + (int) ch2[i]); } if (by[i] != by2[i]) { System.out.println(" Byte error at " + i + " " + by[i] + " " + by2[i]); } if (bl[i] != bl2[i]) { System.out.println(" Bool error at " + i + " " + bl[i] + " " + bl2[i]); } } System.out.println(" Scalars:"); // Check the scalars. if (bls != bls2) { System.out.println(" Bool Scalar mismatch:" + bls + " " + bls2); } if (bs != bs2) { System.out.println(" Byte Scalar mismatch:" + bs + " " + bs2); } if (cs != cs2) { System.out.println(" Char Scalar mismatch:" + (int) cs + " " + (int) cs2); } if (ss != ss2) { System.out.println(" Short Scalar mismatch:" + ss + " " + ss2); } if (is != is2) { System.out.println(" Int Scalar mismatch:" + is + " " + is2); } if (ls != ls2) { System.out.println(" Long Scalar mismatch:" + ls + " " + ls2); } if (fs != fs2) { System.out.println(" Float Scalar mismatch:" + fs + " " + fs2); } if (ds != ds2) { System.out.println(" Double Scalar mismatch:" + ds + " " + ds2); } System.out.println(" Multi: odd rows should match"); for (int i = 0; i < 10; i += 1) { System.out.println(" " + i + " " + multi[i][i][i][i] + " " + multi2[i][i][i][i]); } System.out.println("Done BufferedFile Tests"); } static long lastTime; static void resetTime() { lastTime = new java.util.Date().getTime(); } static double deltaTime() { long time = lastTime; lastTime = new java.util.Date().getTime(); return (lastTime - time) / 1000.; } @Test public void testBufferedFile() throws Exception { double[][] td = new double[100][600]; for (int i = 0; i < 100; i += 1) { for (int j = 0; j < 600; j += 1) { td[i][j] = i + 2 * j; } } int[][][] ti = new int[5][4][3]; for (int i = 0; i < 5; i += 1) { for (int j = 0; j < 4; j += 1) { for (int k = 0; k < 3; k += 1) { ti[i][j][k] = i * j * k; } } } float[][] tf = new float[10][]; for (int i = 0; i < 10; i += 1) { tf[i] = new float[i]; for (int j = 0; j < i; j += 1) { tf[i][j] = (float) Math.sin(i * j); } } boolean[] tb = new boolean[100]; for (int i = 2; i < 100; i += 1) { tb[i] = !tb[i - 1]; } short[][] ts = new short[5][5]; ts[2][2] = 222; byte[] tbyte = new byte[1024]; for (int i = 0; i < tbyte.length; i += 1) { tbyte[i] = (byte) i; } char[] tc = new char[10]; tc[3] = 'c'; long[][][] tl0 = new long[1][1][1]; long[][][] tl1 = new long[1][1][0]; BufferedFile bf = new BufferedFile("jtest.fil", "rw"); bf.writeArray(td); bf.writeArray(tf); bf.writeArray(ti); bf.writeArray(ts); bf.writeArray(tb); bf.writeArray(tbyte); bf.writeArray(tc); bf.writeArray(tl0); bf.writeArray(tl1); bf.writeArray(ts); bf.close(); bf = new BufferedFile("jtest.fil", "r"); boolean thrown = false; try { bf.writeArray(td); } catch (Exception e) { thrown = true; } assertEquals("BufferedFile protections", true, thrown); try { bf.close(); } catch (Exception e) { } bf = new BufferedFile("jtest.fil", "r"); testArray(bf, "double", td); testArray(bf, "float", tf); testArray(bf, "int", ti); testArray(bf, "short", ts); testArray(bf, "bool", tb); testArray(bf, "byte", tbyte); testArray(bf, "char", tc); testArray(bf, "long1", tl0); testArray(bf, "longnull", tl1); testArray(bf, "short2", ts); } @Test public void testBufferedStreams() throws Exception { double[][] td = new double[100][600]; for (int i = 0; i < 100; i += 1) { for (int j = 0; j < 600; j += 1) { td[i][j] = i + 2 * j; } } int[][][] ti = new int[5][4][3]; for (int i = 0; i < 5; i += 1) { for (int j = 0; j < 4; j += 1) { for (int k = 0; k < 3; k += 1) { ti[i][j][k] = i * j * k; } } } float[][] tf = new float[10][]; for (int i = 0; i < 10; i += 1) { tf[i] = new float[i]; for (int j = 0; j < i; j += 1) { tf[i][j] = (float) Math.sin(i * j); } } boolean[] tb = new boolean[100]; for (int i = 2; i < 100; i += 1) { tb[i] = !tb[i - 1]; } short[][] ts = new short[5][5]; ts[2][2] = 222; byte[] tbyte = new byte[1024]; for (int i = 0; i < tbyte.length; i += 1) { tbyte[i] = (byte) i; } char[] tc = new char[10]; tc[3] = 'c'; long[][][] tl0 = new long[1][1][1]; long[][][] tl1 = new long[1][1][0]; BufferedDataOutputStream bf = new BufferedDataOutputStream( new FileOutputStream("jtest.fil")); bf.writeArray(td); bf.writeArray(tf); bf.writeArray(ti); bf.writeArray(ts); bf.writeArray(tb); bf.writeArray(tbyte); bf.writeArray(tc); bf.writeArray(tl0); bf.writeArray(tl1); bf.writeArray(ts); bf.close(); BufferedDataInputStream bi = new BufferedDataInputStream( new FileInputStream("jtest.fil")); testArray(bi, "sdouble", td); testArray(bi, "sfloat", tf); testArray(bi, "sint", ti); testArray(bi, "sshort", ts); testArray(bi, "sbool", tb); testArray(bi, "sbyte", tbyte); testArray(bi, "schar", tc); testArray(bi, "slong1", tl0); testArray(bi, "slongnull", tl1); testArray(bi, "sshort2", ts); } void testArray(ArrayDataInput bf, String label, Object array) throws Exception { Object newArray = ArrayFuncs.mimicArray(array, ArrayFuncs.getBaseClass(array)); bf.readLArray(newArray); boolean state = ArrayFuncs.arrayEquals(array, newArray); assertEquals(label, true, state); } } fits-1.10.0/nom/tam/util/test/ArrayFuncsTest.java0000664000175000017500000003027312031031530022023 0ustar frothmaifrothmai/* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /* * ArrayFuncsTest.java * JUnit based test * * Created on December 2, 2007, 7:19 PM */ package nom.tam.util.test; import junit.framework.*; import java.lang.reflect.*; import java.util.Arrays; import nom.tam.util.ArrayFuncs; /** * * @author Thomas McGlynn */ public class ArrayFuncsTest extends TestCase { public ArrayFuncsTest(String testName) { super(testName); } protected void setUp() throws Exception { } protected void tearDown() throws Exception { } /** * Test of computeSize method, of class nom.tam.util.ArrayFuncs. */ public void testComputeSize() { System.out.println("computeSize"); Object o = null; int expResult = 0; int result = ArrayFuncs.computeSize(o); assertEquals(expResult, result); int[][] x = new int[2][3]; assertEquals(ArrayFuncs.computeSize(x), 24); assertEquals(ArrayFuncs.computeSize(new double[3]), 24); assertEquals(ArrayFuncs.computeSize("1234"), 4); assertEquals(ArrayFuncs.computeSize(new Object()), 0); assertEquals(ArrayFuncs.computeSize(new Double[5]), 0); assertEquals(ArrayFuncs.computeSize(new Double[]{ new Double(0), new Double(1), new Double(2)}), 24); assertEquals(ArrayFuncs.computeLSize(x), 24); assertEquals(ArrayFuncs.computeLSize(new double[3]), 24); assertEquals(ArrayFuncs.computeLSize("1234"), 4); assertEquals(ArrayFuncs.computeLSize(new Object()), 0); assertEquals(ArrayFuncs.computeLSize(new Double[5]), 0); assertEquals(ArrayFuncs.computeLSize(new Double[]{ new Double(0), new Double(1), new Double(2)}), 24); } /** * Test of nElements method, of class nom.tam.util.ArrayFuncs. */ public void testNElements() { System.out.println("nElements"); Object o = null; assertEquals(ArrayFuncs.nElements(null), 0); assertEquals(ArrayFuncs.nElements(new int[2][2][3]), 12); assertEquals(ArrayFuncs.nLElements(null), 0); assertEquals(ArrayFuncs.nLElements(new int[2][2][3]), 12); } /** * Test of deepClone method, of class nom.tam.util.ArrayFuncs. */ public void testDeepClone() { int[][] test = {{0, 1}, {2, 3}, {4, 5}}; int[][] result = (int[][]) nom.tam.util.ArrayFuncs.deepClone(test); for (int i = 0; i < test.length; i += 1) { for (int j = 0; j < test[i].length; j += 1) { assertEquals(test[i][j], result[i][j]); } } } public class CloneTest implements Cloneable { public int value = 2; public Object clone() { try { return super.clone(); } catch (Exception e) { } return null; } public boolean equals(Object x) { return (x instanceof CloneTest) && (((CloneTest) x).value == this.value); } } /** * Test of genericClone method, of class nom.tam.util.ArrayFuncs. */ public void testGenericClone() { System.out.println("genericClone"); Object o = new int[]{1, 2, 3}; Object result = nom.tam.util.ArrayFuncs.genericClone(o); int[] x = (int[]) o; int[] y = (int[]) result; for (int i = 0; i < x.length; i += 1) { assertEquals(x[i], y[i]); } CloneTest xa = new CloneTest(); xa.value = 4; Object ya = ArrayFuncs.genericClone(xa); assertTrue(xa != ya); assertTrue(xa.equals(ya)); } /** * Test of copyArray method, of class nom.tam.util.ArrayFuncs. */ public void testCopyArray() { System.out.println("copyArray"); double[] start = new double[]{1, 2, 3, 4, 5, 6}; double[] finish = new double[6]; ArrayFuncs.copyArray(start, finish); assertTrue(ArrayFuncs.arrayEquals(start, finish)); } /** * Test of getDimensions method, of class nom.tam.util.ArrayFuncs. */ public void testGetDimensions() { System.out.println("getDimensions"); Object o = null; int[] expResult = null; int[] result = nom.tam.util.ArrayFuncs.getDimensions(o); assertEquals(expResult, result); assertEquals(ArrayFuncs.getDimensions(new Integer(0)).length, 0); int[][] test = new int[2][3]; int[] dims = ArrayFuncs.getDimensions(test); assertEquals(dims.length, 2); assertEquals(dims[0], 2); assertEquals(dims[1], 3); } /** * Test of getBaseArray method, of class nom.tam.util.ArrayFuncs. */ public void testGetBaseArray() { int[][][] test = new int[2][3][4]; byte b = 0; ArrayFuncs.testPattern(test, b); assertEquals(ArrayFuncs.getBaseArray(test), test[0][0]); } /** * Test of getBaseClass method, of class nom.tam.util.ArrayFuncs. */ public void testGetBaseClass() { System.out.println("getBaseClass"); assertEquals(ArrayFuncs.getBaseClass(new int[2][3]), int.class); assertEquals(ArrayFuncs.getBaseClass(new String[3]), String.class); } /** * Test of getBaseLength method, of class nom.tam.util.ArrayFuncs. */ public void testGetBaseLength() { assertEquals(ArrayFuncs.getBaseLength(new int[2][3]), 4); assertEquals(ArrayFuncs.getBaseLength(new double[2][3]), 8); assertEquals(ArrayFuncs.getBaseLength(new byte[2][3]), 1); assertEquals(ArrayFuncs.getBaseLength(new short[2][3]), 2); assertEquals(ArrayFuncs.getBaseLength(new int[2][3]), 4); assertEquals(ArrayFuncs.getBaseLength(new char[2][3]), 2); assertEquals(ArrayFuncs.getBaseLength(new float[2][3]), 4); assertEquals(ArrayFuncs.getBaseLength(new boolean[2][3]), 1); assertEquals(ArrayFuncs.getBaseLength(new Object[2][3]), -1); } /** * Test of generateArray method, of class nom.tam.util.ArrayFuncs. */ public void testGenerateArray() { System.out.println("generateArray"); Class baseType = int.class; int[] dims = {2, 3, 4}; Object result = nom.tam.util.ArrayFuncs.generateArray(baseType, dims); assertEquals(result.getClass(), int[][][].class); int[][][] x = (int[][][]) result; assertEquals(x.length, 2); assertEquals(x[0].length, 3); assertEquals(x[0][0].length, 4); } /** * Test of testPattern method, of class nom.tam.util.ArrayFuncs. */ public void testTestPattern() { System.out.println("testPattern"); byte start = 2; int[] arr = new int[8]; byte expResult = 0; byte result = nom.tam.util.ArrayFuncs.testPattern(arr, start); assertEquals(result, (byte) (start + arr.length)); assertEquals(start, arr[0]); assertEquals(start + arr.length - 1, arr[arr.length - 1]); } /** * Test of flatten method, of class nom.tam.util.ArrayFuncs. */ public void testFlatten() { System.out.println("flatten"); int[][][] test = new int[2][3][4]; int[] result = (int[]) ArrayFuncs.flatten(test); assertEquals(result.length, 24); } /** * Test of curl method, of class nom.tam.util.ArrayFuncs. */ public void testCurl() { System.out.println("curl"); int[] dimens = new int[]{2, 3, 4}; int[] test = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; int[][][] res = (int[][][]) nom.tam.util.ArrayFuncs.curl(test, dimens); assertEquals(res.length, 2); assertEquals(res[0].length, 3); assertEquals(res[0][0].length, 4); assertEquals(res[0][0][0], 0); assertEquals(res[0][0][3], 3); assertEquals(res[1][2][3], 23); } /** * Test of mimicArray method, of class nom.tam.util.ArrayFuncs. */ public void testMimicArray() { System.out.println("mimicArray"); int[][] array = new int[2][3]; Class newType = double.class; double[][] result = (double[][]) nom.tam.util.ArrayFuncs.mimicArray(array, newType); assertEquals(result.length, array.length); assertEquals(result[0].length, array[0].length); } /** * Test of convertArray method, of class nom.tam.util.ArrayFuncs. */ public void testConvertArray() { System.out.println("convertArray"); int[][] array = {{1, 2, 3}, {4, 5, 6}}; Class newType = double.class; boolean reuse = true; double[][] dres = (double[][]) ArrayFuncs.convertArray(array, newType, reuse); assertEquals(dres.length, array.length); assertEquals(dres[0].length, array[0].length); newType = int.class; int[][] ires = (int[][]) ArrayFuncs.convertArray(array, newType, true); assertEquals(array, ires); ires = (int[][]) ArrayFuncs.convertArray(array, newType, false); assertNotSame(array, ires); assertTrue(ArrayFuncs.arrayEquals(array, ires)); } /** * Test of copyInto method, of class nom.tam.util.ArrayFuncs. */ public void testCopyInto() { System.out.println("copyInto"); int[][] x = {{2, 3, 4}, {5, 6, 7}}; double[][] y = new double[2][3]; ArrayFuncs.copyInto(x, y); assertEquals((double) x[0][0], y[0][0]); assertEquals((double) x[1][2], y[1][2]); } /** * Test of arrayEquals method, of class nom.tam.util.ArrayFuncs. */ public void testArrayEquals() { System.out.println("arrayEquals"); int[][] x = {{1, 2, 3}, {4, 5, 6}}; int[][] y = {{1, 2, 3}, {4, 5, 6}}; int[][] z = {{1, 2, 3}, {4, 5, 7}}; int[][] t = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; assertTrue(ArrayFuncs.arrayEquals(null, null)); assertFalse(ArrayFuncs.arrayEquals(null, new int[2])); assertTrue(ArrayFuncs.arrayEquals(x, y)); assertFalse(ArrayFuncs.arrayEquals(x, z)); assertFalse(ArrayFuncs.arrayEquals(x, t)); assertTrue(ArrayFuncs.arrayEquals(x[0], z[0])); } /** * Test of doubleArrayEquals method, of class nom.tam.util.ArrayFuncs. */ public void testDoubleArrayEquals() { double x[] = {1, 2, 3}; double y[] = {1, 2, 3}; System.out.println("doubleArrayEquals"); double tol = 0.0; assertTrue(ArrayFuncs.doubleArrayEquals(x, y, tol)); x[0] += 1.e-14; assertFalse(ArrayFuncs.doubleArrayEquals(x, y, tol)); tol = 1.e-13; assertTrue(ArrayFuncs.doubleArrayEquals(x, y, tol)); } /** * Test of floatArrayEquals method, of class nom.tam.util.ArrayFuncs. */ public void testFloatArrayEquals() { float x[] = {1f, 2f, 3f}; float y[] = {1f, 2f, 3f}; System.out.println("floatArrayEquals"); float tol = 0.0F; assertTrue(ArrayFuncs.floatArrayEquals(x, y, tol)); x[0] += 1.e-6f; assertFalse(ArrayFuncs.floatArrayEquals(x, y, tol)); tol = 1.e-5f; assertTrue(ArrayFuncs.floatArrayEquals(x, y, tol)); } } fits-1.10.0/nom/tam/util/test/BigFileTest.java0000664000175000017500000000535412031031526021256 0ustar frothmaifrothmaipackage nom.tam.util.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; import nom.tam.util.BufferedFile; import nom.tam.util.BufferedDataInputStream; import java.io.FileInputStream; public class BigFileTest { @Test public void test() throws Exception { try { // First create a 3 GB file. String fname = System.getenv("BIGFILETEST"); if (fname == null) { System.out.println("BIGFILETEST environment not set. Returning without test"); return; } System.out.println("Big file test. Takes quite a while."); byte[] buf = new byte[100000000]; // 100 MB BufferedFile bf = new BufferedFile(fname, "rw"); byte sample = 13; for (int i = 0; i < 30; i += 1) { bf.write(buf); // 30 x 100 MB = 3 GB. if (i == 24) { bf.write(new byte[]{sample}); } // Add a marker. } bf.close(); // Now try to skip within the file. bf = new BufferedFile(fname, "r"); long skip = 2500000000L; // 2.5 G long val1 = bf.skipBytes(skip); long val2 = bf.getFilePointer(); int val = bf.read(); bf.close(); assertEquals("SkipResult", skip, val1); assertEquals("SkipPos", skip, val2); assertEquals("SkipVal", (int) sample, val); BufferedDataInputStream bdis = new BufferedDataInputStream( new FileInputStream(fname)); val1 = bdis.skipBytes(skip); val = bdis.read(); bdis.close(); assertEquals("SSkipResult", skip, val1); assertEquals("SSkipVal", (int) sample, val); } catch (Exception e) { e.printStackTrace(System.err); throw e; } } } fits-1.10.0/nom/tam/util/test/ByteFormatParseTest.java0000664000175000017500000002564312031031530023022 0ustar frothmaifrothmaipackage nom.tam.util.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; /** This class tests the ByteFormatter and ByteParser classes. */ import nom.tam.util.*; import java.util.Arrays; public class ByteFormatParseTest { byte[] buffer = new byte[100000]; ByteFormatter bf = new ByteFormatter(); ByteParser bp = new ByteParser(buffer); int offset = 0; int cnt = 0; @Test public void testInt() throws Exception { for (int i = 0; i < 10; i += 1) { buffer[i] = (byte) ' '; } bp.setOffset(0); assertEquals("IntBlank", 0, bp.getInt(10)); bf.setAlign(true); bf.setTruncationThrow(false); int[] tint = new int[100]; tint[0] = Integer.MIN_VALUE; tint[1] = Integer.MAX_VALUE; tint[2] = 0; for (int i = 0; i < tint.length; i += 1) { tint[i] = (int) (Integer.MAX_VALUE * (2 * (Math.random() - .5))); } // Write 100 numbers int colSize = 12; while (cnt < tint.length) { offset = bf.format(tint[cnt], buffer, offset, colSize); cnt += 1; if (cnt % 8 == 0) { offset = bf.format("\n", buffer, offset, 1); } } // Now see if we can get them back bp.setOffset(0); for (int i = 0; i < tint.length; i += 1) { int chk = bp.getInt(colSize); assertEquals("IntegersRA", chk, tint[i]); if ((i + 1) % 8 == 0) { bp.skip(1); } } // Now do it with left-aligned numbers. bf.setAlign(false); bp.setFillFields(true); offset = 0; colSize = 12; cnt = 0; offset = 0; while (cnt < tint.length) { int oldOffset = offset; offset = bf.format(tint[cnt], buffer, offset, colSize); int nb = colSize - (offset - oldOffset); if (nb > 0) { offset = bf.alignFill(buffer, offset, nb); } cnt += 1; if (cnt % 8 == 0) { offset = bf.format("\n", buffer, offset, 1); } } // Now see if we can get them back bp.setOffset(0); for (int i = 0; i < tint.length; i += 1) { int chk = bp.getInt(colSize); assertEquals("IntegersLA", chk, tint[i]); if ((i + 1) % 8 == 0) { bp.skip(1); } } offset = 0; colSize = 12; cnt = 0; offset = 0; while (cnt < tint.length) { offset = bf.format(tint[cnt], buffer, offset, colSize); cnt += 1; if (cnt % 8 == 0) { offset = bf.format("\n", buffer, offset, 1); } } String myStr = new String(buffer, 0, offset); assertEquals("No spaces", -1, myStr.indexOf(" ")); bf.setAlign(false); offset = 0; colSize = 12; cnt = 0; offset = 0; while (cnt < tint.length) { offset = bf.format(tint[cnt], buffer, offset, colSize); offset = bf.format(" ", buffer, offset, 1); cnt += 1; } myStr = new String(buffer, 0, offset); String[] array = myStr.split(" "); assertEquals("Split size", 100, array.length); for (int i = 0; i < array.length; i += 1) { assertEquals("Parse token", tint[i], Integer.parseInt(array[i])); } bf.setTruncationThrow(false); int val = 1; Arrays.fill(buffer, (byte) ' '); for (int i = 0; i < 10; i += 1) { offset = bf.format(val, buffer, 0, 6); String test = (val + " ").substring(0, 6); if (i < 6) { assertEquals("TestTrunc" + i, test, new String(buffer, 0, 6)); } else { assertEquals("TestTrunc" + i, "******", new String(buffer, 0, 6)); } val *= 10; } bf.setTruncationThrow(true); val = 1; for (int i = 0; i < 10; i += 1) { boolean thrown = false; try { offset = bf.format(val, buffer, 0, 6); } catch (TruncationException e) { thrown = true; } if (i < 6) { assertEquals("TestTruncThrow" + i, false, thrown); } else { assertEquals("TestTruncThrow" + i, true, thrown); } val *= 10; } } @Test public void testLong() throws Exception { for (int i = 0; i < 10; i += 1) { buffer[i] = (byte) ' '; } bp.setOffset(0); assertEquals("LongBlank", 0L, bp.getLong(10)); long[] lng = new long[100]; for (int i = 0; i < lng.length; i += 1) { lng[i] = (long) (Long.MAX_VALUE * (2 * (Math.random() - 0.5))); } lng[0] = Long.MAX_VALUE; lng[1] = Long.MIN_VALUE; lng[2] = 0; bf.setTruncationThrow(false); bp.setFillFields(true); bf.setAlign(true); offset = 0; for (int i = 0; i < lng.length; i += 1) { offset = bf.format(lng[i], buffer, offset, 20); if ((i + 1) % 4 == 0) { offset = bf.format("\n", buffer, offset, 1); } } bp.setOffset(0); for (int i = 0; i < lng.length; i += 1) { assertEquals("Long check", lng[i], bp.getLong(20)); if ((i + 1) % 4 == 0) { bp.skip(1); } } } @Test public void testFloat() throws Exception { for (int i = 0; i < 10; i += 1) { buffer[i] = (byte) ' '; } bp.setOffset(0); assertEquals("FloatBlank", 0.f, bp.getFloat(10), 0.); float[] flt = new float[100]; for (int i = 6; i < flt.length; i += 1) { flt[i] = (float) (2 * (Math.random() - 0.5) * Math.pow(10, 60 * (Math.random() - 0.5))); } flt[0] = Float.MAX_VALUE; flt[1] = Float.MIN_VALUE; flt[2] = 0; flt[3] = Float.NaN; flt[4] = Float.POSITIVE_INFINITY; flt[5] = Float.NEGATIVE_INFINITY; bf.setTruncationThrow(false); bf.setAlign(true); offset = 0; cnt = 0; while (cnt < flt.length) { offset = bf.format(flt[cnt], buffer, offset, 24); cnt += 1; if (cnt % 4 == 0) { offset = bf.format("\n", buffer, offset, 1); } } bp.setOffset(0); for (int i = 0; i < flt.length; i += 1) { float chk = bp.getFloat(24); float dx = Math.abs(chk - flt[i]); if (flt[i] != 0) { dx = dx / Math.abs(flt[i]); } if (Float.isNaN(flt[i])) { assertEquals("Float check:" + i, true, Float.isNaN(chk)); } else if (Float.isInfinite(flt[i])) { assertEquals("Float check:" + i, flt[i], chk, 0); } else { assertEquals("Float check:" + i, 0., dx, 1.e-6); } if ((i + 1) % 4 == 0) { bp.skip(1); } } } @Test public void testDouble() throws Exception { for (int i = 0; i < 10; i += 1) { buffer[i] = (byte) ' '; } bp.setOffset(0); assertEquals("DoubBlank", 0., bp.getDouble(10), 0.); double[] dbl = new double[100]; for (int i = 6; i < dbl.length; i += 1) { dbl[i] = 2 * (Math.random() - 0.5) * Math.pow(10, 60 * (Math.random() - 0.5)); } dbl[0] = Double.MAX_VALUE; dbl[1] = Double.MIN_VALUE; dbl[2] = 0; dbl[3] = Double.NaN; dbl[4] = Double.POSITIVE_INFINITY; dbl[5] = Double.NEGATIVE_INFINITY; bf.setTruncationThrow(false); bf.setAlign(true); offset = 0; cnt = 0; while (cnt < dbl.length) { offset = bf.format(dbl[cnt], buffer, offset, 25); cnt += 1; if (cnt % 4 == 0) { offset = bf.format("\n", buffer, offset, 1); } } bp.setOffset(0); for (int i = 0; i < dbl.length; i += 1) { double chk = bp.getDouble(25); double dx = Math.abs(chk - dbl[i]); if (dbl[i] != 0) { dx = dx / Math.abs(dbl[i]); } if (Double.isNaN(dbl[i])) { assertEquals("Double check:" + i, true, Double.isNaN(chk)); } else if (Double.isInfinite(dbl[i])) { assertEquals("Double check:" + i, dbl[i], chk, 0); } else { assertEquals("Double check:" + i, 0., dx, 1.e-14); } if ((i + 1) % 4 == 0) { bp.skip(1); } } } @Test public void testBoolean() throws Exception { boolean[] btst = new boolean[100]; for (int i = 0; i < btst.length; i += 1) { btst[i] = Math.random() > 0.5; } offset = 0; for (int i = 0; i < btst.length; i += 1) { offset = bf.format(btst[i], buffer, offset, 1); offset = bf.format(" ", buffer, offset, 1); } bp.setOffset(0); for (int i = 0; i < btst.length; i += 1) { assertEquals("Boolean:" + i, btst[i], bp.getBoolean()); } } @Test public void testString() throws Exception { offset = 0; String bigStr = "abcdefghijklmnopqrstuvwxyz"; for (int i = 0; i < 100; i += 1) { offset = bf.format(bigStr.substring(i % 27), buffer, offset, 13); offset = bf.format(" ", buffer, offset, 1); } bp.setOffset(0); for (int i = 0; i < 100; i += 1) { int ind = i % 27; if (ind > 13) { ind = 13; } String want = bigStr.substring(i % 27); if (want.length() > 13) { want = want.substring(0, 13); } String s = bp.getString(want.length()); assertEquals("String:" + i, want, s); bp.skip(1); } } } fits-1.10.0/nom/tam/util/test/HashedListTest.java0000664000175000017500000002054312031031530021775 0ustar frothmaifrothmaipackage nom.tam.util.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import nom.tam.util.HashedList; import nom.tam.util.Cursor; import java.util.*; import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; /** This class tests and illustrates the use * of the HashedList class. Tests are in three * parts. *

* The first section (in testCollection) tests the methods * that are present in the Collection interface. * All of the optional methods of that interface * are supported. This involves tests of the * HashedClass interface directly. *

* The second set of tests uses the Iterator (in testIterator) * returned by the iterator() method and tests * the standard Iterator methods to display * and remove rows from the HashedList. *

* The third set of tests (in testCursor) tests the extended * capabilities of the HashedListIterator * to add rows to the table, and to work * as a cursor to move in a non-linear fashion * through the list. * */ public class HashedListTest { @Test public void testCollection() { HashedList h1 = new HashedList(); HashedList h2 = new HashedList(); Cursor i = h1.iterator(0); Iterator j; // Add a few unkeyed rows. h1.add("Row 1"); h1.add("Row 2"); h1.add("Row 3"); assertEquals("Adding unkeyed rows", 3, h1.size()); assertEquals("Has row 1", true, h1.contains("Row 1")); assertEquals("Has row 2", true, h1.contains("Row 2")); h1.remove("Row 2"); assertEquals("Has row 1", true, h1.contains("Row 1")); assertEquals("Has row 2", false, h1.contains("Row 2")); assertEquals("Delete unkeyed rows", 2, h1.size()); h1.clear(); assertEquals("Cleared unkeyed rows", 0, h1.size()); h1.add("key 1", "Row 1"); h1.add("key 2", "Row 2"); h1.add("key 3", "Row 3"); assertEquals("Adding keyed rows", 3, h1.size()); assertEquals("Has Row 1", true, h1.contains("Row 1")); assertEquals("Has key 1", true, h1.containsKey("key 1")); assertEquals("Has Row 2", true, h1.contains("Row 2")); assertEquals("Has key 2", true, h1.containsKey("key 2")); assertEquals("Has Row 3", true, h1.contains("Row 3")); assertEquals("Has key 3", true, h1.containsKey("key 3")); h1.removeKey("key 2"); assertEquals("Delete keyed row", 2, h1.size()); assertEquals("Has Row 1", true, h1.contains("Row 1")); assertEquals("Has key 1", true, h1.containsKey("key 1")); assertEquals("Has Row 2", false, h1.contains("Row 2")); assertEquals("Has key 2", false, h1.containsKey("key 2")); assertEquals("Has Row 3", true, h1.contains("Row 3")); assertEquals("Has key 3", true, h1.containsKey("key 3")); h1.clear(); assertEquals("Clear keyed rows", 0, h1.size()); h1.add("key 1", "Row 1"); h1.add("key 2", "Row 2"); h1.add("key 3", "Row 3"); assertEquals("Re-Adding keyed rows", 3, h1.size()); assertEquals("Has Row 2", true, h1.contains("Row 2")); assertEquals("Has key 2", true, h1.containsKey("key 2")); h2.add("key 4", "Row 4"); h2.add("key 5", "Row 5"); assertEquals("containsAll(beforeAdd)", false, h1.containsAll(h2)); h1.addAll(h2); assertEquals("addAll()", 5, h1.size()); assertEquals("containsAll(afterAdd)", true, h1.containsAll(h2)); assertEquals("has row 4", true, h1.contains("Row 4")); h1.remove("Row 4"); assertEquals("dropped row 4", false, h1.contains("Row 4")); assertEquals("containsAll(afterDrop)", false, h1.containsAll(h2)); assertEquals("isEmpty(false)", false, h1.isEmpty()); h1.remove("Row 1"); h1.remove("Row 2"); h1.remove("Row 3"); h1.remove("Row 5"); assertEquals("isEmpty(true)", true, h1.isEmpty()); h1.add("Row 1"); h1.add("Row 2"); h1.add("Row 3"); h1.addAll(h2); assertEquals("Adding back", 5, h1.size()); h1.removeAll(h2); assertEquals("removeAll()", 3, h1.size()); h1.addAll(h2); assertEquals("Adding back again", 5, h1.size()); h1.retainAll(h2); assertEquals("retainAll()", 2, h1.size()); } @Test public void testIterator() { HashedList h1 = new HashedList(); h1.add("key 4", "Row 4"); h1.add("key 5", "Row 5"); Iterator j = h1.iterator(); assertEquals("next1", true, j.hasNext()); assertEquals("TestIter1", "Row 4", (String) j.next()); assertEquals("next2", true, j.hasNext()); assertEquals("TestIter2", "Row 5", (String) j.next()); assertEquals("next3", false, j.hasNext()); h1.clear(); h1.add("key 1", "Row 1"); h1.add("key 2", "Row 2"); h1.add("Row 3"); h1.add("key 4", "Row 4"); h1.add("Row 5"); assertEquals("Before remove", true, h1.contains("Row 2")); j = h1.iterator(); j.next(); j.next(); j.remove(); // Should get rid of second row assertEquals("After remove", false, h1.contains("Row 2")); assertEquals("n3", true, j.hasNext()); assertEquals("n3v", "Row 3", (String) j.next()); assertEquals("n4", true, j.hasNext()); assertEquals("n4v", "Row 4", (String) j.next()); assertEquals("n5", true, j.hasNext()); assertEquals("n5v", "Row 5", (String) j.next()); assertEquals("n6", false, j.hasNext()); } @Test public void TestCursor() { HashedList h1 = new HashedList(); h1.add("key 1", "Row 1"); h1.add("Row 3"); h1.add("key 4", "Row 4"); h1.add("Row 5"); Cursor j = (Cursor) h1.iterator(0); assertEquals("n1x", true, j.hasNext()); assertEquals("n1xv", "Row 1", (String) j.next()); assertEquals("n1xv", "Row 3", (String) j.next()); assertEquals("No Row 2", false, h1.containsKey("key 2")); assertEquals("No Row 2", false, h1.contains("Row 2")); j.setKey("key 1"); assertEquals("setKey()", "Row 1", (String) j.next()); j.add("key 2", "Row 2"); assertEquals("has Row 2", true, h1.contains("Row 2")); assertEquals("after add", "Row 3", (String) j.next()); j.setKey("key 4"); assertEquals("setKey(1)", "Row 4", (String) j.next()); assertEquals("setKey(2)", "Row 5", (String) j.next()); assertEquals("setKey(3)", false, j.hasNext()); j.setKey("key 2"); assertEquals("setKey(4)", "Row 2", (String) j.next()); assertEquals("setKey(5)", "Row 3", (String) j.next()); j.add("Row 3.5"); j.add("Row 3.6"); assertEquals("After add", 7, h1.size()); j = h1.iterator("key 2"); j.add("Row 1.5"); j.add("key 1.7", "Row 1.7"); j.add("Row 1.9"); assertEquals("next() after adds", "Row 2", (String) j.next()); j.setKey("key 1.7"); assertEquals("next() after adds", "Row 1.7", (String) j.next()); assertEquals("prev(1)", "Row 1.7", (String) j.prev()); assertEquals("prev(2)", "Row 1.5", (String) j.prev()); assertEquals("prev(3)", true, j.hasPrev()); assertEquals("prev(4)", "Row 1", (String) j.prev()); assertEquals("prev(5)", false, j.hasPrev()); } void show(HashedList h, String msg) { Iterator t = h.iterator(); System.out.println("\n Looking at list:" + msg); while (t.hasNext()) { System.out.println("Has element:" + t.next()); } } } fits-1.10.0/nom/tam/util/ArrayDataOutput.java0000664000175000017500000000641212031033644021226 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import java.io.IOException; public interface ArrayDataOutput extends java.io.DataOutput { /** Write a generic (possibly multi-dimenionsional) primitive or String * array. An array of Objects is also allowed if all * of the elements are valid arrays. *

* This routine is not called 'write' to avoid possible compilation * errors in routines which define only some of the other methods * of the interface (and defer to the superclass on others). * In that case there is an ambiguity as to whether to * call the routine in the current class but convert to * Object, or call the method from the super class with * the same type argument. * @param o The primitive or String array to be written. * @throws IOException if the argument is not of the proper type */ public void writeArray(Object o) throws IOException; /* Write a complete array */ public void write(byte[] buf) throws IOException; public void write(boolean[] buf) throws IOException; public void write(short[] buf) throws IOException; public void write(char[] buf) throws IOException; public void write(int[] buf) throws IOException; public void write(long[] buf) throws IOException; public void write(float[] buf) throws IOException; public void write(double[] buf) throws IOException; /* Write an array of Strings */ public void write(String[] buf) throws IOException; /* Write a segment of a primitive array. */ public void write(byte[] buf, int offset, int size) throws IOException; public void write(boolean[] buf, int offset, int size) throws IOException; public void write(char[] buf, int offset, int size) throws IOException; public void write(short[] buf, int offset, int size) throws IOException; public void write(int[] buf, int offset, int size) throws IOException; public void write(long[] buf, int offset, int size) throws IOException; public void write(float[] buf, int offset, int size) throws IOException; public void write(double[] buf, int offset, int size) throws IOException; /* Write some of an array of Strings */ public void write(String[] buf, int offset, int size) throws IOException; /* Flush the output buffer */ public void flush() throws IOException; public void close() throws IOException; } fits-1.10.0/nom/tam/util/BufferedFile.java0000664000175000017500000011164312031033644020462 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This class is intended for high performance I/O in scientific applications. * It adds buffering to the RandomAccessFile and also * provides efficient handling of arrays. Primitive arrays * may be written using a single method call. Large buffers * are used to minimize synchronization overheads since methods * of this class are not synchronized. *

* Note that although this class supports most of the * contract of RandomAccessFile it does not (and can not) * extend that class since many of the methods of * RandomAccessFile are final. In practice this * method works much like the StreamFilter classes. * All methods are implemented in this class but * some are simply delegated to an underlying RandomAccessFile member. *

* Testing and timing routines are available in * the nom.tam.util.test.BufferedFileTester class. * * Version 1.1 October 12, 2000: Fixed handling of EOF in array reads * so that a partial array will be returned when an EOF is detected. * Excess bytes that cannot be used to construct array elements will * be discarded (e.g., if there are 2 bytes left and the user is * reading an int array). * Version 1.2 December 8, 2002: Added getChannel method. * Version 1.3 March 2, 2007: Added File based constructors. * Version 1.4 July 20, 2009: Added support for >2G Object reads. * This is still a bit problematic in that we do not support * primitive arrays larger than 2 GB/atomsize. However except * in the case of bytes this is not currently a major issue. * */ import java.io.RandomAccessFile; import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.EOFException; public class BufferedFile implements ArrayDataInput, ArrayDataOutput, RandomAccess { /** The current offset into the buffer */ private int bufferOffset; /** The number of valid characters in the buffer */ private int bufferLength; /** The declared length of the buffer array */ private int bufferSize; /** Counter used in reading arrays */ private long primitiveArrayCount; /** The data buffer. */ private byte[] buffer; /** The underlying access to the file system */ private RandomAccessFile raf; /** The offset of the beginning of the current buffer */ private long fileOffset; /** Is the buffer being used for input or output */ private boolean doingInput; /** Create a read-only buffered file */ public BufferedFile(String filename) throws IOException { this(filename, "r", 32768); } /** Create a buffered file with the given mode. * @param filename The file to be accessed. * @param mode A string composed of "r" and "w" for * read and write access. */ public BufferedFile(String filename, String mode) throws IOException { this(filename, mode, 32768); } /** Create a buffered file from a File descriptor */ public BufferedFile(File file) throws IOException { this(file, "r", 32768); } /** Create a buffered file from a File descriptor */ public BufferedFile(File file, String mode) throws IOException { this(file, mode, 32768); } /** Create a buffered file with the given mode and a specified * buffer size. * @param filename The file to be accessed. * @param mode A string composed of "r" and "w" indicating * read or write access. * @param bufferSize The buffer size to be used. This should be * substantially larger than 100 bytes and * defaults to 32768 bytes in the other * constructors. */ public BufferedFile(String filename, String mode, int bufferSize) throws IOException { File file = new File(filename); initialize(file, mode, bufferSize); } /** Create a buffered file from a file descriptor */ public BufferedFile(File file, String mode, int bufferSize) throws IOException { initialize(file, mode, bufferSize); } protected void initialize(File file, String mode, int bufferSize) throws IOException { raf = new RandomAccessFile(file, mode); buffer = new byte[bufferSize]; bufferOffset = 0; bufferLength = 0; fileOffset = 0; this.bufferSize = bufferSize; } /** Create a buffered file using a mapped /** Read an entire byte array. * Note BufferedFile will return a partially filled array * only at an end-of-file. * @param buf The array to be filled. */ public int read(byte[] buf) throws IOException { return read(buf, 0, buf.length); } /** Read into a segment of a byte array. * @param buf The array to be filled. * @param offset The starting location for input. * @param len The number of bytes to be read. Fewer bytes * will be read if an EOF is reached. */ public int read(byte[] buf, int offset, int len) throws IOException { checkBuffer(-1); int total = 0; // Ensure that the entire buffer is read. while (len > 0) { if (bufferOffset < bufferLength) { int get = len; if (bufferOffset + get > bufferLength) { get = bufferLength - bufferOffset; } System.arraycopy(buffer, bufferOffset, buf, offset, get); len -= get; bufferOffset += get; offset += get; total += get; continue; } else { // This might be pretty long, but we know that the // old buffer is exhausted. try { if (len > bufferSize) { checkBuffer(bufferSize); } else { checkBuffer(len); } } catch (EOFException e) { if (bufferLength > 0) { System.arraycopy(buffer, 0, buf, offset, bufferLength); total += bufferLength; bufferLength = 0; } if (total == 0) { throw e; } else { return total; } } } } return total; } /** This should only be used when a small number of * bytes is required (substantially smaller than * bufferSize. */ private void checkBuffer(int needBytes) throws IOException { // Check if the buffer has some pending output. if (!doingInput && bufferOffset > 0) { flush(); } doingInput = true; if (bufferOffset + needBytes < bufferLength) { return; } /* Move the last few bytes to the beginning of the buffer * and read in enough data to fill the current demand. */ int len = bufferLength - bufferOffset; /* Note that new location that the beginning of the buffer * corresponds to. */ fileOffset += bufferOffset; if (len > 0) { System.arraycopy(buffer, bufferOffset, buffer, 0, len); } needBytes -= len; bufferLength = len; bufferOffset = 0; while (needBytes > 0) { len = raf.read(buffer, bufferLength, bufferSize - bufferLength); if (len < 0) { throw new EOFException(); } needBytes -= len; bufferLength += len; } } /** Read a byte */ public int read() throws IOException { checkBuffer(1); bufferOffset += 1; return buffer[bufferOffset - 1]; } /** Skip from the current position. * @param offset The number of bytes from the * current position. This may * be negative. */ public long skip(long offset) throws IOException { if (offset > 0 && fileOffset + bufferOffset + offset > raf.length()) { offset = raf.length() - fileOffset - bufferOffset; seek(raf.length()); } else if (fileOffset + bufferOffset + offset < 0) { offset = -(fileOffset + bufferOffset); seek(0); } else { seek(fileOffset + bufferOffset + offset); } return offset; } /** Move to the current offset from the beginning of the file. * A user may move past the end of file but this * does not extend the file unless data is written there. */ public void seek(long offsetFromStart) throws IOException { if (!doingInput) { // Have to flush before a seek... flush(); } // Are we within the current buffer? if (fileOffset <= offsetFromStart && offsetFromStart < fileOffset + bufferLength) { bufferOffset = (int) (offsetFromStart - fileOffset); } else { // Seek to the desired location. if (offsetFromStart < 0) { offsetFromStart = 0; } fileOffset = offsetFromStart; raf.seek(fileOffset); // Invalidate the current buffer. bufferLength = 0; bufferOffset = 0; } } /** Read a boolean * @return a boolean generated from the next * byte in the input. */ public boolean readBoolean() throws IOException { return convertToBoolean(); } /** Get a boolean from the buffer */ private boolean convertToBoolean() throws IOException { checkBuffer(1); bufferOffset += 1; return buffer[bufferOffset - 1] == 1; } /** Read a byte * @return the next byte in the input. */ public byte readByte() throws IOException { checkBuffer(1); bufferOffset += 1; return buffer[bufferOffset - 1]; } /** Read an unsigned byte. * @return the unsigned value of the next byte as * an integer. */ public int readUnsignedByte() throws IOException { checkBuffer(1); bufferOffset += 1; return buffer[bufferOffset - 1] | 0x00ff; } /** Read an int * @return an integer read from the input. */ public int readInt() throws IOException { return convertToInt(); } /** Get an integer value from the buffer */ private int convertToInt() throws IOException { checkBuffer(4); int x = bufferOffset; int i = buffer[x] << 24 | (buffer[x + 1] & 0xFF) << 16 | (buffer[x + 2] & 0xFF) << 8 | (buffer[x + 3] & 0xFF); bufferOffset += 4; return i; } /** Read a short * @return a short read from the input. */ public short readShort() throws IOException { return convertToShort(); } /** Get a short from the buffer */ private short convertToShort() throws IOException { checkBuffer(2); short s = (short) (buffer[bufferOffset] << 8 | (buffer[bufferOffset + 1] & 0xFF)); bufferOffset += 2; return s; } /** Read an unsigned short. * @return an unsigned short value as an integer. */ public int readUnsignedShort() throws IOException { return readShort() & 0xFFFF; } /** Read a char * @return a char read from the input. */ public char readChar() throws IOException { return convertToChar(); } /** Get a char from the buffer */ private char convertToChar() throws IOException { checkBuffer(2); char c = (char) (buffer[bufferOffset] << 8 | (buffer[bufferOffset + 1] & 0xFF)); bufferOffset += 2; return c; } /** Read a long. * @return a long value read from the input. */ public long readLong() throws IOException { return convertToLong(); } /** Get a long value from the buffer */ private long convertToLong() throws IOException { checkBuffer(8); int x = bufferOffset; int i1 = buffer[x] << 24 | (buffer[x + 1] & 0xFF) << 16 | (buffer[x + 2] & 0xFF) << 8 | (buffer[x + 3] & 0xFF); int i2 = buffer[x + 4] << 24 | (buffer[x + 5] & 0xFF) << 16 | (buffer[x + 6] & 0xFF) << 8 | (buffer[x + 7] & 0xFF); bufferOffset += 8; return (((long) i1) << 32) | (((long) i2) & 0x00000000ffffffffL); } /** Read a float. * @return a float value read from the input. */ public float readFloat() throws IOException { return Float.intBitsToFloat(convertToInt()); } /** Read a double. * @return a double value read from the input. */ public double readDouble() throws IOException { return Double.longBitsToDouble(convertToLong()); } /** Read a byte array fully. * Since the read method of this class reads an entire * buffer, the only difference with readFully is that * readFully will signal an EOF if the buffer cannot be filled. */ public void readFully(byte[] b) throws IOException { readFully(b, 0, b.length); } /** Read a byte array fully. * Since the read method of this class reads an entire * buffer, the only difference with readFully is that * readFully will signal an EOF if the buffer cannot be filled. */ public void readFully(byte[] b, int off, int len) throws IOException { if (off < 0 || len < 0 || off + len > b.length) { throw new IOException("Attempt to read outside byte array"); } if (read(b, off, len) < len) { throw new EOFException(); } } /** Skip the number of bytes. * This differs from the skip method in that * it will throw an EOF if a forward skip cannot * be fully accomplished... (However that isn't * supposed to happen with a random access file, * so there is probably no operational difference). */ public int skipBytes(int toSkip) throws IOException { return (int) skipBytes((long) toSkip); } public long skipBytes(long toSkip) throws IOException { // Note that we allow negative skips... if (skip(toSkip) < toSkip) { throw new EOFException(); } else { return toSkip; } } /** Read a string encoded as a UTF. * @return the string. */ public String readUTF() throws IOException { checkBuffer(-1); raf.seek(fileOffset + bufferOffset); String utf = raf.readUTF(); fileOffset = raf.getFilePointer(); // Invalidate the buffer. bufferLength = 0; bufferOffset = 0; return utf; } /** Read a line of input. * @return the next line. */ public String readLine() throws IOException { checkBuffer(-1); raf.seek(fileOffset + bufferOffset); String line = raf.readLine(); fileOffset = raf.getFilePointer(); // Invalidate the buffer. bufferLength = 0; bufferOffset = 0; return line; } /** This routine provides efficient reading of arrays of any primitive type. * @deprecated The readLArray(Object) routine should be used to * ensure that large arrays which read more than * two-gigabytes return the proper value. * * * @param o The object to be read. It must be an array of a primitive type, * or an array of Object's. */ public int readArray(Object o) throws IOException { return (int) readLArray(o); } /** This routine provides efficient reading of arrays of any primitive * type. * @param o The object to be read. It must be an arraof of a primtive type * (or any dimension), or an array of Objects which contains * pointers to primitive arrays or other object arrays. */ public long readLArray(Object o) throws IOException { // Note that we assume that only a single thread is // doing a primitive Array read at any given time. Otherwise // primitiveArrayCount can be wrong and also the // input data can be mixed up. If this assumption is not // true we need to synchronize this call. primitiveArrayCount = 0; return primitiveArrayRecurse(o); } protected long primitiveArrayRecurse(Object o) throws IOException { if (o == null) { return primitiveArrayCount; } String className = o.getClass().getName(); if (className.charAt(0) != '[') { throw new IOException("Invalid object passed to BufferedDataInputStream.readArray:" + className); } // Is this a multidimensional array? If so process recursively. if (className.charAt(1) == '[') { for (int i = 0; i < ((Object[]) o).length; i += 1) { primitiveArrayRecurse(((Object[]) o)[i]); } } else { // This is a one-d array. Process it using our special functions. switch (className.charAt(1)) { case 'Z': primitiveArrayCount += read((boolean[]) o, 0, ((boolean[]) o).length); break; case 'B': int len = read((byte[]) o, 0, ((byte[]) o).length); break; case 'C': primitiveArrayCount += read((char[]) o, 0, ((char[]) o).length); break; case 'S': primitiveArrayCount += read((short[]) o, 0, ((short[]) o).length); break; case 'I': primitiveArrayCount += read((int[]) o, 0, ((int[]) o).length); break; case 'J': primitiveArrayCount += read((long[]) o, 0, ((long[]) o).length); break; case 'F': primitiveArrayCount += read((float[]) o, 0, ((float[]) o).length); break; case 'D': primitiveArrayCount += read((double[]) o, 0, ((double[]) o).length); break; case 'L': // Handle an array of Objects by recursion. Anything // else is an error. if (className.equals("[Ljava.lang.Object;")) { for (int i = 0; i < ((Object[]) o).length; i += 1) { primitiveArrayRecurse(((Object[]) o)[i]); } } else { throw new IOException("Invalid object passed to BufferedFile.readPrimitiveArray: " + className); } break; default: throw new IOException("Invalid object passed to BufferedDataInputStream.readArray: " + className); } } return primitiveArrayCount; } public int read(boolean[] b) throws IOException { return read(b, 0, b.length); } public int read(boolean[] b, int start, int length) throws IOException { int i = start; try { for (; i < start + length; i += 1) { b[i] = convertToBoolean(); } return length; } catch (EOFException e) { return eofCheck(e, start, i, 1); } } public int read(short[] s) throws IOException { return read(s, 0, s.length); } public int read(short[] s, int start, int length) throws IOException { int i = start; try { for (; i < start + length; i += 1) { s[i] = convertToShort(); } return length * 2; } catch (EOFException e) { return eofCheck(e, start, i, 2); } } public int read(char[] c) throws IOException { return read(c, 0, c.length); } public int read(char[] c, int start, int length) throws IOException { int i = start; try { for (; i < start + length; i += 1) { c[i] = convertToChar(); } return length * 2; } catch (EOFException e) { return eofCheck(e, start, i, 2); } } public int read(int[] i) throws IOException { return read(i, 0, i.length); } public int read(int[] i, int start, int length) throws IOException { int ii = start; try { for (; ii < start + length; ii += 1) { i[ii] = convertToInt(); } return length * 4; } catch (EOFException e) { return eofCheck(e, start, ii, 4); } } public int read(long[] l) throws IOException { return read(l, 0, l.length); } public int read(long[] l, int start, int length) throws IOException { int i = start; try { for (; i < start + length; i += 1) { l[i] = convertToLong(); } return length * 8; } catch (EOFException e) { return eofCheck(e, start, i, 8); } } public int read(float[] f) throws IOException { return read(f, 0, f.length); } public int read(float[] f, int start, int length) throws IOException { int i = start; try { for (; i < start + length; i += 1) { f[i] = Float.intBitsToFloat(convertToInt()); } return length * 4; } catch (EOFException e) { return eofCheck(e, start, i, 4); } } public int read(double[] d) throws IOException { return read(d, 0, d.length); } public int read(double[] d, int start, int length) throws IOException { int i = start; try { for (; i < start + length; i += 1) { d[i] = Double.longBitsToDouble(convertToLong()); } return length * 8; } catch (EOFException e) { return eofCheck(e, start, i, 8); } } /** See if an exception should be thrown during an array read. */ private int eofCheck(EOFException e, int start, int index, int length) throws EOFException { if (start == index) { throw e; } else { return (index - start) * length; } } /**** Output Routines ****/ private void needBuffer(int need) throws IOException { if (doingInput) { fileOffset += bufferOffset; raf.seek(fileOffset); doingInput = false; bufferOffset = 0; bufferLength = 0; } if (bufferOffset + need >= bufferSize) { raf.write(buffer, 0, bufferOffset); fileOffset += bufferOffset; bufferOffset = 0; } } public void write(int buf) throws IOException { convertFromByte(buf); } public void write(byte[] buf) throws IOException { write(buf, 0, buf.length); } public void write(byte[] buf, int offset, int length) throws IOException { if (length < bufferSize) { /* If we can use the buffer do so... */ needBuffer(length); System.arraycopy(buf, offset, buffer, bufferOffset, length); bufferOffset += length; } else { /* Otherwise flush the buffer and write the data directly. * Make sure that we indicate that the buffer is clean when * we're done. */ flush(); raf.write(buf, offset, length); fileOffset += length; doingInput = false; bufferOffset = 0; bufferLength = 0; } } /** Flush output buffer if necessary. * This method is not present in RandomAccessFile * but users may need to call flush to ensure * that data has been written. */ public void flush() throws IOException { if (!doingInput && bufferOffset > 0) { raf.write(buffer, 0, bufferOffset); fileOffset += bufferOffset; bufferOffset = 0; bufferLength = 0; } } /** Clear up any pending output at cleanup. */ protected void finalize() { try { if (getFD().valid()) { flush(); close(); } } catch (Exception e) { } } /** Write a boolean value * @param b The value to be written. Externally true is represented as * a byte of 1 and false as a byte value of 0. */ public void writeBoolean(boolean b) throws IOException { convertFromBoolean(b); } private void convertFromBoolean(boolean b) throws IOException { needBuffer(1); if (b) { buffer[bufferOffset] = (byte) 1; } else { buffer[bufferOffset] = (byte) 0; } bufferOffset += 1; } /** Write a byte value. */ public void writeByte(int b) throws IOException { convertFromByte(b); } private void convertFromByte(int b) throws IOException { needBuffer(1); buffer[bufferOffset++] = (byte) b; } /** Write an integer value. */ public void writeInt(int i) throws IOException { convertFromInt(i); } private void convertFromInt(int i) throws IOException { needBuffer(4); buffer[bufferOffset++] = (byte) (i >>> 24); buffer[bufferOffset++] = (byte) (i >>> 16); buffer[bufferOffset++] = (byte) (i >>> 8); buffer[bufferOffset++] = (byte) i; } /** Write a short value. */ public void writeShort(int s) throws IOException { convertFromShort(s); } private void convertFromShort(int s) throws IOException { needBuffer(2); buffer[bufferOffset++] = (byte) (s >>> 8); buffer[bufferOffset++] = (byte) s; } /** Write a char value. */ public void writeChar(int c) throws IOException { convertFromChar(c); } private void convertFromChar(int c) throws IOException { needBuffer(2); buffer[bufferOffset++] = (byte) (c >>> 8); buffer[bufferOffset++] = (byte) c; } /** Write a long value. */ public void writeLong(long l) throws IOException { convertFromLong(l); } private void convertFromLong(long l) throws IOException { needBuffer(8); buffer[bufferOffset++] = (byte) (l >>> 56); buffer[bufferOffset++] = (byte) (l >>> 48); buffer[bufferOffset++] = (byte) (l >>> 40); buffer[bufferOffset++] = (byte) (l >>> 32); buffer[bufferOffset++] = (byte) (l >>> 24); buffer[bufferOffset++] = (byte) (l >>> 16); buffer[bufferOffset++] = (byte) (l >>> 8); buffer[bufferOffset++] = (byte) l; } /** Write a float value. */ public void writeFloat(float f) throws IOException { convertFromInt(Float.floatToIntBits(f)); } /** Write a double value. */ public void writeDouble(double d) throws IOException { convertFromLong(Double.doubleToLongBits(d)); } /** Write a string using the local protocol to convert char's to bytes. * * @param s The string to be written. */ public void writeBytes(String s) throws IOException { write(s.getBytes(), 0, s.length()); } /** Write a string as an array of chars. */ public void writeChars(String s) throws IOException { int len = s.length(); for (int i = 0; i < len; i += 1) { convertFromChar(s.charAt(i)); } } /** Write a string as a UTF. */ public void writeUTF(String s) throws IOException { flush(); raf.writeUTF(s); fileOffset = raf.getFilePointer(); } /** This routine provides efficient writing of arrays of any primitive type. * The String class is also handled but it is an error to invoke this * method with an object that is not an array of these types. If the * array is multidimensional, then it calls itself recursively to write * the entire array. Strings are written using the standard * 1 byte format (i.e., as in writeBytes). * * If the array is an array of objects, then write will * be called for each element of the array. * * @param o The object to be written. It must be an array of a primitive * type, Object, or String. */ public void writeArray(Object o) throws IOException { String className = o.getClass().getName(); if (className.charAt(0) != '[') { throw new IOException("Invalid object passed to BufferedFile.writeArray:" + className); } // Is this a multidimensional array? If so process recursively. if (className.charAt(1) == '[') { for (int i = 0; i < ((Object[]) o).length; i += 1) { writeArray(((Object[]) o)[i]); } } else { // This is a one-d array. Process it using our special functions. switch (className.charAt(1)) { case 'Z': write((boolean[]) o, 0, ((boolean[]) o).length); break; case 'B': write((byte[]) o, 0, ((byte[]) o).length); break; case 'C': write((char[]) o, 0, ((char[]) o).length); break; case 'S': write((short[]) o, 0, ((short[]) o).length); break; case 'I': write((int[]) o, 0, ((int[]) o).length); break; case 'J': write((long[]) o, 0, ((long[]) o).length); break; case 'F': write((float[]) o, 0, ((float[]) o).length); break; case 'D': write((double[]) o, 0, ((double[]) o).length); break; case 'L': // Handle two exceptions: an array of strings, or an // array of objects. . if (className.equals("[Ljava.lang.String;")) { write((String[]) o, 0, ((String[]) o).length); } else if (className.equals("[Ljava.lang.Object;")) { for (int i = 0; i < ((Object[]) o).length; i += 1) { writeArray(((Object[]) o)[i]); } } else { throw new IOException("Invalid object passed to BufferedFile.write: " + className); } break; default: throw new IOException("Invalid object passed to BufferedFile.write: " + className); } } } /** Write an array of booleans. */ public void write(boolean[] b) throws IOException { write(b, 0, b.length); } public void write(boolean[] b, int start, int length) throws IOException { for (int i = start; i < start + length; i += 1) { convertFromBoolean(b[i]); } } /** Write an array of shorts. */ public void write(short[] s) throws IOException { write(s, 0, s.length); } public void write(short[] s, int start, int length) throws IOException { for (int i = start; i < start + length; i += 1) { convertFromShort(s[i]); } } /** Write an array of char's. */ public void write(char[] c) throws IOException { write(c, 0, c.length); } public void write(char[] c, int start, int length) throws IOException { for (int i = start; i < start + length; i += 1) { convertFromChar(c[i]); } } /** Write an array of int's. */ public void write(int[] i) throws IOException { write(i, 0, i.length); } public void write(int[] i, int start, int length) throws IOException { for (int ii = start; ii < start + length; ii += 1) { convertFromInt(i[ii]); } } /** Write an array of longs. */ public void write(long[] l) throws IOException { write(l, 0, l.length); } public void write(long[] l, int start, int length) throws IOException { for (int i = start; i < start + length; i += 1) { convertFromLong(l[i]); } } /** Write an array of floats. */ public void write(float[] f) throws IOException { write(f, 0, f.length); } public void write(float[] f, int start, int length) throws IOException { for (int i = start; i < start + length; i += 1) { convertFromInt(Float.floatToIntBits(f[i])); } } /** Write an array of doubles. */ public void write(double[] d) throws IOException { write(d, 0, d.length); } public void write(double[] d, int start, int length) throws IOException { for (int i = start; i < start + length; i += 1) { convertFromLong(Double.doubleToLongBits(d[i])); } } /** Write an array of Strings -- equivalent to calling writeBytes for each string. */ public void write(String[] s) throws IOException { write(s, 0, s.length); } public void write(String[] s, int start, int length) throws IOException { for (int i = start; i < start + length; i += 1) { writeBytes(s[i]); } } /** Close the file */ public void close() throws IOException { flush(); raf.close(); } /** Get the file descriptor associated with * this stream. Note that this returns the file * descriptor of the associated RandomAccessFile. */ public FileDescriptor getFD() throws IOException { return raf.getFD(); } /** Get the channel associated with * this file. Note that this returns the channel * of the associated RandomAccessFile. * Note that since the BufferedFile buffers the I/O's to the * underlying file, the offset of the channel may be * different than the offset of the BufferedFile. This * is different than for a RandomAccessFile where the * offsets are guaranteed to be the same. */ public java.nio.channels.FileChannel getChannel() { return raf.getChannel(); } /** Get the current length of the file. */ public long length() throws IOException { flush(); return raf.length(); } /** Get the current offset into the file. */ public long getFilePointer() { return fileOffset + bufferOffset; } /** Set the length of the file. This method calls * the method of the same name in RandomAccessFile which * is only available in JDK1.2 and greater. This method * may be deleted for compilation with earlier versions. * * @param newLength The number of bytes at which the file * is set. * */ public void setLength(long newLength) throws IOException { flush(); raf.setLength(newLength); if (newLength < fileOffset) { fileOffset = newLength; } } } fits-1.10.0/nom/tam/util/BufferedDataInputStream.java0000664000175000017500000005606412031033644022655 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ // What do we use in here? import java.io.InputStream; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.EOFException; /** This class is intended for high performance I/O in scientific applications. * It combines the functionality of the BufferedInputStream and the * DataInputStream as well as more efficient handling of arrays. * This minimizes the number of method calls that are required to * read data. Informal tests of this method show that it can * be as much as 10 times faster than using a DataInputStream layered * on a BufferedInputStream for writing large arrays. The performance * gain on scalars or small arrays will be less but there should probably * never be substantial degradation of performance. *

* Many new read calls are added to allow efficient reading * off array data. The read(Object o) call provides * for reading a primitive array of arbitrary type or * dimensionality. There are also reads for each type * of one dimensional array. *

* Note that there is substantial duplication of code to minimize method * invocations. E.g., the floating point read routines read the data * as integer values and then convert to float. However the integer * code is duplicated rather than invoked. There has been * considerable effort expended to ensure that these routines are * efficient, but they could easily be superceded if * an efficient underlying I/O package were ever delivered * as part of the basic Java libraries. * [This has subsequently happened with the NIO package * and in an ideal universe these classes would be * rewritten to take advantage of NIO.] *

* Testing and timing routines are provided in the * nom.tam.util.test.BufferedFileTester class. * * Version 1.1: October 12, 2000: Fixed handling of EOF to return * partially read arrays when EOF is detected. * Version 1.2: July 20, 2009: Added handling of very large Object * arrays. Additional work is required to handle very large arrays * generally. */ public class BufferedDataInputStream extends BufferedInputStream implements ArrayDataInput { private long primitiveArrayCount; private byte[] bb = new byte[8]; /** Use the BufferedInputStream constructor */ public BufferedDataInputStream(InputStream o) { super(o, 32768); } /** Use the BufferedInputStream constructor */ public BufferedDataInputStream(InputStream o, int bufLength) { super(o, bufLength); } /** Read a byte array. This is the only method * for reading arrays in the fundamental I/O classes. * @param obuf The byte array. * @param offset The starting offset into the array. * @param len The number of bytes to read. * @return The actual number of bytes read. */ public int read(byte[] obuf, int offset, int len) throws IOException { int total = 0; while (len > 0) { // Use just the buffered I/O to get needed info. int xlen = super.read(obuf, offset, len); if (xlen <= 0) { if (total == 0) { throw new EOFException(); } else { return total; } } else { len -= xlen; total += xlen; offset += xlen; } } return total; } /** Read a boolean value. * @return b The value read. */ public boolean readBoolean() throws IOException { int b = read(); if (b == 1) { return true; } else { return false; } } /** Read a byte value in the range -128 to 127. * @return The byte value as a byte (see read() to return the value * as an integer. */ public byte readByte() throws IOException { return (byte) read(); } /** Read a byte value in the range 0-255. * @return The byte value as an integer. */ public int readUnsignedByte() throws IOException { return read() | 0x00ff; } /** Read an integer. * @return The integer value. */ public int readInt() throws IOException { if (read(bb, 0, 4) < 4) { throw new EOFException(); } int i = bb[0] << 24 | (bb[1] & 0xFF) << 16 | (bb[2] & 0xFF) << 8 | (bb[3] & 0xFF); return i; } /** Read a 2-byte value as a short (-32788 to 32767) * @return The short value. */ public short readShort() throws IOException { if (read(bb, 0, 2) < 2) { throw new EOFException(); } short s = (short) (bb[0] << 8 | (bb[1] & 0xFF)); return s; } /** Read a 2-byte value in the range 0-65536. * @return the value as an integer. */ public int readUnsignedShort() throws IOException { if (read(bb, 0, 2) < 2) { throw new EOFException(); } return (bb[0] & 0xFF) << 8 | (bb[1] & 0xFF); } /** Read a 2-byte value as a character. * @return The character read. */ public char readChar() throws IOException { byte[] b = new byte[2]; if (read(b, 0, 2) < 2) { throw new EOFException(); } char c = (char) (b[0] << 8 | (b[1] & 0xFF)); return c; } /** Read a long. * @return The value read. */ public long readLong() throws IOException { // use two ints as intermediarys to // avoid casts of bytes to longs... if (read(bb, 0, 8) < 8) { throw new EOFException(); } int i1 = bb[0] << 24 | (bb[1] & 0xFF) << 16 | (bb[2] & 0xFF) << 8 | (bb[3] & 0xFF); int i2 = bb[4] << 24 | (bb[5] & 0xFF) << 16 | (bb[6] & 0xFF) << 8 | (bb[7] & 0xFF); return (((long) i1) << 32) | (((long) i2) & 0x00000000ffffffffL); } /** Read a 4 byte real number. * @return The value as a float. */ public float readFloat() throws IOException { if (read(bb, 0, 4) < 4) { throw new EOFException(); } int i = bb[0] << 24 | (bb[1] & 0xFF) << 16 | (bb[2] & 0xFF) << 8 | (bb[3] & 0xFF); return Float.intBitsToFloat(i); } /** Read an 8 byte real number. * @return The value as a double. */ public double readDouble() throws IOException { if (read(bb, 0, 8) < 8) { throw new EOFException(); } int i1 = bb[0] << 24 | (bb[1] & 0xFF) << 16 | (bb[2] & 0xFF) << 8 | (bb[3] & 0xFF); int i2 = bb[4] << 24 | (bb[5] & 0xFF) << 16 | (bb[6] & 0xFF) << 8 | (bb[7] & 0xFF); return Double.longBitsToDouble(((long) i1) << 32 | ((long) i2 & 0x00000000ffffffffL)); } /** Read a buffer and signal an EOF if the buffer * cannot be fully read. * @param b The buffer to be read. */ public void readFully(byte[] b) throws IOException { readFully(b, 0, b.length); } /** Read a buffer and signal an EOF if the requested elements * cannot be read. * * This differs from read(b,off,len) since that call * will not signal and end of file unless no bytes can * be read. However both of these routines will attempt * to fill their buffers completely. * @param b The input buffer. * @param off The requested offset into the buffer. * @param len The number of bytes requested. */ public void readFully(byte[] b, int off, int len) throws IOException { if (off < 0 || len < 0 || off + len > b.length) { throw new IOException("Attempt to read outside byte array"); } if (read(b, off, len) < len) { throw new EOFException(); } } /** Skip the requested number of bytes. * This differs from the skip call in that * it takes an long argument and will throw * an end of file if the full number of bytes cannot be skipped. * @param toSkip The number of bytes to skip. */ private byte[] skipBuf = null; public int skipBytes(int toSkip) throws IOException { return (int) skipBytes((long) toSkip); } public long skipBytes(long toSkip) throws IOException { long need = toSkip; while (need > 0) { try { long got = skip(need); if (got > 0) { need -= got; } else { break; } } catch (IOException e) { // Some input streams (process outputs) don't allow // skipping. The kludgy solution here is to // try to do a read when we get an error in the skip.... // Real IO errors will presumably casue an error // in these reads too. if (skipBuf == null) { skipBuf = new byte[8192]; } while (need > 8192) { int got = read(skipBuf, 0, 8192); if (got <= 0) { break; } need -= got; } while (need > 0) { int got = read(skipBuf, 0, (int) need); if (got <= 0) { break; } need -= got; } } } if (need > 0) { throw new EOFException(); } else { return toSkip; } } /** Read a String in the UTF format. * The implementation of this is very inefficient and * use of this class is not recommended for applications * which will use this routine heavily. * @return The String that was read. */ public String readUTF() throws IOException { // Punt on this one and use DataInputStream routines. DataInputStream d = new DataInputStream(this); return d.readUTF(); } /** * Emulate the deprecated DataInputStream.readLine() method. * Originally we used the method itself, but Alan Brighton * suggested using a BufferedReader to eliminate the deprecation warning. * This was used for a long time, but more recently we * noted that this doesn't work. We now use * a simple method that largely ignores character encodings and only * uses the "\n" as the line separator. * This method is slow regardless. * * In the current version * * @return The String read. * @deprecated Use BufferedReader methods. */ public String readLine() throws IOException { // Punt on this and use BufferedReader routines. StringBuilder b = new StringBuilder(""); int chr; while ((chr = read()) >= 0) { if (chr != '\n') { b.append((char) chr); } else { return b.toString(); } } return b.toString(); } /** This routine provides efficient reading of arrays of any primitive type. * It is an error to invoke this method with an object that is not an array * of some primitive type. Note that there is no corresponding capability * to writePrimitiveArray in BufferedDataOutputStream to read in an * array of Strings. * * @param o The object to be read. It must be an array of a primitive type, * or an array of Object's. * @deprecated See readLArray(Object o). */ public int readPrimitiveArray(Object o) throws IOException { // Note that we assume that only a single thread is // doing a primitive Array read at any given time. Otherwise // primitiveArrayCount can be wrong and also the // input data can be mixed up. primitiveArrayCount = 0; return (int) readLArray(o); } /** Read an object. An EOF will be signaled if the * object cannot be fully read. The getPrimitiveArrayCount() * method may then be used to get a minimum number of bytes read. * @param o The object to be read. This object should * be a primitive (possibly multi-dimensional) array. * * @return The number of bytes read. * @deprecated See readLArray(Object) which handles large arrays properly. */ public int readArray(Object o) throws IOException { return (int) readLArray(o); } /** Read an object. An EOF will be signaled if the * object cannot be fully read. The getPrimitiveArrayCount() * method may then be used to get a minimum number of bytes read. * @param o The object to be read. This object should * be a primitive (possibly multi-dimensional) array. * * @return The number of bytes read. */ public long readLArray(Object o) throws IOException { primitiveArrayCount = 0; return primitiveArrayRecurse(o); } /** Read recursively over a multi-dimensional array. * @return The number of bytes read. */ protected long primitiveArrayRecurse(Object o) throws IOException { if (o == null) { return primitiveArrayCount; } String className = o.getClass().getName(); if (className.charAt(0) != '[') { throw new IOException("Invalid object passed to BufferedDataInputStream.readArray:" + className); } // Is this a multidimensional array? If so process recursively. if (className.charAt(1) == '[') { for (int i = 0; i < ((Object[]) o).length; i += 1) { primitiveArrayRecurse(((Object[]) o)[i]); } } else { // This is a one-d array. Process it using our special functions. switch (className.charAt(1)) { case 'Z': primitiveArrayCount += read((boolean[]) o, 0, ((boolean[]) o).length); break; case 'B': int len = read((byte[]) o, 0, ((byte[]) o).length); primitiveArrayCount += len; if (len < ((byte[]) o).length) { throw new EOFException(); } break; case 'C': primitiveArrayCount += read((char[]) o, 0, ((char[]) o).length); break; case 'S': primitiveArrayCount += read((short[]) o, 0, ((short[]) o).length); break; case 'I': primitiveArrayCount += read((int[]) o, 0, ((int[]) o).length); break; case 'J': primitiveArrayCount += read((long[]) o, 0, ((long[]) o).length); break; case 'F': primitiveArrayCount += read((float[]) o, 0, ((float[]) o).length); break; case 'D': primitiveArrayCount += read((double[]) o, 0, ((double[]) o).length); break; case 'L': // Handle an array of Objects by recursion. Anything // else is an error. if (className.equals("[Ljava.lang.Object;")) { for (int i = 0; i < ((Object[]) o).length; i += 1) { primitiveArrayRecurse(((Object[]) o)[i]); } } else { throw new IOException("Invalid object passed to BufferedDataInputStream.readArray: " + className); } break; default: throw new IOException("Invalid object passed to BufferedDataInputStream.readArray: " + className); } } return primitiveArrayCount; } /** Ensure that the requested number of bytes * are available in the buffer or throw an EOF * if they cannot be obtained. Note that this * routine will try to fill the buffer completely. * * @param The required number of bytes. */ private void fillBuf(int need) throws IOException { if (count > pos) { System.arraycopy(buf, pos, buf, 0, count - pos); count -= pos; need -= count; pos = 0; } else { count = 0; pos = 0; } while (need > 0) { int len = in.read(buf, count, buf.length - count); if (len <= 0) { throw new EOFException(); } count += len; need -= len; } } /** Read a boolean array */ public int read(boolean[] b) throws IOException { return read(b, 0, b.length); } /** Read a boolean array. */ public int read(boolean[] b, int start, int len) throws IOException { int i = start; try { for (; i < start + len; i += 1) { if (pos >= count) { fillBuf(1); } if (buf[pos] == 1) { b[i] = true; } else { b[i] = false; } pos += 1; } } catch (EOFException e) { return eofCheck(e, i, start, 1); } return len; } /** Read a short array */ public int read(short[] s) throws IOException { return read(s, 0, s.length); } /** Read a short array */ public int read(short[] s, int start, int len) throws IOException { int i = start; try { for (; i < start + len; i += 1) { if (count - pos < 2) { fillBuf(2); } s[i] = (short) (buf[pos] << 8 | (buf[pos + 1] & 0xFF)); pos += 2; } } catch (EOFException e) { return eofCheck(e, i, start, 2); } return 2 * len; } /** Read a character array */ public int read(char[] c) throws IOException { return read(c, 0, c.length); } /** Read a character array */ public int read(char[] c, int start, int len) throws IOException { int i = start; try { for (; i < start + len; i += 1) { if (count - pos < 2) { fillBuf(2); } c[i] = (char) (buf[pos] << 8 | (buf[pos + 1] & 0xFF)); pos += 2; } } catch (EOFException e) { return eofCheck(e, i, start, 2); } return 2 * len; } /** Read an integer array */ public int read(int[] i) throws IOException { return read(i, 0, i.length); } /** Read an integer array */ public int read(int[] i, int start, int len) throws IOException { int ii = start; try { for (; ii < start + len; ii += 1) { if (count - pos < 4) { fillBuf(4); } i[ii] = buf[pos] << 24 | (buf[pos + 1] & 0xFF) << 16 | (buf[pos + 2] & 0xFF) << 8 | (buf[pos + 3] & 0xFF); pos += 4; } } catch (EOFException e) { return eofCheck(e, ii, start, 4); } return i.length * 4; } /** Read a long array */ public int read(long[] l) throws IOException { return read(l, 0, l.length); } /** Read a long array */ public int read(long[] l, int start, int len) throws IOException { int i = start; try { for (; i < start + len; i += 1) { if (count - pos < 8) { fillBuf(8); } int i1 = buf[pos] << 24 | (buf[pos + 1] & 0xFF) << 16 | (buf[pos + 2] & 0xFF) << 8 | (buf[pos + 3] & 0xFF); int i2 = buf[pos + 4] << 24 | (buf[pos + 5] & 0xFF) << 16 | (buf[pos + 6] & 0xFF) << 8 | (buf[pos + 7] & 0xFF); l[i] = ((long) i1) << 32 | ((long) i2 & 0x00000000FFFFFFFFL); pos += 8; } } catch (EOFException e) { return eofCheck(e, i, start, 8); } return 8 * len; } /** Read a float array */ public int read(float[] f) throws IOException { return read(f, 0, f.length); } /** Read a float array */ public int read(float[] f, int start, int len) throws IOException { int i = start; try { for (; i < start + len; i += 1) { if (count - pos < 4) { fillBuf(4); } int t = buf[pos] << 24 | (buf[pos + 1] & 0xFF) << 16 | (buf[pos + 2] & 0xFF) << 8 | (buf[pos + 3] & 0xFF); f[i] = Float.intBitsToFloat(t); pos += 4; } } catch (EOFException e) { return eofCheck(e, i, start, 4); } return 4 * len; } /** Read a double array */ public int read(double[] d) throws IOException { return read(d, 0, d.length); } /** Read a double array */ public int read(double[] d, int start, int len) throws IOException { int i = start; try { for (; i < start + len; i += 1) { if (count - pos < 8) { fillBuf(8); } int i1 = buf[pos] << 24 | (buf[pos + 1] & 0xFF) << 16 | (buf[pos + 2] & 0xFF) << 8 | (buf[pos + 3] & 0xFF); int i2 = buf[pos + 4] << 24 | (buf[pos + 5] & 0xFF) << 16 | (buf[pos + 6] & 0xFF) << 8 | (buf[pos + 7] & 0xFF); d[i] = Double.longBitsToDouble( ((long) i1) << 32 | ((long) i2 & 0x00000000FFFFFFFFL)); pos += 8; } } catch (EOFException e) { return eofCheck(e, i, start, 8); } return 8 * len; } /** For array reads return an EOF if unable to * read any data. */ private int eofCheck(EOFException e, int i, int start, int length) throws EOFException { if (i == start) { throw e; } else { return (i - start) * length; } } /** Represent the stream as a string */ public String toString() { return super.toString() + "[count=" + count + ",pos=" + pos + "]"; } } fits-1.10.0/nom/tam/util/DataTable.java0000664000175000017500000000274312031033644017761 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This interface defines the properties that * a generic table should have. */ public interface DataTable { public abstract void setRow(int row, Object newRow) throws TableException; public abstract Object getRow(int row); public abstract void setColumn(int column, Object newColumn) throws TableException; public abstract Object getColumn(int column); public abstract void setElement(int row, int col, Object newElement) throws TableException; public abstract Object getElement(int row, int col); public abstract int getNRows(); public abstract int getNCols(); } fits-1.10.0/nom/tam/util/Cursor.java0000664000175000017500000000351612031033644017414 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This interface extends the Iterator interface * to allow insertion of data and move to previous entries * in a collection. */ public interface Cursor extends java.util.Iterator { /** Is there a previous element in the collection? */ public abstract boolean hasPrev(); /** Get the previous element */ public abstract Object prev() throws java.util.NoSuchElementException; /** Point the list at a particular element. * Point to the end of the list if the key is not found. */ public abstract void setKey(Object key); /** Add an unkeyed element to the collection. * The new element is placed such that it will be called * by a prev() call, but not a next() call. */ public abstract void add(Object reference); /** Add a keyed element to the collection. * The new element is placed such that it will be called * by a prev() call, but not a next() call. */ public abstract void add(Object key, Object reference); } fits-1.10.0/nom/tam/util/HashedList.java0000664000175000017500000002716612031033644020176 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This class implements a structure which can * be accessed either through a hash or * as linear list. Only some elements may have * a hash key. * * This class is motivated by the FITS header * structure where a user may wish to go through * the header element by element, or jump directly * to a given keyword. It assumes that all * keys are unique. However, all elements in the * structure need not have a key. * * This class does only the search structure * and knows nothing of the semantics of the * referenced objects. * */ import java.util.*; import java.lang.reflect.Array; import nom.tam.util.ArrayFuncs; import java.util.HashMap; import java.util.ArrayList; public class HashedList implements Collection { /** An ordered list of the keys */ private ArrayList ordered = new ArrayList(); /** The key value pairs */ private HashMap keyed = new HashMap(); /** This is used to generate unique keys for * elements entered without an key. */ private int unkeyedIndex = 0; private class HashedListIterator implements Cursor { /** This index points to the value that would be returned in * the next 'next' call. */ private int current; HashedListIterator(int start) { current = start; } /** Is there another element? */ public boolean hasNext() { return current >= 0 && current < ordered.size(); } /** Is there a previous element? */ public boolean hasPrev() { return current > 0; } /** Get the next entry. */ public Object next() throws NoSuchElementException { if (current < 0 || current >= ordered.size()) { throw new NoSuchElementException("Outside list"); } else { Object key = ordered.get(current); current += 1; return keyed.get(key); } } /** Get the previous entry. */ public Object prev() throws NoSuchElementException { if (current <= 0) { throw new NoSuchElementException("Before beginning of list"); } current -= 1; Object key = ordered.get(current); return keyed.get(key); } /** Remove an entry from the tree. Note that this can * now be called anytime after the iterator is created. */ public void remove() { if (current > 0 && current <= ordered.size()) { HashedList.this.remove(current - 1); // If we just removed the last entry, then we need // to go back one. if (current > 0) { current -= 1; } } } /** Add an entry at the current location. The new entry goes before * the entry that would be returned in the next 'next' call, and * that call will not be affected by the insertion. * Note: this method is not in the Iterator interface. */ public void add(Object ref) { Integer nKey = new Integer(unkeyedIndex); unkeyedIndex += 1; HashedList.this.add(current, nKey, ref); current += 1; } /** Add a keyed entry at the current location. The new entry is inserted * before the entry that would be returned in the next invocation of * 'next'. The return value for that call is unaffected. * Note: this method is not in the Iterator interface. */ public void add(Object key, Object ref) { HashedList.this.add(current, key, ref); current += 1; } /** Point the iterator to a particular keyed entry. This * method is not in the Iterator interface. * @param key */ public void setKey(Object key) { if (keyed.containsKey(key)) { current = ordered.indexOf(key); } else { current = ordered.size(); } } } /** Add an element to the end of the list. */ public boolean add(Object reference) { Integer nKey = new Integer(unkeyedIndex); unkeyedIndex += 1; HashedList.this.add(ordered.size(), nKey, reference); return true; } /** Add a keyed element to the end of the list. */ public boolean add(Object key, Object reference) { add(ordered.size(), key, reference); return true; } /** Add an element to the list. * @param pos The element before which the current element * be placed. If pos is null put the element at * the end of the list. * @param key The hash key for the new object. This may be null * for an unkeyed entry. * @param reference The actual object being stored. */ public boolean add(int pos, Object key, Object reference) { if (keyed.containsKey(key)) { int oldPos = ordered.indexOf(key); removeKey(key); if (oldPos < pos) { pos -= 1; } } keyed.put(key, reference); if (pos >= ordered.size()) { ordered.add(key); } else { ordered.add(pos, key); } return true; } /** Remove a keyed object from the list. Unkeyed * objects can be removed from the list using a * HashedListIterator or using the remove(Object) * method. */ public boolean removeKey(Object key) { if (keyed.containsKey(key)) { int index = ordered.indexOf(key); keyed.remove(key); ordered.remove(index); return true; } return false; } /** Remove an object from the list giving just * the object value. */ public boolean remove(Object o) { if (keyed.containsValue(o)) { for (int i = 0; i < ordered.size(); i += 1) { if (keyed.get(ordered.get(i)).equals(o)) { return removeKey(ordered.get(i)); } } } return false; } /** Remove an object from the list giving the object index..*/ public boolean remove(int index) { if (index >= 0 && index < ordered.size()) { Object key = ordered.get(index); return removeKey(key); } return false; } /** Return an iterator over the entire list. * The iterator may be used to delete * entries as well as to retrieve existing * entries. A knowledgeable user can * cast this to a HashedListIterator and * use it to add as well as delete entries. */ public Iterator iterator() { return new HashedListIterator(0); } /** Return an iterator over the list starting * with the entry with a given key. */ public HashedListIterator iterator(Object key) throws NoSuchElementException { if (keyed.containsKey(key)) { return new HashedListIterator(ordered.indexOf(key)); } else { throw new NoSuchElementException("Unknown key for iterator:" + key); } } /** Return an iterator starting with the n'th * entry. */ public HashedListIterator iterator(int n) throws NoSuchElementException { if (n >= 0 && n <= ordered.size()) { return new HashedListIterator(n); } else { throw new NoSuchElementException("Invalid index for iterator:" + n); } } /** Return the value of a keyed entry. Non-keyed * entries may be returned by requesting an iterator. */ public Object get(Object key) { return keyed.get(key); } /** Return the n'th entry from the beginning. */ public Object get(int n) throws NoSuchElementException { return keyed.get(ordered.get(n)); } /** Replace the key of a given element. * @param oldKey The previous key. This key must * be present in the hash. * @param newKey The new key. This key * must not be present in the hash. * @return if the replacement was successful. */ public boolean replaceKey(Object oldKey, Object newKey) { if (!keyed.containsKey(oldKey) || keyed.containsKey(newKey)) { return false; } Object oldVal = keyed.get(oldKey); int index = ordered.indexOf(oldKey); remove(index); return add(index, newKey, oldVal); } /** Check if the key is included in the list */ public boolean containsKey(Object key) { return keyed.containsKey(key); } /** Return the number of elements in the list. */ public int size() { return ordered.size(); } /** Add another collection to this one list. * All entries are added as unkeyed entries to the end of the list. */ public boolean addAll(Collection c) { Object[] array = c.toArray(); for (int i = 0; i < array.length; i += 1) { add(array[i]); } return true; } /** Clear the collection */ public void clear() { keyed.clear(); ordered.clear(); } /** Does the HashedList contain this element? */ public boolean contains(Object o) { return keyed.containsValue(o); } /** Does the HashedList contain all the elements * of this other collection. */ public boolean containsAll(Collection c) { return keyed.values().containsAll(c); } /** Is the HashedList empty? */ public boolean isEmpty() { return keyed.isEmpty(); } /** Remove all the elements that are found in another collection. */ public boolean removeAll(Collection c) { Object[] o = c.toArray(); boolean result = false; for (int i = 0; i < o.length; i += 1) { result = result | remove(o[i]); } return result; } /** Retain only elements contained in another collection */ public boolean retainAll(Collection c) { Iterator iter = iterator(); boolean result = false; while (iter.hasNext()) { Object o = iter.next(); if (!c.contains(o)) { iter.remove(); result = true; } } return result; } /** Convert to an array of objects */ public Object[] toArray() { Object[] o = new Object[ordered.size()]; return toArray(o); } /** Convert to an array of objects of * a specified type. */ public Object[] toArray(Object[] o) { return keyed.values().toArray(o); } /** Sort the keys into some desired order. */ public void sort(Comparator comp) { java.util.Collections.sort(ordered, comp); } } fits-1.10.0/nom/tam/util/DataIO.java0000664000175000017500000000231212031033644017231 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import java.io.DataInput; import java.io.DataOutput; /** This interface combines the DataInput, DataOutput and * RandomAccess interfaces to provide a reference type * which can be used to build BufferedFile in a fashion * that accommodates both the RandomAccessFile and ByteBuffers */ public interface DataIO extends DataInput, DataOutput, RandomAccess { } fits-1.10.0/nom/tam/util/TableException.java0000664000175000017500000000201112031033644021032 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ public class TableException extends Exception { public TableException() { super(); } public TableException(String msg) { super(msg); } } fits-1.10.0/nom/tam/util/BufferedDataOutputStream.java0000664000175000017500000003611312031033644023047 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ // What do we use in here? import java.io.OutputStream; import java.io.BufferedOutputStream; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.IOException; /** This class is intended for high performance I/O in scientific applications. * It combines the functionality of the BufferedOutputStream and the * DataOutputStream as well as more efficient handling of arrays. * This minimizes the number of method calls that are required to * write data. Informal tests of this method show that it can * be as much as 10 times faster than using a DataOutputStream layered * on a BufferedOutputStream for writing large arrays. The performance * gain on scalars or small arrays will be less but there should probably * never be substantial degradation of performance. *

* Note that there is substantial duplication of code to minimize method * invocations. However simple output methods were used where empirical * tests seemed to indicate that the simpler method did not cost any time. * It seems likely that most of these variations will be * washed out across different compilers and users who wish to tune * the method for their particular system may wish to compare the * the implementation of write(int[], int, int) with write(float[], int, int). *

* Testing and timing for this class is * peformed in the nom.tam.util.test.BufferedFileTester class. */ public class BufferedDataOutputStream extends BufferedOutputStream implements ArrayDataOutput { /** Use the BufferedOutputStream constructor * @param o An open output stream. */ public BufferedDataOutputStream(OutputStream o) { super(o, 32768); } /** Use the BufferedOutputStream constructor * @param o An open output stream. * @param bufLength The buffer size. */ public BufferedDataOutputStream(OutputStream o, int bufLength) { super(o, bufLength); } /** Write a boolean value * @param b The value to be written. Externally true is represented as * a byte of 1 and false as a byte value of 0. */ public void writeBoolean(boolean b) throws IOException { checkBuf(1); if (b) { buf[count++] = 1; } else { buf[count++] = 0; } } /** Write a byte value. */ public void writeByte(int b) throws IOException { checkBuf(1); buf[count++] = (byte) b; } /** Write an integer value. */ public void writeInt(int i) throws IOException { checkBuf(4); buf[count++] = (byte) (i >>> 24); buf[count++] = (byte) (i >>> 16); buf[count++] = (byte) (i >>> 8); buf[count++] = (byte) i; } /** Write a short value. */ public void writeShort(int s) throws IOException { checkBuf(2); buf[count++] = (byte) (s >>> 8); buf[count++] = (byte) s; } /** Write a char value. */ public void writeChar(int c) throws IOException { checkBuf(2); buf[count++] = (byte) (c >>> 8); buf[count++] = (byte) c; } /** Write a long value. */ public void writeLong(long l) throws IOException { checkBuf(8); buf[count++] = (byte) (l >>> 56); buf[count++] = (byte) (l >>> 48); buf[count++] = (byte) (l >>> 40); buf[count++] = (byte) (l >>> 32); buf[count++] = (byte) (l >>> 24); buf[count++] = (byte) (l >>> 16); buf[count++] = (byte) (l >>> 8); buf[count++] = (byte) l; } /** Write a float value. */ public void writeFloat(float f) throws IOException { checkBuf(4); int i = Float.floatToIntBits(f); buf[count++] = (byte) (i >>> 24); buf[count++] = (byte) (i >>> 16); buf[count++] = (byte) (i >>> 8); buf[count++] = (byte) i; } /** Write a double value. */ public void writeDouble(double d) throws IOException { checkBuf(8); long l = Double.doubleToLongBits(d); buf[count++] = (byte) (l >>> 56); buf[count++] = (byte) (l >>> 48); buf[count++] = (byte) (l >>> 40); buf[count++] = (byte) (l >>> 32); buf[count++] = (byte) (l >>> 24); buf[count++] = (byte) (l >>> 16); buf[count++] = (byte) (l >>> 8); buf[count++] = (byte) l; } /** Write a string using the local protocol to convert char's to bytes. * * @param s The string to be written. */ public void writeBytes(String s) throws IOException { write(s.getBytes(), 0, s.length()); } /** Write a string as an array of chars. */ public void writeChars(String s) throws IOException { for (int i = 0; i < s.length(); i += 1) { writeChar(s.charAt(i)); } } /** Write a string as a UTF. Note that this class does not * handle this situation efficiently since it creates * new DataOutputStream to handle each call. */ public void writeUTF(String s) throws IOException { // Punt on this one and use standard routines. DataOutputStream d = new DataOutputStream(this); d.writeUTF(s); d.flush(); d.close(); } /** This routine provides efficient writing of arrays of any primitive type. * The String class is also handled but it is an error to invoke this * method with an object that is not an array of these types. If the * array is multidimensional, then it calls itself recursively to write * the entire array. Strings are written using the standard * 1 byte format (i.e., as in writeBytes). * * If the array is an array of objects, then writePrimitiveArray will * be called for each element of the array. * * @param o The object to be written. It must be an array of a primitive * type, Object, or String. */ public void writePrimitiveArray(Object o) throws IOException { writeArray(o); } /** This routine provides efficient writing of arrays of any primitive type. * The String class is also handled but it is an error to invoke this * method with an object that is not an array of these types. If the * array is multidimensional, then it calls itself recursively to write * the entire array. Strings are written using the standard * 1 byte format (i.e., as in writeBytes). * * If the array is an array of objects, then writePrimitiveArray will * be called for each element of the array. * * @param o The object to be written. It must be an array of a primitive * type, Object, or String. */ public void writeArray(Object o) throws IOException { String className = o.getClass().getName(); if (className.charAt(0) != '[') { throw new IOException("Invalid object passed to BufferedDataOutputStream.write" + className); } // Is this a multidimensional array? If so process recursively. if (className.charAt(1) == '[') { for (int i = 0; i < ((Object[]) o).length; i += 1) { writeArray(((Object[]) o)[i]); } } else { // This is a one-d array. Process it using our special functions. switch (className.charAt(1)) { case 'Z': write((boolean[]) o, 0, ((boolean[]) o).length); break; case 'B': write((byte[]) o, 0, ((byte[]) o).length); break; case 'C': write((char[]) o, 0, ((char[]) o).length); break; case 'S': write((short[]) o, 0, ((short[]) o).length); break; case 'I': write((int[]) o, 0, ((int[]) o).length); break; case 'J': write((long[]) o, 0, ((long[]) o).length); break; case 'F': write((float[]) o, 0, ((float[]) o).length); break; case 'D': write((double[]) o, 0, ((double[]) o).length); break; case 'L': // Handle two exceptions: an array of strings, or an // array of objects. . if (className.equals("[Ljava.lang.String;")) { write((String[]) o, 0, ((String[]) o).length); } else if (className.equals("[Ljava.lang.Object;")) { for (int i = 0; i < ((Object[]) o).length; i += 1) { writeArray(((Object[]) o)[i]); } } else { throw new IOException("Invalid object passed to BufferedDataOutputStream.writeArray: " + className); } break; default: throw new IOException("Invalid object passed to BufferedDataOutputStream.writeArray: " + className); } } } /** Write an array of booleans. */ public void write(boolean[] b) throws IOException { write(b, 0, b.length); } /** Write a segment of an array of booleans. */ public void write(boolean[] b, int start, int len) throws IOException { for (int i = start; i < start + len; i += 1) { if (count + 1 > buf.length) { checkBuf(1); } if (b[i]) { buf[count++] = 1; } else { buf[count++] = 0; } } } /** Write an array of shorts. */ public void write(short[] s) throws IOException { write(s, 0, s.length); } /** Write a segment of an array of shorts. */ public void write(short[] s, int start, int len) throws IOException { for (int i = start; i < start + len; i += 1) { if (count + 2 > buf.length) { checkBuf(2); } buf[count++] = (byte) (s[i] >> 8); buf[count++] = (byte) (s[i]); } } /** Write an array of char's. */ public void write(char[] c) throws IOException { write(c, 0, c.length); } /** Write a segment of an array of char's. */ public void write(char[] c, int start, int len) throws IOException { for (int i = start; i < start + len; i += 1) { if (count + 2 > buf.length) { checkBuf(2); } buf[count++] = (byte) (c[i] >> 8); buf[count++] = (byte) (c[i]); } } /** Write an array of int's. */ public void write(int[] i) throws IOException { write(i, 0, i.length); } /** Write a segment of an array of int's. */ public void write(int[] i, int start, int len) throws IOException { for (int ii = start; ii < start + len; ii += 1) { if (count + 4 > buf.length) { checkBuf(4); } buf[count++] = (byte) (i[ii] >>> 24); buf[count++] = (byte) (i[ii] >>> 16); buf[count++] = (byte) (i[ii] >>> 8); buf[count++] = (byte) (i[ii]); } } /** Write an array of longs. */ public void write(long[] l) throws IOException { write(l, 0, l.length); } /** Write a segement of an array of longs. */ public void write(long[] l, int start, int len) throws IOException { for (int i = start; i < start + len; i += 1) { if (count + 8 > buf.length) { checkBuf(8); } int t = (int) (l[i] >>> 32); buf[count++] = (byte) (t >>> 24); buf[count++] = (byte) (t >>> 16); buf[count++] = (byte) (t >>> 8); buf[count++] = (byte) (t); t = (int) (l[i]); buf[count++] = (byte) (t >>> 24); buf[count++] = (byte) (t >>> 16); buf[count++] = (byte) (t >>> 8); buf[count++] = (byte) (t); } } /** Write an array of floats. */ public void write(float[] f) throws IOException { write(f, 0, f.length); } public void write(float[] f, int start, int len) throws IOException { for (int i = start; i < start + len; i += 1) { if (count + 4 > buf.length) { checkBuf(4); } int t = Float.floatToIntBits(f[i]); buf[count++] = (byte) (t >>> 24); buf[count++] = (byte) (t >>> 16); buf[count++] = (byte) (t >>> 8); buf[count++] = (byte) t; } } /** Write an array of doubles. */ public void write(double[] d) throws IOException { write(d, 0, d.length); } public void write(double[] d, int start, int len) throws IOException { for (int i = start; i < start + len; i += 1) { if (count + 8 > buf.length) { checkBuf(8); } long t = Double.doubleToLongBits(d[i]); int ix = (int) (t >>> 32); buf[count++] = (byte) (ix >>> 24); buf[count++] = (byte) (ix >>> 16); buf[count++] = (byte) (ix >>> 8); buf[count++] = (byte) (ix); ix = (int) t; buf[count++] = (byte) (ix >>> 24); buf[count++] = (byte) (ix >>> 16); buf[count++] = (byte) (ix >>> 8); buf[count++] = (byte) ix; } } /** Write an array of Strings -- equivalent to calling writeBytes for each string. */ public void write(String[] s) throws IOException { write(s, 0, s.length); } /** Write a segment of an array of Strings. * Equivalent to calling writeBytes for the selected elements. */ public void write(String[] s, int start, int len) throws IOException { // Do not worry about buffering this specially since the // strings may be of differing lengths. for (int i = 0; i < s.length; i += 1) { writeBytes(s[i]); } } /* See if there is enough space to add * something to the buffer. */ protected void checkBuf(int need) throws IOException { if (count + need > buf.length) { out.write(buf, 0, count); count = 0; } } } fits-1.10.0/nom/tam/util/FormatException.java0000664000175000017500000000200712031033644021240 0ustar frothmaifrothmaipackage nom.tam.util; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ public class FormatException extends java.lang.Exception { FormatException() { super(); } FormatException(String msg) { super(msg); } } fits-1.10.0/nom/tam/image/0000775000175000017500000000000011566662572015420 5ustar frothmaifrothmaifits-1.10.0/nom/tam/image/ImageTiler.java0000664000175000017500000002651312031033644020270 0ustar frothmaifrothmaipackage nom.tam.image; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import nom.tam.util.*; import java.lang.reflect.Array; import java.io.IOException; /** This class provides a subset of an N-dimensional image. * Modified May 2, 2000 by T. McGlynn to permit * tiles that go off the edge of the image. */ public class ImageTiler { RandomAccess f; long fileOffset; int[] dims; Class base; /** Create a tiler. * @param f The random access device from which image data may be read. * This may be null if the tile information is available from * memory. * @param fileOffset The file offset within the RandomAccess device at which * the data begins. * @param dims The actual dimensions of the image. * @param base The base class (should be a primitive type) of the image. */ public ImageTiler(RandomAccess f, long fileOffset, int[] dims, Class base) { this.f = f; this.fileOffset = fileOffset; this.dims = dims; this.base = base; } /** See if we can get the image data from memory. * This may be overriden by other classes, notably * in nom.tam.fits.ImageData. */ public Object getMemoryImage() { return null; } /** Get a subset of the image. An image tile is returned * as a one-dimensional array although the image will * normally be multi-dimensional. * @param corners The starting corner (using 0 as the start) for the image. * @param lengths The length requested in each dimension. */ public Object getTile(int[] corners, int[] lengths) throws IOException { if (corners.length != dims.length || lengths.length != dims.length) { throw new IOException("Inconsistent sub-image request"); } int arraySize = 1; for (int i = 0; i < dims.length; i += 1) { if (corners[i] < 0 || lengths[i] < 0 || corners[i] + lengths[i] > dims[i]) { throw new IOException("Sub-image not within image"); } arraySize *= lengths[i]; } Object outArray = ArrayFuncs.newInstance(base, arraySize); getTile(outArray, corners, lengths); return outArray; } /** Get a tile, filling in a prespecified array. * This version does not check that the user hase * entered a valid set of corner and length arrays. * ensure that out matches the * length implied by the lengths array. * * @param outArray The output tile array. A one-dimensional * array. * Data not within the valid limits of the image will * be left unchanged. The length of this * array should be the product of lengths. * @param corners The corners of the tile. * @param lengths The dimensions of the tile. * */ public void getTile(Object outArray, int[] corners, int[] lengths) throws IOException { Object data = getMemoryImage(); if (data == null && f == null) { throw new IOException("No data source for tile subset"); } fillTile(data, outArray, dims, corners, lengths); } /** Fill the subset. * @param data The memory-resident data image. * This may be null if the image is to * be read from a file. This should * be a multi-dimensional primitive array. * @param o The tile to be filled. This is a * simple primitive array. * @param dims The dimensions of the full image. * @param corners The indices of the corner of the image. * @param lengths The dimensions of the subset. */ protected void fillTile(Object data, Object o, int[] dims, int[] corners, int[] lengths) throws IOException { int n = dims.length; int[] posits = new int[n]; int baseLength = ArrayFuncs.getBaseLength(o); int segment = lengths[n - 1]; System.arraycopy(corners, 0, posits, 0, n); long currentOffset = 0; if (data == null) { currentOffset = f.getFilePointer(); } int outputOffset = 0; do { // This implies there is some overlap // in the last index (in conjunction // with other tests) int mx = dims.length - 1; boolean validSegment = posits[mx] + lengths[mx] >= 0 && posits[mx] < dims[mx]; // Don't do anything for the current // segment if anything but the // last index is out of range. if (validSegment) { for (int i = 0; i < mx; i += 1) { if (posits[i] < 0 || posits[i] >= dims[i]) { validSegment = false; break; } } } if (validSegment) { if (data != null) { fillMemData(data, posits, segment, o, outputOffset, 0); } else { int offset = getOffset(dims, posits) * baseLength; // Point to offset at real beginning // of segment int actualLen = segment; int actualOffset = offset; int actualOutput = outputOffset; if (posits[mx] < 0) { actualOffset -= posits[mx] * baseLength; actualOutput -= posits[mx]; actualLen += posits[mx]; } if (posits[mx] + segment > dims[mx]) { actualLen -= posits[mx] + segment - dims[mx]; } fillFileData(o, actualOffset, actualOutput, actualLen); } } outputOffset += segment; } while (incrementPosition(corners, posits, lengths)); if (data == null) { f.seek(currentOffset); } } /** Fill a single segment from memory. * This routine is called recursively to handle multi-dimensional * arrays. E.g., if data is three-dimensional, this will * recurse two levels until we get a call with a single dimensional * datum. At that point the appropriate data will be copied * into the output. * * @param data The in-memory image data. * @param posits The current position for which data is requested. * @param length The size of the segments. * @param output The output tile. * @param outputOffset The current offset into the output tile. * @param dim The current dimension being */ protected void fillMemData(Object data, int[] posits, int length, Object output, int outputOffset, int dim) { if (data instanceof Object[]) { Object[] xo = (Object[]) data; fillMemData(xo[posits[dim]], posits, length, output, outputOffset, dim + 1); } else { // Adjust the spacing for the actual copy. int startFrom = posits[dim]; int startTo = outputOffset; int copyLength = length; if (posits[dim] < 0) { startFrom -= posits[dim]; startTo -= posits[dim]; copyLength += posits[dim]; } if (posits[dim] + length > dims[dim]) { copyLength -= (posits[dim] + length - dims[dim]); } System.arraycopy(data, startFrom, output, startTo, copyLength); } } /** File a tile segment from a file. * @param output The output tile. * @param delta The offset from the beginning of the image in bytes. * @param outputOffset The index into the output array. * @param segment The number of elements to be read for this segment. */ protected void fillFileData(Object output, int delta, int outputOffset, int segment) throws IOException { f.seek(fileOffset + delta); if (base == float.class) { f.read((float[]) output, outputOffset, segment); } else if (base == int.class) { f.read((int[]) output, outputOffset, segment); } else if (base == short.class) { f.read((short[]) output, outputOffset, segment); } else if (base == double.class) { f.read((double[]) output, outputOffset, segment); } else if (base == byte.class) { f.read((byte[]) output, outputOffset, segment); } else if (base == char.class) { f.read((char[]) output, outputOffset, segment); } else if (base == long.class) { f.read((long[]) output, outputOffset, segment); } else { throw new IOException("Invalid type for tile array"); } } /** Increment the offset within the position array. * Note that we never look at the last index since * we copy data a block at a time and not byte by byte. * @param start The starting corner values. * @param current The current offsets. * @param lengths The desired dimensions of the subset. */ protected static boolean incrementPosition(int[] start, int[] current, int[] lengths) { for (int i = start.length - 2; i >= 0; i -= 1) { if (current[i] - start[i] < lengths[i] - 1) { current[i] += 1; for (int j = i + 1; j < start.length - 1; j += 1) { current[j] = start[j]; } return true; } } return false; } /** Get the offset of a given position. * @param dims The dimensions of the array. * @param pos The index requested. */ public static final int getOffset(int[] dims, int[] pos) { int offset = 0; for (int i = 0; i < dims.length; i += 1) { if (i > 0) { offset *= dims[i]; } offset += pos[i]; } return offset; } /** Read the entire image into a multidimensional * array. */ public Object getCompleteImage() throws IOException { if (f == null) { throw new IOException("Attempt to read from null file"); } long currentOffset = f.getFilePointer(); Object o = ArrayFuncs.newInstance(base, dims); f.seek(fileOffset); f.readLArray(o); f.seek(currentOffset); return o; } } fits-1.10.0/nom/tam/fits/0000775000175000017500000000000011566662560015300 5ustar frothmaifrothmaifits-1.10.0/nom/tam/fits/HeaderCardException.java0000664000175000017500000000212412031033644021762 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /* This class was contributed by David Glowacki */ public class HeaderCardException extends FitsException { public HeaderCardException() { super(); } public HeaderCardException(String s) { super(s); } } fits-1.10.0/nom/tam/fits/BinaryTableHDU.java0000664000175000017500000002425112031033644020663 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import nom.tam.util.ArrayFuncs; import nom.tam.util.*; import java.io.IOException; import java.lang.reflect.Array; import java.io.ByteArrayOutputStream; import java.io.ByteArrayInputStream; /** FITS binary table header/data unit */ public class BinaryTableHDU extends TableHDU { private BinaryTable table; /** The standard column keywords for a binary table. */ private String[] keyStems = {"TTYPE", "TFORM", "TUNIT", "TNULL", "TSCAL", "TZERO", "TDISP", "TDIM"}; public BinaryTableHDU(Header hdr, Data datum) { super((TableData) datum); myHeader = hdr; myData = datum; table = (BinaryTable) datum; } /** Create data from a binary table header. * @param header the template specifying the binary table. * @exception FitsException if there was a problem with the header. */ public static Data manufactureData(Header header) throws FitsException { return new BinaryTable(header); } public Data manufactureData() throws FitsException { return manufactureData(myHeader); } /** Build a binary table HDU from the supplied data. * @param data the data used to build the binary table. This is typically * some kind of array of objects. * @exception FitsException if there was a problem with the data. */ public static Header manufactureHeader(Data data) throws FitsException { Header hdr = new Header(); data.fillHeader(hdr); return hdr; } /** Encapsulate data in a BinaryTable data type */ public static Data encapsulate(Object o) throws FitsException { if (o instanceof nom.tam.util.ColumnTable) { return new BinaryTable((nom.tam.util.ColumnTable) o); } else if (o instanceof Object[][]) { return new BinaryTable((Object[][]) o); } else if (o instanceof Object[]) { return new BinaryTable((Object[]) o); } else { throw new FitsException("Unable to encapsulate object of type:" + o.getClass().getName() + " as BinaryTable"); } } /** Check that this is a valid binary table header. * @param header to validate. * @return true if this is a binary table header. */ public static boolean isHeader(Header header) { String xten = header.getStringValue("XTENSION"); if (xten == null) { return false; } xten = xten.trim(); if (xten.equals("BINTABLE") || xten.equals("A3DTABLE")) { return true; } else { return false; } } /** Check that this HDU has a valid header. * @return true if this HDU has a valid header. */ public boolean isHeader() { return isHeader(myHeader); } /* Check if this data object is consistent with a binary table. There * are three options: a column table object, an Object[][], or an Object[]. * This routine doesn't check that the dimensions of arrays are properly * consistent. */ public static boolean isData(Object o) { if (o instanceof nom.tam.util.ColumnTable || o instanceof Object[][] || o instanceof Object[]) { return true; } else { return false; } } /** 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. Note: 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 int addColumn(Object data) throws FitsException { int col = table.addColumn(data); table.pointToColumn(getNCols() - 1, myHeader); return col; } // Need to tell header about the Heap before writing. public void write(ArrayDataOutput ado) throws FitsException { int oldSize = myHeader.getIntValue("PCOUNT"); if (oldSize != table.getHeapSize()) { myHeader.addValue("PCOUNT", table.getHeapSize(), "ntf::binarytablehdu:pcount:1"); } if (myHeader.getIntValue("PCOUNT") == 0) { myHeader.deleteKey("THEAP"); } else { myHeader.getIntValue("TFIELDS"); int offset = myHeader.getIntValue("NAXIS1") * myHeader.getIntValue("NAXIS2") + table.getHeapOffset(); myHeader.addValue("THEAP", offset, "ntf::binarytablehdu:theap:1"); } super.write(ado); } /** * Convert a column in the table to complex. Only tables with appropriate * types and dimensionalities can be converted. It is legal to call this on * a column that is already complex. * * @param index The 0-based index of the column to be converted. * @return Whether the column can be converted * @throws FitsException */ public boolean setComplexColumn(int index) throws FitsException { boolean status = false; if (table.setComplexColumn(index)) { // No problem with the data. Make sure the header // is right. int[] dimens = table.getDimens()[index]; Class base = table.getBases()[index]; int dim = 1; String tdim = ""; String sep = ""; // Don't loop over all values. // The last is the [2] for the complex data. for (int i = 0; i < dimens.length - 1; i += 1) { dim *= dimens[i]; tdim = dimens[i] + sep + tdim; sep = ","; } String suffix = "C"; // For complex // Update the TFORMn keyword. if (base == double.class) { suffix = "M"; } // Worry about variable length columns. String prefix = ""; if (table.isVarCol(index)) { prefix = "P"; dim = 1; if (table.isLongVary(index)) { prefix = "Q"; } } // Now update the header. myHeader.findCard("TFORM" + (index + 1)); HeaderCard hc = myHeader.nextCard(); String oldComment = hc.getComment(); if (oldComment == null) { oldComment = "Column converted to complex"; } myHeader.addValue("TFORM" + (index + 1), dim + prefix + suffix, oldComment); if (tdim.length() > 0) { myHeader.addValue("TDIM" + (index + 1), "(" + tdim + ")", "ntf::binarytablehdu:tdimN:1"); } else { // Just in case there used to be a TDIM card that's no longer needed. myHeader.removeCard("TDIM" + (index + 1)); } status = true; } return status; } private void prtField(String type, String field) { String val = myHeader.getStringValue(field); if (val != null) { System.out.print(type + '=' + val + "; "); } } /** 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 + ":"); prtField("Name", "TTYPE" + i); prtField("Format", "TFORM" + i); prtField("Dimens", "TDIM" + i); System.out.println(""); } System.out.println(" Data Information:"); if (myData == null || table.getNRows() == 0 || table.getNCols() == 0) { System.out.println(" No data present"); if (table.getHeapSize() > 0) { System.out.println(" Heap size is: " + table.getHeapSize() + " bytes"); } } else { System.out.println(" Number of rows=" + table.getNRows()); System.out.println(" Number of columns=" + table.getNCols()); if (table.getHeapSize() > 0) { System.out.println(" Heap size is: " + table.getHeapSize() + " bytes"); } Object[] cols = table.getFlatColumns(); for (int i = 0; i < cols.length; i += 1) { System.out.println(" " + i + ":" + ArrayFuncs.arrayDescription(cols[i])); } } } /** What are the standard column stems for a binary table? */ public String[] columnKeyStems() { return keyStems; } } fits-1.10.0/nom/tam/fits/BinaryTable.java0000664000175000017500000015726312031033644020334 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import java.io.*; import nom.tam.util.*; import java.lang.reflect.Array; import java.util.Vector; /** This class defines the methods for accessing FITS binary table data. */ public class BinaryTable extends Data implements TableData { /** This is the area in which variable length column data lives. */ FitsHeap heap; /** The number of bytes between the end of the data and the heap */ int heapOffset; // Added by A. Kovacs (4/1/08) // as a way for checking whether the heap was initialized from stream... /** Has the heap been read */ boolean heapReadFromStream = false; /** The sizes of each column (in number of entries per row) */ int[] sizes; /** The dimensions of each column. * If a column is a scalar then entry for that * index is an array of length 0. */ int[][] dimens; /** Info about column */ int[] flags; /** Flag indicating that we've given Variable length conversion warning. * We only want to do that once per HDU. */ private boolean warnedOnVariableConversion = false; final static int COL_CONSTANT = 0; final static int COL_VARYING = 1; final static int COL_COMPLEX = 2; final static int COL_STRING = 4; final static int COL_BOOLEAN = 8; final static int COL_BIT = 16; final static int COL_LONGVARY = 32; /** The number of rows in the table. */ int nRow; /** The number of columns in the table. */ int nCol; /** The length in bytes of each row. */ int rowLen; /** The base classes for the arrays in the table. */ Class[] bases; /** An example of the structure of a row */ Object[] modelRow; /** A pointer to the data in the columns. This * variable is only used to assist in the * construction of BinaryTable's that are defined * to point to an array of columns. It is * not generally filled. The ColumnTable is used * to store the actual data of the BinaryTable. */ Object[] columns; /** Where the data is actually stored. */ ColumnTable table; /** The stream used to input the image */ ArrayDataInput currInput; /** Create a null binary table data segment. */ public BinaryTable() throws FitsException { try { table = new ColumnTable(new Object[0], new int[0]); } catch (TableException e) { System.err.println("Impossible exception in BinaryTable() constructor" + e); } heap = new FitsHeap(0); extendArrays(0); nRow = 0; nCol = 0; rowLen = 0; } /** Create a binary table from given header information. * * @param myHeader A header describing what the binary * table should look like. */ public BinaryTable(Header myHeader) throws FitsException { long heapSizeL = myHeader.getLongValue("PCOUNT"); long heapOffsetL = myHeader.getLongValue("THEAP"); if (heapOffsetL > Integer.MAX_VALUE) { throw new FitsException("Heap Offset > 2GB"); } heapOffset = (int) heapOffsetL; if (heapSizeL > Integer.MAX_VALUE) { throw new FitsException("Heap size > 2 GB"); } int heapSize = (int) heapSizeL; int rwsz = myHeader.getIntValue("NAXIS1"); nRow = myHeader.getIntValue("NAXIS2"); // Subtract out the size of the regular table from // the heap offset. if (heapOffset > 0) { heapOffset -= nRow * rwsz; } if (heapOffset < 0 || heapOffset > heapSize) { throw new FitsException("Inconsistent THEAP and PCOUNT"); } if (heapSize - heapOffset > Integer.MAX_VALUE) { throw new FitsException("Unable to allocate heap > 2GB"); } heap = new FitsHeap((heapSize - heapOffset)); nCol = myHeader.getIntValue("TFIELDS"); rowLen = 0; extendArrays(nCol); for (int col = 0; col < nCol; col += 1) { rowLen += processCol(myHeader, col); } HeaderCard card = myHeader.findCard("NAXIS1"); card.setValue(String.valueOf(rowLen)); myHeader.updateLine("NAXIS1", card); } /** Create a binary table from existing data in row order. * * @param data The data used to initialize the binary table. */ public BinaryTable(Object[][] data) throws FitsException { this(convertToColumns(data)); } /** Create a binary table from existing data in column order. */ public BinaryTable(Object[] o) throws FitsException { heap = new FitsHeap(0); modelRow = new Object[o.length]; extendArrays(o.length); for (int i = 0; i < o.length; i += 1) { addColumn(o[i]); } } /** Create a binary table from an existing ColumnTable */ public BinaryTable(ColumnTable tab) { nCol = tab.getNCols(); extendArrays(nCol); bases = tab.getBases(); sizes = tab.getSizes(); modelRow = new Object[nCol]; dimens = new int[nCol][]; // Set all flags to 0. flags = new int[nCol]; // Set the column dimension. Note that // we cannot distinguish an array of length 1 from a // scalar here: we assume a scalar. for (int col = 0; col < nCol; col += 1) { if (sizes[col] != 1) { dimens[col] = new int[]{sizes[col]}; } else { dimens[col] = new int[0]; } } for (int col = 0; col < nCol; col += 1) { modelRow[col] = ArrayFuncs.newInstance(bases[col], sizes[col]); } columns = null; table = tab; heap = new FitsHeap(0); rowLen = 0; for (int col = 0; col < nCol; col += 1) { rowLen += sizes[col] * ArrayFuncs.getBaseLength(tab.getColumn(col)); } heapOffset = 0; nRow = tab.getNRows(); } /** Return a row that may be used for direct i/o to the table. */ public Object[] getModelRow() { return modelRow; } /** Process one column from a FITS Header */ private int processCol(Header header, int col) throws FitsException { String tform = header.getStringValue("TFORM" + (col + 1)); if (tform == null) { throw new FitsException("Attempt to process column " + (col + 1) + " but no TFORMn found."); } tform = tform.trim(); String tdims = header.getStringValue("TDIM" + (col + 1)); if (tdims != null) { tdims = tdims.trim(); } char type = getTFORMType(tform); if (type == 'P' || type == 'Q') { flags[col] |= COL_VARYING; if (type == 'Q') { flags[col] |= COL_LONGVARY; } type = getTFORMVarType(tform); } int size = getTFORMLength(tform); // Handle the special size cases. // // Bit arrays (8 bits fit in a byte) if (type == 'X') { size = (size + 7) / 8; flags[col] |= COL_BIT; // Variable length arrays always have a two-element pointer (offset and size) } else if (isVarCol(col)) { size = 2; } // bSize is the number of bytes in the field. int bSize = size; int[] dims = null; // Cannot really handle arbitrary arrays of bits. if (tdims != null && type != 'X' && !isVarCol(col)) { dims = getTDims(tdims); } if (dims == null) { if (size == 1) { dims = new int[0]; // Marks this as a scalar column } else { dims = new int[]{size}; } } if (type == 'C' || type == 'M') { flags[col] |= COL_COMPLEX; } Class colBase = null; switch (type) { case 'A': colBase = byte.class; flags[col] |= COL_STRING; bases[col] = String.class; break; case 'L': colBase = byte.class; bases[col] = boolean.class; flags[col] |= COL_BOOLEAN; break; case 'X': case 'B': colBase = byte.class; bases[col] = byte.class; break; case 'I': colBase = short.class; bases[col] = short.class; bSize *= 2; break; case 'J': colBase = int.class; bases[col] = int.class; bSize *= 4; break; case 'K': colBase = long.class; bases[col] = long.class; bSize *= 8; break; case 'E': case 'C': colBase = float.class; bases[col] = float.class; bSize *= 4; break; case 'D': case 'M': colBase = double.class; bases[col] = double.class; bSize *= 8; break; default: throw new FitsException("Invalid type in column:" + col); } if (isVarCol(col)) { dims = new int[]{nRow, 2}; colBase = int.class; bSize = 8; if (isLongVary(col)) { colBase = long.class; bSize = 16; } } if (!isVarCol(col) && isComplex(col)) { int[] xdims = new int[dims.length + 1]; System.arraycopy(dims, 0, xdims, 0, dims.length); xdims[dims.length] = 2; dims = xdims; bSize *= 2; size *= 2; } modelRow[col] = ArrayFuncs.newInstance(colBase, dims); dimens[col] = dims; sizes[col] = size; return bSize; } /** Get the type in the TFORM field */ char getTFORMType(String tform) { for (int i = 0; i < tform.length(); i += 1) { if (!Character.isDigit(tform.charAt(i))) { return tform.charAt(i); } } return 0; } /** Get the type in a varying length column TFORM */ char getTFORMVarType(String tform) { int ind = tform.indexOf("P"); if (ind < 0) { ind = tform.indexOf("Q"); } if (tform.length() > ind + 1) { return tform.charAt(ind + 1); } else { return 0; } } /** Get the explicit or implied length of the TFORM field */ int getTFORMLength(String tform) { tform = tform.trim(); if (Character.isDigit(tform.charAt(0))) { return initialNumber(tform); } else { return 1; } } /** Get an unsigned number at the beginning of a string */ private int initialNumber(String tform) { int i; for (i = 0; i < tform.length(); i += 1) { if (!Character.isDigit(tform.charAt(i))) { break; } } return Integer.parseInt(tform.substring(0, i)); } /** Parse the TDIMS value. * * If the TDIMS value cannot be deciphered a one-d * array with the size given in arrsiz is returned. * * @param tdims The value of the TDIMSn card. * @return An int array of the desired dimensions. * Note that the order of the tdims is the inverse * of the order in the TDIMS key. */ public static int[] getTDims(String tdims) { // The TDIMs value should be of the form: "(iiii,jjjj,kkk,...)" int[] dims = null; int first = tdims.indexOf('('); int last = tdims.lastIndexOf(')'); if (first >= 0 && last > first) { tdims = tdims.substring(first + 1, last - first); java.util.StringTokenizer st = new java.util.StringTokenizer(tdims, ","); int dim = st.countTokens(); if (dim > 0) { dims = new int[dim]; for (int i = dim - 1; i >= 0; i -= 1) { dims[i] = Integer.parseInt(st.nextToken().trim()); } } } return dims; } /** Convert a column from float/double to float complex/double complex. * This is only possible for certain columns. The return status * indicates if the conversion is possible. * @param index The 0-based index of the column to be reset. * @return Whether the conversion is possible. */ boolean setComplexColumn(int index) throws FitsException { // Currently there is almost no change required to the BinaryTable // object itself when we convert an eligible column to complex, since the internal // representation of the data is unchanged. We just need // to set the flag that the column is complex. // Check that the index is valid, // the data type is float or double // the most rapidly changing index in the array has dimension 2. if (index >= 0 && index < bases.length && (bases[index] == float.class || bases[index] == double.class) && dimens[index][dimens[index].length - 1] == 2) { // By coincidence a variable length column will also have // a last index of 2, so we'll get here. Otherwise // we'd need to test that in parallel rather than in series. // If this is a variable length column, then // we need to check the length of each row. if ((flags[index] & COL_VARYING) != 0) { // We need to make sure that for every row, there are // an even number of elements so that we can // convert to an integral number of complex numbers. Object col = getFlattenedColumn(index); if (col instanceof int[]) { int[] ptrs = (int[]) col; for (int i = 1; i < ptrs.length; i += 2) { if (ptrs[i] % 2 != 0) { return false; } } } else { long[] ptrs = (long[]) col; for (int i = 1; i < ptrs.length; i += 1) { if (ptrs[i] % 2 != 0) { return false; } } } } // Set the column to complex flags[index] |= COL_COMPLEX; return true; } return false; } /** Update a FITS header to reflect the current state of the data. */ public void fillHeader(Header h) throws FitsException { try { h.setXtension("BINTABLE"); h.setBitpix(8); h.setNaxes(2); h.setNaxis(1, rowLen); h.setNaxis(2, nRow); h.addValue("PCOUNT", heap.size(), "ntf::binarytable:pcount:1"); h.addValue("GCOUNT", 1, "ntf::binarytable:gcount:1"); Cursor iter = h.iterator(); iter.setKey("GCOUNT"); iter.next(); iter.add("TFIELDS", new HeaderCard("TFIELDS", modelRow.length, "ntf::binarytable:tfields:1")); for (int i = 0; i < modelRow.length; i += 1) { if (i > 0) { h.positionAfterIndex("TFORM", i); } fillForColumn(h, i, iter); } } catch (HeaderCardException e) { System.err.println("Error updating BinaryTableHeader:" + e); } } /** Updata the header to reflect information about a given column. * This routine tries to ensure that the Header is organized by column. */ void pointToColumn(int col, Header hdr) throws FitsException { Cursor iter = hdr.iterator(); if (col > 0) { hdr.positionAfterIndex("TFORM", col); } fillForColumn(hdr, col, iter); } /** Update the header to reflect the details of a given column */ void fillForColumn(Header h, int col, Cursor iter) throws FitsException { String tform; if (isVarCol(col)) { if (isLongVary(col)) { tform = "1Q"; } else { tform = "1P"; } } else { tform = "" + sizes[col]; } if (bases[col] == int.class) { tform += "J"; } else if (bases[col] == short.class || bases[col] == char.class) { tform += "I"; } else if (bases[col] == byte.class) { tform += "B"; } else if (bases[col] == float.class) { if (isComplex(col)) { tform += "C"; } else { tform += "E"; } } else if (bases[col] == double.class) { if (isComplex(col)) { tform += "M"; } else { tform += "D"; } } else if (bases[col] == long.class) { tform += "K"; } else if (bases[col] == boolean.class) { tform += "L"; } else if (bases[col] == String.class) { tform += "A"; } else { throw new FitsException("Invalid column data class:" + bases[col]); } String key = "TFORM" + (col + 1); iter.add(key, new HeaderCard(key, tform, "ntf::binarytable:tformN:1")); if (dimens[col].length > 0 && !isVarCol(col)) { StringBuffer tdim = new StringBuffer(); char comma = '('; for (int i = dimens[col].length - 1; i >= 0; i -= 1) { tdim.append(comma); tdim.append(dimens[col][i]); comma = ','; } tdim.append(')'); key = "TDIM" + (col + 1); iter.add(key, new HeaderCard(key, new String(tdim), "ntf::headercard:tdimN:1")); } } /** Create a column table given the number of * rows and a model row. This is used when * we defer instantiation of the ColumnTable until * the user requests data from the table. */ private ColumnTable createTable() throws FitsException { int nfields = modelRow.length; Object[] arrCol = new Object[nfields]; for (int i = 0; i < nfields; i += 1) { arrCol[i] = ArrayFuncs.newInstance( ArrayFuncs.getBaseClass(modelRow[i]), sizes[i] * nRow); } ColumnTable table; try { table = new ColumnTable(arrCol, sizes); } catch (TableException e) { throw new FitsException("Unable to create table:" + e); } return table; } /** Convert a two-d table to a table of columns. Handle * String specially. Every other element of data should be * a primitive array of some dimensionality. */ private static Object[] convertToColumns(Object[][] data) { Object[] row = data[0]; int nrow = data.length; Object[] results = new Object[row.length]; for (int col = 0; col < row.length; col += 1) { if (row[col] instanceof String) { String[] sa = new String[nrow]; for (int irow = 0; irow < nrow; irow += 1) { sa[irow] = (String) data[irow][col]; } results[col] = sa; } else { Class base = ArrayFuncs.getBaseClass(row[col]); int[] dims = ArrayFuncs.getDimensions(row[col]); if (dims.length > 1 || dims[0] > 1) { int[] xdims = new int[dims.length + 1]; xdims[0] = nrow; Object[] arr = (Object[]) ArrayFuncs.newInstance(base, xdims); for (int irow = 0; irow < nrow; irow += 1) { arr[irow] = data[irow][col]; } results[col] = arr; } else { Object arr = ArrayFuncs.newInstance(base, nrow); for (int irow = 0; irow < nrow; irow += 1) { System.arraycopy(data[irow][col], 0, arr, irow, 1); } results[col] = arr; } } } return results; } /** Get a given row * @param row The index of the row to be returned. * @return A row of data. */ public Object[] getRow(int row) throws FitsException { if (!validRow(row)) { throw new FitsException("Invalid row"); } Object[] res; if (table != null) { res = getMemoryRow(row); } else { res = getFileRow(row); } return res; } /** Get a row from memory. */ private Object[] getMemoryRow(int row) throws FitsException { Object[] data = new Object[modelRow.length]; for (int col = 0; col < modelRow.length; col += 1) { Object o = table.getElement(row, col); o = columnToArray(col, o, 1); data[col] = encurl(o, col, 1); if (data[col] instanceof Object[]) { data[col] = ((Object[]) data[col])[0]; } } return data; } /** Get a row from the file. */ private Object[] getFileRow(int row) throws FitsException { /** Read the row from memory */ Object[] data = new Object[nCol]; for (int col = 0; col < data.length; col += 1) { data[col] = ArrayFuncs.newInstance( ArrayFuncs.getBaseClass(modelRow[col]), sizes[col]); } try { FitsUtil.reposition(currInput, fileOffset + row * rowLen); currInput.readLArray(data); } catch (IOException e) { throw new FitsException("Error in deferred row read"); } for (int col = 0; col < data.length; col += 1) { data[col] = columnToArray(col, data[col], 1); data[col] = encurl(data[col], col, 1); if (data[col] instanceof Object[]) { data[col] = ((Object[]) data[col])[0]; } } return data; } /** Replace a row in the table. * @param row The index of the row to be replaced. * @param data The new values for the row. * @exception FitsException Thrown if the new row cannot * match the existing data. */ public void setRow(int row, Object data[]) throws FitsException { if (table == null) { getData(); } if (data.length != getNCols()) { throw new FitsException("Updated row size does not agree with table"); } Object[] ydata = new Object[data.length]; for (int col = 0; col < data.length; col += 1) { Object o = ArrayFuncs.flatten(data[col]); ydata[col] = arrayToColumn(col, o); } try { table.setRow(row, ydata); } catch (TableException e) { throw new FitsException("Error modifying table: " + e); } } /** Replace a column in the table. * @param col The index of the column to be replaced. * @param xcol The new data for the column * @exception FitsException Thrown if the data does not match * the current column description. */ public void setColumn(int col, Object xcol) throws FitsException { xcol = arrayToColumn(col, xcol); xcol = ArrayFuncs.flatten(xcol); setFlattenedColumn(col, xcol); } /** Set a column with the data aleady flattened. * * @param col The index of the column to be replaced. * @param data The new data array. This should be a one-d * primitive array. * @exception FitsException Thrown if the type of length of * the replacement data differs from the * original. */ public void setFlattenedColumn(int col, Object data) throws FitsException { if (table == null) { getData(); } Object oldCol = table.getColumn(col); if (data.getClass() != oldCol.getClass() || Array.getLength(data) != Array.getLength(oldCol)) { throw new FitsException("Replacement column mismatch at column:" + col); } try { table.setColumn(col, data); } catch (TableException e) { throw new FitsException("Unable to set column:" + col + " error:" + e); } } /** Get a given column * @param col The index of the column. */ public Object getColumn(int col) throws FitsException { if (table == null) { getData(); } Object res = getFlattenedColumn(col); res = encurl(res, col, nRow); return res; } private Object encurl(Object res, int col, int rows) { if (bases[col] != String.class) { if (!isVarCol(col) && (dimens[col].length > 0)) { int[] dims = new int[dimens[col].length + 1]; System.arraycopy(dimens[col], 0, dims, 1, dimens[col].length); dims[0] = rows; res = ArrayFuncs.curl(res, dims); } } else { // Handle Strings. Remember the last element // in dimens is the length of the Strings and // we already used that when we converted from // byte arrays to strings. So we need to ignore // the last element of dimens, and add the row count // at the beginning to curl. if (dimens[col].length > 2) { int[] dims = new int[dimens[col].length]; System.arraycopy(dimens[col], 0, dims, 1, dimens[col].length - 1); dims[0] = rows; res = ArrayFuncs.curl(res, dims); } } return res; } /** Get a column in flattened format. * For large tables getting a column in standard format can be * inefficient because a separate object is needed for * each row. Leaving the data in flattened format means * that only a single object is created. * @param col */ public Object getFlattenedColumn(int col) throws FitsException { if (table == null) { getData(); } if (!validColumn(col)) { throw new FitsException("Invalid column"); } Object res = table.getColumn(col); return columnToArray(col, res, nRow); } /** Get a particular element from the table. * @param i The row of the element. * @param j The column of the element. */ public Object getElement(int i, int j) throws FitsException { if (!validRow(i) || !validColumn(j)) { throw new FitsException("No such element"); } Object ele; if (isVarCol(j) && table == null) { // Have to read in entire data set. getData(); } if (table == null) { // This is really inefficient. // Need to either save the row, or just read the one element. Object[] row = getRow(i); ele = row[j]; } else { ele = table.getElement(i, j); ele = columnToArray(j, ele, 1); ele = encurl(ele, j, 1); if (ele instanceof Object[]) { ele = ((Object[]) ele)[0]; } } return ele; } /** Get a particular element from the table but * do no processing of this element (e.g., * dimension conversion or extraction of * variable length array elements/) * @param i The row of the element. * @param j The column of the element. */ public Object getRawElement(int i, int j) throws FitsException { if (table == null) { getData(); } return table.getElement(i, j); } /** Add a row at the end of the table. Given the way the * table is structured this will normally not be very efficient. * @param o An array of elements to be added. Each element of o * should be an array of primitives or a String. */ public int addRow(Object[] o) throws FitsException { if (table == null) { getData(); } if (nCol == 0 && nRow == 0) { for (int i = 0; i < o.length; i += 1) { addColumn(o); } } else { Object[] flatRow = new Object[getNCols()]; for (int i = 0; i < getNCols(); i += 1) { Object x = ArrayFuncs.flatten(o[i]); flatRow[i] = arrayToColumn(i, x); } try { table.addRow(flatRow); } catch (TableException e) { throw new FitsException("Error add row to table"); } nRow += 1; } return nRow; } /** Delete rows from a table. * @param row The 0-indexed start of the rows to be deleted. * @param len The number of rows to be deleted. */ public void deleteRows(int row, int len) throws FitsException { try { getData(); table.deleteRows(row, len); nRow -= len; } catch (TableException e) { throw new FitsException("Error deleting row block " + row + " to " + (row + len - 1) + " from table"); } } /** Add a column to the end of a table. * @param o An array of identically structured objects with the * same number of elements as other columns in the table. */ public int addColumn(Object o) throws FitsException { int primeDim = Array.getLength(o); extendArrays(nCol + 1); Class base = ArrayFuncs.getBaseClass(o); // A varying length column is a two-d primitive // array where the second index is not constant. // We do not support Q types here, since Java // can't handle the long indices anyway... // This will probably change in some version of Java. if (isVarying(o)) { flags[nCol] |= COL_VARYING; dimens[nCol] = new int[]{2}; } if (isVaryingComp(o)) { flags[nCol] |= COL_VARYING | COL_COMPLEX; dimens[nCol] = new int[]{2}; } // Flatten out everything but 1-D arrays and the // two-D arrays associated with variable length columns. if (!isVarCol(nCol)) { int[] allDim = ArrayFuncs.getDimensions(o); // Add a dimension for the length of Strings. if (base == String.class) { int[] xdim = new int[allDim.length + 1]; System.arraycopy(allDim, 0, xdim, 0, allDim.length); xdim[allDim.length] = -1; allDim = xdim; } if (allDim.length == 1) { dimens[nCol] = new int[0]; } else { dimens[nCol] = new int[allDim.length - 1]; System.arraycopy(allDim, 1, dimens[nCol], 0, allDim.length - 1); o = ArrayFuncs.flatten(o); } } addFlattenedColumn(o, dimens[nCol]); if (nRow == 0 && nCol == 0) { nRow = primeDim; } nCol += 1; return getNCols(); } private boolean isVaryingComp(Object o) { String classname = o.getClass().getName(); if (classname.equals("[[[F")) { return checkCompVary((float[][][]) o); } else if (classname.equals("[[[D")) { return checkDCompVary((double[][][]) o); } return false; } /** Is this a variable length column? * It is if it's a two-d primitive array and * the second dimension is not constant. * It may also be a 3-d array of type float or double * where the last index is always 2 (when the second index * is non-zero). In this case it can be * a complex varying column. */ private boolean isVarying(Object o) { if (o == null) { return false; } String classname = o.getClass().getName(); if (classname.length() != 3 || classname.charAt(0) != '[' || classname.charAt(1) != '[') { return false; } Object[] ox = (Object[]) o; if (ox.length < 2) { return false; } int flen = Array.getLength(ox[0]); for (int i = 1; i < ox.length; i += 1) { if (Array.getLength(ox[i]) != flen) { return true; } } return false; } // Check if this is consistent with a varying // complex row. That requires // The second index varies. // The third index is 2 whenever the second // index is non-zero. // This function will fail if nulls are encountered. private boolean checkCompVary(float[][][] o) { boolean varying = false; int len0 = o[0].length; for (int i = 0; i < o.length; i += 1) { if (o[i].length != len0) { varying = true; } if (o[i].length > 0) { for (int j = 0; j < o[i].length; j += 1) { if (o[i][j].length != 2) { return false; } } } } return varying; } private boolean checkDCompVary(double[][][] o) { boolean varying = false; int len0 = o[0].length; for (int i = 0; i < o.length; i += 1) { if (o[i].length != len0) { varying = true; } if (o[i].length > 0) { for (int j = 0; j < o[i].length; j += 1) { if (o[i][j].length != 2) { return false; } } } } return varying; } /** Add a column where the data is already flattened. * @param o The new column data. This should be a one-dimensional * primitive array. * @param dims The dimensions of one row of the column. */ public int addFlattenedColumn(Object o, int[] dims) throws FitsException { extendArrays(nCol + 1); bases[nCol] = ArrayFuncs.getBaseClass(o); if (bases[nCol] == boolean.class) { flags[nCol] |= COL_BOOLEAN; } else if (bases[nCol] == String.class) { flags[nCol] |= COL_STRING; } // Convert to column first in case // this is a String or variable length array. o = arrayToColumn(nCol, o); int size = 1; for (int dim = 0; dim < dims.length; dim += 1) { size *= dims[dim]; } sizes[nCol] = size; if (size != 0) { int xRow = Array.getLength(o) / size; if (xRow > 0 && nCol != 0 && xRow != nRow) { throw new FitsException("Added column does not have correct row count"); } } if (!isVarCol(nCol)) { modelRow[nCol] = ArrayFuncs.newInstance(ArrayFuncs.getBaseClass(o), dims); rowLen += size * ArrayFuncs.getBaseLength(o); } else { if (isLongVary(nCol)) { modelRow[nCol] = new long[2]; rowLen += 16; } else { modelRow[nCol] = new int[2]; rowLen += 8; } } // Only add to table if table already exists or if we // are filling up the last element in columns. // This way if we allocate a bunch of columns at the beginning // we only create the column table after we have all the columns // ready. columns[nCol] = o; try { if (table != null) { table.addColumn(o, sizes[nCol]); } else if (nCol == columns.length - 1) { table = new ColumnTable(columns, sizes); } } catch (TableException e) { throw new FitsException("Error in ColumnTable:" + e); } return nCol; } /** Get the number of rows in the table */ public int getNRows() { return nRow; } /** Get the number of columns in the table. */ public int getNCols() { return nCol; } /** Check to see if this is a valid row. * @param i The Java index (first=0) of the row to check. */ protected boolean validRow(int i) { if (getNRows() > 0 && i >= 0 && i < getNRows()) { return true; } else { return false; } } /** Check if the column number is valid. * * @param j The Java index (first=0) of the column to check. */ protected boolean validColumn(int j) { return (j >= 0 && j < getNCols()); } /** Replace a single element within the table. * * @param i The row of the data. * @param j The column of the data. * @param o The replacement data. */ public void setElement(int i, int j, Object o) throws FitsException { getData(); try { if (isVarCol(j)) { int size = Array.getLength(o); // The offset for the row is the offset to the heap plus the offset within the heap. int offset = (int) heap.getSize(); heap.putData(o); if (isLongVary(j)) { table.setElement(i, j, new long[]{size, offset}); } else { table.setElement(i, j, new int[]{size, offset}); } } else { table.setElement(i, j, ArrayFuncs.flatten(o)); } } catch (TableException e) { throw new FitsException("Error modifying table:" + e); } } /** Read the data -- or defer reading on random access */ public void read(ArrayDataInput i) throws FitsException { setFileOffset(i); currInput = i; if (i instanceof RandomAccess) { try { i.skipBytes(getTrueSize()); } catch (IOException e) { throw new FitsException("Unable to skip binary table HDU:" + e); } try { i.skipBytes(FitsUtil.padding(getTrueSize())); } catch (EOFException e) { throw new PaddingException("Missing padding after binary table:" + e, this); } catch (IOException e) { throw new FitsException("Error skipping padding after binary table:" + e); } } else { /** Read the data associated with the HDU including the hash area if present. * @param i The input stream */ if (table == null) { table = createTable(); } readTrueData(i); } } /** Read table, heap and padding */ protected void readTrueData(ArrayDataInput i) throws FitsException { try { table.read(i); i.skipBytes(heapOffset); heap.read(i); heapReadFromStream = true; } catch (IOException e) { throw new FitsException("Error reading binary table data:" + e); } try { i.skipBytes(FitsUtil.padding(getTrueSize())); } catch (EOFException e) { throw new PaddingException("Error skipping padding after binary table", this); } catch (IOException e) { throw new FitsException("Error reading binary table data padding:" + e); } } /** Read the heap which contains the data for variable length * arrays. * A. Kovacs (4/1/08) Separated heap reading, s.t. the heap can * be properly initialized even if in deferred read mode. * columnToArray() checks and initializes the heap as necessary. */ protected void readHeap(ArrayDataInput input) throws FitsException { FitsUtil.reposition(input, fileOffset + nRow * rowLen + heapOffset); heap.read(input); heapReadFromStream = true; } /** Get the size of the data in the HDU sans padding. */ public long getTrueSize() { long len = ((long) nRow) * rowLen; if (heap.size() > 0) { len += heap.size() + heapOffset; } return len; } /** Write the table, heap and padding */ public void write(ArrayDataOutput os) throws FitsException { getData(); int len; try { // First write the table. len = table.write(os); if (heapOffset > 0) { int off = heapOffset; // Minimize memory usage. This also accommodates // the possibility that heapOffset > 2GB. // Previous code might have allocated up to 2GB // array. [In practice this is always going // to be really small though...] int arrSiz = 4000000; while (off > 0) { if (arrSiz > off) { arrSiz = (int) off; } os.write(new byte[arrSiz]); off -= arrSiz; } } // Now check if we need to write the heap if (heap.size() > 0) { heap.write(os); } FitsUtil.pad(os, getTrueSize()); } catch (IOException e) { throw new FitsException("Unable to write table:" + e); } } public Object getData() throws FitsException { if (table == null) { if (currInput == null) { throw new FitsException("Cannot find input for deferred read"); } table = createTable(); long currentOffset = FitsUtil.findOffset(currInput); FitsUtil.reposition(currInput, fileOffset); readTrueData(input); FitsUtil.reposition(currInput, currentOffset); } return table; } public int[][] getDimens() { return dimens; } public Class[] getBases() { return table.getBases(); } public char[] getTypes() { if (table == null) { try { getData(); } catch (FitsException e) { } } return table.getTypes(); } public Object[] getFlatColumns() { if (table == null) { try { getData(); } catch (FitsException e) { } } return table.getColumns(); } public int[] getSizes() { return sizes; } /** Convert the external representation to the * BinaryTable representation. Transformation include * boolean -> T/F, Strings -> byte arrays, * variable length arrays -> pointers (after writing data * to heap). */ private Object arrayToColumn(int col, Object o) throws FitsException { if (flags[col] == 0) { return o; } if (!isVarCol(col)) { if (isString(col)) { // Convert strings to array of bytes. int[] dims = dimens[col]; // Set the length of the string if we are just adding the column. if (dims[dims.length - 1] < 0) { dims[dims.length - 1] = FitsUtil.maxLength((String[]) o); } if (o instanceof String) { o = new String[]{(String) o}; } o = FitsUtil.stringsToByteArray((String[]) o, dims[dims.length - 1]); } else if (isBoolean(col)) { // Convert true/false to 'T'/'F' o = FitsUtil.booleanToByte((boolean[]) o); } } else { if (isBoolean(col)) { // Handle addRow/addElement if (o instanceof boolean[]) { o = new boolean[][]{(boolean[]) o}; } // Convert boolean to byte arrays boolean[][] to = (boolean[][]) o; byte[][] xo = new byte[to.length][]; for (int i = 0; i < to.length; i += 1) { xo[i] = FitsUtil.booleanToByte(to[i]); } o = xo; } // Write all rows of data onto the heap. int offset = heap.putData(o); int blen = ArrayFuncs.getBaseLength(o); // Handle an addRow of a variable length element. // In this case we only get a one-d array, but we just // make is 1 x n to get the second dimension. if (!(o instanceof Object[])) { o = new Object[]{o}; } // Create the array descriptors int nrow = Array.getLength(o); int factor = 1; if (isComplex(col)) { factor = 2; } if (isLongVary(col)) { long[] descrip = new long[2 * nrow]; Object[] x = (Object[]) o; // Fill the descriptor for each row. for (int i = 0; i < nrow; i += 1) { int len = Array.getLength(x[i]); descrip[2 * i] = len; descrip[2 * i + 1] = offset; offset += len * blen * factor; } o = descrip; } else { int[] descrip = new int[2 * nrow]; Object[] x = (Object[]) o; // Fill the descriptor for each row. for (int i = 0; i < nrow; i += 1) { int len = Array.getLength(x[i]); descrip[2 * i] = len; descrip[2 * i + 1] = offset; offset += len * blen * factor; } o = descrip; } } return o; } /** Convert data from binary table representation to external * Java representation. */ private Object columnToArray(int col, Object o, int rows) throws FitsException { // Most of the time we need do nothing! if (flags[col] == 0) { return o; } // If a varying length column use the descriptors to // extract appropriate information from the headers. if (isVarCol(col)) { // A. Kovacs (4/1/08) // Ensure that the heap has been initialized if (!heapReadFromStream) { readHeap(currInput); } int[] descrip; if (isLongVary(col)) { // Convert longs to int's. This is dangerous. if (!warnedOnVariableConversion) { System.err.println("Warning: converting long variable array pointers to int's"); warnedOnVariableConversion = true; } descrip = (int[]) ArrayFuncs.convertArray(o, int.class); } else { descrip = (int[]) o; } int nrow = descrip.length / 2; Object[] res; // Res will be the result of extracting from the heap. int[] dims; // Used to create result arrays. if (isComplex(col)) { // Complex columns have an extra dimension for each row dims = new int[]{nrow, 0, 0}; res = (Object[]) ArrayFuncs.newInstance(bases[col], dims); // Set up dims for individual rows. dims = new int[2]; dims[1] = 2; // ---> Added clause by Attila Kovacs (13 July 2007) // String columns have to read data into a byte array at first // then do the string conversion later. } else if (isString(col)) { dims = new int[]{nrow, 0}; res = (Object[]) ArrayFuncs.newInstance(byte.class, dims); } else { // Non-complex data has a simple primitive array for each row dims = new int[]{nrow, 0}; res = (Object[]) ArrayFuncs.newInstance(bases[col], dims); } // Now read in each requested row. for (int i = 0; i < nrow; i += 1) { Object row; int offset = descrip[2 * i + 1]; int dim = descrip[2 * i]; if (isComplex(col)) { dims[0] = dim; row = ArrayFuncs.newInstance(bases[col], dims); // ---> Added clause by Attila Kovacs (13 July 2007) // Again, String entries read data into a byte array at first // then do the string conversion later. } else if (isString(col)) { // For string data, we need to read bytes and convert // to strings row = ArrayFuncs.newInstance(byte.class, dim); } else if (isBoolean(col)) { // For boolean data, we need to read bytes and convert // to booleans. row = ArrayFuncs.newInstance(byte.class, dim); } else { row = ArrayFuncs.newInstance(bases[col], dim); } heap.getData(offset, row); // Now do the boolean conversion. if (isBoolean(col)) { row = FitsUtil.byteToBoolean((byte[]) row); } res[i] = row; } o = res; } else { // Fixed length columns // Need to convert String byte arrays to appropriate Strings. if (isString(col)) { int[] dims = dimens[col]; byte[] bytes = (byte[]) o; if (bytes.length > 0) { if (dims.length > 0) { o = FitsUtil.byteArrayToStrings(bytes, dims[dims.length - 1]); } else { o = FitsUtil.byteArrayToStrings(bytes, 1); } } else { // This probably fails for multidimensional arrays of strings where // all elements are null. String[] str = new String[rows]; for (int i = 0; i < str.length; i += 1) { str[i] = ""; } o = str; } } else if (isBoolean(col)) { o = FitsUtil.byteToBoolean((byte[]) o); } } return o; } /** Make sure the arrays which describe the columns are * long enough, and if not extend them. */ private void extendArrays(int need) { boolean wasNull = false; if (sizes == null) { wasNull = true; } else if (sizes.length > need) { return; } // Allocate the arrays. int[] newSizes = new int[need]; int[][] newDimens = new int[need][]; int[] newFlags = new int[need]; Object[] newModel = new Object[need]; Object[] newColumns = new Object[need]; Class[] newBases = new Class[need]; if (!wasNull) { int len = sizes.length; System.arraycopy(sizes, 0, newSizes, 0, len); System.arraycopy(dimens, 0, newDimens, 0, len); System.arraycopy(flags, 0, newFlags, 0, len); System.arraycopy(modelRow, 0, newModel, 0, len); System.arraycopy(columns, 0, newColumns, 0, len); System.arraycopy(bases, 0, newBases, 0, len); } sizes = newSizes; dimens = newDimens; flags = newFlags; modelRow = newModel; columns = newColumns; bases = newBases; } /** What is the size of the heap -- including the offset from the end of the * table data. */ public int getHeapSize() { return heapOffset + heap.size(); } /** What is the offset to the heap */ public int getHeapOffset() { return heapOffset; } /** Does this column have variable length arrays? */ boolean isVarCol(int col) { return (flags[col] & COL_VARYING) != 0; } /** Does this column have variable length arrays? */ boolean isLongVary(int col) { return (flags[col] & COL_LONGVARY) != 0; } /** Is this column a string column */ private boolean isString(int col) { return (flags[col] & COL_STRING) != 0; } /** Is this column complex? */ private boolean isComplex(int col) { return (flags[col] & COL_COMPLEX) != 0; } /** Is this column a boolean column */ private boolean isBoolean(int col) { return (flags[col] & COL_BOOLEAN) != 0; } /** Is this column a bit column */ private boolean isBit(int col) { return (flags[col] & COL_BOOLEAN) != 0; } /** Delete a set of columns. Note that this * does not fix the header, so users should normally * call the routine in TableHDU. */ public void deleteColumns(int start, int len) throws FitsException { getData(); try { rowLen = table.deleteColumns(start, len); nCol -= len; } catch (Exception e) { throw new FitsException("Error deleting columns from BinaryTable:" + e); } } /** Update the header after a deletion. */ public void updateAfterDelete(int oldNcol, Header hdr) throws FitsException { hdr.addValue("NAXIS1", rowLen, "ntf::binarytable:naxis1:1"); } } fits-1.10.0/nom/tam/fits/TableData.java0000664000175000017500000000362512031033644017751 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This class allows FITS binary and ASCII tables to * be accessed via a common interface. */ public interface TableData { public abstract Object[] getRow (int row) throws FitsException; public abstract Object getColumn (int col) throws FitsException; public abstract Object getElement(int row, int col) throws FitsException; public abstract void setRow (int row, Object[] newRow) throws FitsException; public abstract void setColumn (int col, Object newCol) throws FitsException; public abstract void setElement (int row, int col, Object element) throws FitsException; public abstract int addRow (Object[] newRow) throws FitsException; public abstract int addColumn(Object newCol) throws FitsException; public abstract void deleteRows(int row, int len) throws FitsException; public abstract void deleteColumns(int row, int len) throws FitsException; public abstract void updateAfterDelete(int oldNcol, Header hdr) throws FitsException; public abstract int getNCols(); public abstract int getNRows(); } fits-1.10.0/nom/tam/fits/utilities/0000775000175000017500000000000011566662576017322 5ustar frothmaifrothmaifits-1.10.0/nom/tam/fits/utilities/FitsReader.java0000664000175000017500000000270012031033644022164 0ustar frothmaifrothmaipackage nom.tam.fits.utilities; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import nom.tam.fits.*; public class FitsReader { public static void main(String[] args) throws Exception { String file = args[0]; Fits f = new Fits(file); int i = 0; BasicHDU h; do { h = f.readHDU(); if (h != null) { if (i == 0) { System.out.println("\n\nPrimary header:\n"); } else { System.out.println("\n\nExtension " + i + ":\n"); } i += 1; h.info(); } } while (h != null); } } fits-1.10.0/nom/tam/fits/utilities/FitsCopy.java0000664000175000017500000000307112031033644021676 0ustar frothmaifrothmaipackage nom.tam.fits.utilities; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import nom.tam.fits.*; import nom.tam.util.*; public class FitsCopy { public static void main(String[] args) throws Exception { String file = args[0]; Fits f = new Fits(file); int i = 0; BasicHDU h; do { h = f.readHDU(); if (h != null) { if (i == 0) { System.out.println("\n\nPrimary header:\n"); } else { System.out.println("\n\nExtension " + i + ":\n"); } i += 1; h.info(); } } while (h != null); BufferedFile bf = new BufferedFile(args[1], "rw"); f.write(bf); bf.close(); } } fits-1.10.0/nom/tam/fits/Header.java0000664000175000017500000012605012031033644017316 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import java.io.*; import java.util.*; import nom.tam.util.RandomAccess; import nom.tam.util.*; /** This class describes methods to access and manipulate the header * for a FITS HDU. This class does not include code specific * to particular types of HDU. * * As of version 1.1 this class supports the long keyword convention * which allows long string keyword values to be split among multiple * keywords *

 *    KEY        = 'ABC&'   /A comment
 *    CONTINUE      'DEF&'  / Another comment
 *    CONTINUE      'GHIJKL '
 * 
* The methods getStringValue(key), addValue(key,value,comment) * and deleteCard(key) will get, create/update and delete long string * values if the longStringsEnabled flag is set. This flag is set * automatically when a FITS header with a LONGSTRN card is found. * The value is not checked. It may also be set/unset using the * static method setLongStringsEnabled(boolean). [So if a user wishes to ensure * that it is not set, it should be unset after any header is read] * When long strings are found in the FITS header users should be careful not to interpose * new header cards within a long value sequence. * * When writing long strings, the comment is included in the last * card. If a user is writing long strings, a the keyword * LONGSTRN = 'OGIP 1.0' * should be added to the FITS header, but this is not done automatically * for the user. */ public class Header implements FitsElement { /** The actual header data stored as a HashedList of * HeaderCard's. */ private HashedList cards = new HashedList(); /** This iterator allows one to run through the list. */ private Cursor iter = cards.iterator(0); /** Offset of this Header in the FITS file */ private long fileOffset = -1; private List duplicates; /** Input descriptor last time header was read */ private ArrayDataInput input; /** Number of cards in header before duplicates were removed. * A user may want to know how large the actual FITS header was * on input. Since the keyword hash removes duplicate keys * the internal size may be smaller. * * Added by Booth Hartley (IPAC/Caltech). */ private int originalCardCount = 0; //RBH ADDED /** Create an empty header */ public Header() { } /** Do we support long strings when reading/writing keywords */ private static boolean longStringsEnabled = false; public static void setLongStringsEnabled(boolean flag) { longStringsEnabled = flag; } public static boolean getLongStringsEnabled() { return longStringsEnabled; } /** Create a header and populate it from the input stream * @param is The input stream where header information is expected. */ public Header(ArrayDataInput is) throws TruncatedFileException, IOException { read(is); } /** Create a header and initialize it with a vector of strings. * @param newCards Card images to be placed in the header. */ public Header(String[] newCards) { for (int i = 0; i < newCards.length; i += 1) { HeaderCard card = new HeaderCard(newCards[i]); if (card.getValue() == null) { cards.add(card); } else { cards.add(card.getKey(), card); } } } /** Create a header which points to the * given data object. * @param o The data object to be described. * @exception FitsException if the data was not valid for this header. */ public Header(Data o) throws FitsException { o.fillHeader(this); } /** Create the data element corresponding to the current header */ public Data makeData() throws FitsException { return FitsFactory.dataFactory(this); } /** Get the size of the original header in bytes. * */ public long getOriginalSize() { return FitsUtil.addPadding(originalCardCount * 80); } /** Indicate that we can use the current internal size of the * Header as the 'original' size (e.g., perhaps we've rewritten the * header to disk). Note that affects the results of * rewriteable(), so users should not call this method unless the * underlying data has actually been updated. */ public void resetOriginalSize() { originalCardCount = cards.size(); } /** * Update a line in the header * @param key The key of the card to be replaced. * @param card A new card */ public void updateLine(String key, HeaderCard card) throws HeaderCardException { removeCard(key); iter.add(key, card); } /** * Overwrite the lines in the header. * Add the new PHDU header to the current one. If keywords appear * twice, the new value and comment overwrite the current contents. * * @param newHdr the list of new header data lines to replace the current * ones. * @throws nom.tam.fits.HeaderCardException * @author Richard J Mathar * @since 2005-10-24 */ public void updateLines(final Header newHdr) throws nom.tam.fits.HeaderCardException { Cursor j = newHdr.iterator(); while (j.hasNext()) { HeaderCard nextHCard = (HeaderCard) j.next(); // updateLine() doesn't work with COMMENTs because // this would allow only one COMMENT in total in each header if (nextHCard.getKey().startsWith("COMMENT")) { insertComment(nextHCard.getComment()); } else { updateLine(nextHCard.getKey(), nextHCard); } } } /** Find the number of cards in the header */ public int getNumberOfCards() { return cards.size(); } /** Get an iterator over the header cards */ public Cursor iterator() { return cards.iterator(0); } /** Get the offset of this header */ public long getFileOffset() { return fileOffset; } /** Calculate the unpadded size of the data segment from * the header information. * * @return the unpadded data segment size. */ int trueDataSize() { if (!isValidHeader()) { return 0; } int naxis = getIntValue("NAXIS", 0); int bitpix = getIntValue("BITPIX"); int[] axes = new int[naxis]; for (int axis = 1; axis <= naxis; axis += 1) { axes[axis - 1] = getIntValue("NAXIS" + axis, 0); } boolean isGroup = getBooleanValue("GROUPS", false); int pcount = getIntValue("PCOUNT", 0); int gcount = getIntValue("GCOUNT", 1); int startAxis = 0; if (isGroup && naxis > 1 && axes[0] == 0) { startAxis = 1; } int size = 1; for (int i = startAxis; i < naxis; i += 1) { size *= axes[i]; } size += pcount; size *= gcount; // Now multiply by the number of bits per pixel and // convert to bytes. size *= Math.abs(getIntValue("BITPIX", 0)) / 8; return size; } /** Return the size of the data including any needed padding. * @return the data segment size including any needed padding. */ public long getDataSize() { return FitsUtil.addPadding(trueDataSize()); } /** Get the size of the header in bytes */ public long getSize() { return headerSize(); } /** Return the size of the header data including padding. * @return the header size including any needed padding. */ int headerSize() { if (!isValidHeader()) { return 0; } return FitsUtil.addPadding(cards.size() * 80); } /** Is this a valid header. * @return true for a valid header, * false otherwise. */ boolean isValidHeader() { if (getNumberOfCards() < 4) { return false; } iter = iterator(); String key = ((HeaderCard) iter.next()).getKey(); if (!key.equals("SIMPLE") && !key.equals("XTENSION")) { return false; } key = ((HeaderCard) iter.next()).getKey(); if (!key.equals("BITPIX")) { return false; } key = ((HeaderCard) iter.next()).getKey(); if (!key.equals("NAXIS")) { return false; } while (iter.hasNext()) { key = ((HeaderCard) iter.next()).getKey(); } if (!key.equals("END")) { return false; } return true; } /** Find the card associated with a given key. * If found this sets the mark to the card, otherwise it * unsets the mark. * @param key The header key. * @return null if the keyword could not be found; * return the HeaderCard object otherwise. */ public HeaderCard findCard(String key) { HeaderCard card = (HeaderCard) cards.get(key); if (card != null) { iter.setKey(key); } return card; } /** Get the value associated with the key as an int. * @param key The header key. * @param dft The value to be returned if the key is not found. */ public int getIntValue(String key, int dft) { return (int) getLongValue(key, (long) dft); } /** Get the int value associated with the given key. * @param key The header key. * @return The associated value or 0 if not found. */ public int getIntValue(String key) { return (int) getLongValue(key); } /** Get the long value associated with the given key. * @param key The header key. * @return The associated value or 0 if not found. */ public long getLongValue(String key) { return getLongValue(key, 0L); } /** Get the long value associated with the given key. * @param key The header key. * @param dft The default value to be returned if the key cannot be found. * @return the associated value. */ public long getLongValue(String key, long dft) { HeaderCard fcard = findCard(key); if (fcard == null) { return dft; } try { String v = fcard.getValue(); if (v != null) { return Long.parseLong(v); } } catch (NumberFormatException e) { } return dft; } /** Get the float value associated with the given key. * @param key The header key. * @param dft The value to be returned if the key is not found. */ public float getFloatValue(String key, float dft) { return (float) getDoubleValue(key, dft); } /** Get the float value associated with the given key. * @param key The header key. * @return The associated value or 0.0 if not found. */ public float getFloatValue(String key) { return (float) getDoubleValue(key); } /** Get the double value associated with the given key. * @param key The header key. * @return The associated value or 0.0 if not found. */ public double getDoubleValue(String key) { return getDoubleValue(key, 0.); } /** Get the double value associated with the given key. * @param key The header key. * @param dft The default value to return if the key cannot be found. * @return the associated value. */ public double getDoubleValue(String key, double dft) { HeaderCard fcard = findCard(key); if (fcard == null) { return dft; } try { String v = fcard.getValue(); if (v != null) { return new Double(v).doubleValue(); } } catch (NumberFormatException e) { } return dft; } /** Get the boolean value associated with the given key. * @param key The header key. * @return The value found, or false if not found or if the * keyword is not a logical keyword. */ public boolean getBooleanValue(String key) { return getBooleanValue(key, false); } /** Get the boolean value associated with the given key. * @param key The header key. * @param dft The value to be returned if the key cannot be found * or if the parameter does not seem to be a boolean. * @return the associated value. */ public boolean getBooleanValue(String key, boolean dft) { HeaderCard fcard = findCard(key); if (fcard == null) { return dft; } String val = fcard.getValue(); if (val == null) { return dft; } if (val.equals("T")) { return true; } else if (val.equals("F")) { return false; } else { return dft; } } /** Get the String value associated with the given key. * * @param key The header key. * @return The associated value or null if not found or if the value is not a string. */ public String getStringValue(String key) { HeaderCard fcard = findCard(key); if (fcard == null || !fcard.isStringValue()) { return null; } String val = fcard.getValue(); boolean append = longStringsEnabled && val != null && val.endsWith("&"); iter.next(); // skip the primary card. while (append) { HeaderCard nxt = (HeaderCard) iter.next(); if (nxt == null) { append = false; } else { key = nxt.getKey(); String comm = nxt.getComment(); if (key == null || comm == null || !key.equals("CONTINUE")) { append = false; } else { comm = continueString(comm); if (comm != null) { comm = comm.substring(1, comm.length() - 1); val = val.substring(0, val.length() - 1) + comm; append = comm.endsWith("&"); } } } } return val; } /** Add a card image to the header. * @param fcard The card to be added. */ public void addLine(HeaderCard fcard) { if (fcard != null) { if (fcard.isKeyValuePair()) { iter.add(fcard.getKey(), fcard); } else { iter.add(fcard); } } } /** Add a card image to the header. * @param card The card to be added. * @exception HeaderCardException If the card is not valid. */ public void addLine(String card) throws HeaderCardException { addLine(new HeaderCard(card)); } /** Create a header by reading the information from the input stream. * @param dis The input stream to read the data from. * @return null if there was a problem with the header; * otherwise return the header read from the input stream. */ public static Header readHeader(ArrayDataInput dis) throws TruncatedFileException, IOException { Header myHeader = new Header(); try { myHeader.read(dis); } catch (EOFException e) { // An EOF exception is thrown only if the EOF was detected // when reading the first card. In this case we want // to return a null. return null; } return myHeader; } /** Read a stream for header data. * @param dis The input stream to read the data from. */ public void read(ArrayDataInput dis) throws TruncatedFileException, IOException { if (dis instanceof RandomAccess) { fileOffset = FitsUtil.findOffset(dis); } else { fileOffset = -1; } byte[] buffer = new byte[80]; boolean firstCard = true; int count = 0; try { while (true) { int len; int need = 80; try { while (need > 0) { len = dis.read(buffer, 80 - need, need); count += 1; if (len == 0) { throw new TruncatedFileException(); } need -= len; } } catch (EOFException e) { // Rethrow the EOF if we are at the beginning of the header, // otherwise we have a FITS error. // Added by Booth Hartley: // If this is an extension HDU, then we may allow // junk at the end and simply ignore it // if (firstCard && (need == 80 || (fileOffset > 0 && FitsFactory.getAllowTerminalJunk()))) { throw e; } throw new TruncatedFileException(e.getMessage()); } String cbuf = AsciiFuncs.asciiString(buffer); HeaderCard fcard = new HeaderCard(cbuf); if (firstCard) { String key = fcard.getKey(); if (key == null || (!key.equals("SIMPLE") && !key.equals("XTENSION"))) { if (fileOffset > 0 && FitsFactory.getAllowTerminalJunk()) { throw new EOFException("Not FITS format at "+fileOffset+":"+cbuf); } else { throw new IOException("Not FITS format at " + fileOffset + ":" + cbuf); } } firstCard = false; } String key = fcard.getKey(); if (key != null && cards.containsKey(key)) { System.err.println("Warning: multiple occurrences of key:" + key); addDuplicate((HeaderCard)cards.get(key)); } // We don't check the value here. If the user // wants to be sure that long strings are disabled, // they can call setLongStringsEnabled(false) after // reading the header. if (key.equals("LONGSTRN")) { longStringsEnabled = true; } // save card originalCardCount++; //RBH ADDED addLine(fcard); if (cbuf.substring(0, 8).equals("END ")) { break; // Out of reading the header. } } } catch (EOFException e) { throw e; } catch (Exception e) { if (!(e instanceof EOFException)) { // For compatibility with Java V5 we just add in the error message // rather than using using the cause mechanism. // Probably should update this when we can ignore Java 5. throw new IOException("Invalid FITS Header:"+ e); } } if (fileOffset >= 0) { input = dis; } // Read to the end of the current FITS block. // try { dis.skipBytes(FitsUtil.padding(count * 80)); } catch (IOException e) { throw new TruncatedFileException(e.getMessage()); } } private void addDuplicate(HeaderCard dup) { if (duplicates == null) { duplicates = new ArrayList(); } duplicates.add(dup); } /** Were duplicate header keys found when this record was read in? */ public boolean hadDuplicates() { return duplicates != null; } /** Return the list of duplicate cards. Note that when the * header is read in, only the last entry for a given keyword is * retained in the active header. This method returns earlier * cards that have been discarded in the order in which they * were encountered in the header. It is possible for there to * be many cards with the same keyword in this list. */ public List getDuplicates() { return duplicates; } /** Find the card associated with a given key. * @param key The header key. * @return null if the keyword could not be found; * return the card image otherwise. */ public String findKey(String key) { HeaderCard card = findCard(key); if (card == null) { return null; } else { return card.toString(); } } /** Replace the key with a new key. Typically this is used * when deleting or inserting columns so that TFORMx -> TFORMx-1 * @param oldKey The old header keyword. * @param newKey the new header keyword. * @return true if the card was replaced. * @exception HeaderCardException If newKey is not a * valid FITS keyword. */ boolean replaceKey(String oldKey, String newKey) throws HeaderCardException { HeaderCard oldCard = findCard(oldKey); if (oldCard == null) { return false; } if (!cards.replaceKey(oldKey, newKey)) { throw new HeaderCardException("Duplicate key in replace"); } oldCard.setKey(newKey); return true; } /** Write the current header (including any needed padding) to the * output stream. * @param dos The output stream to which the data is to be written. * @exception FitsException if the header could not be written. */ public void write(ArrayDataOutput dos) throws FitsException { fileOffset = FitsUtil.findOffset(dos); // Ensure that all cards are in the proper order. cards.sort(new HeaderOrder()); checkBeginning(); checkEnd(); if (cards.size() <= 0) { return; } Cursor iter = cards.iterator(0); try { while (iter.hasNext()) { HeaderCard card = (HeaderCard) iter.next(); byte[] b = AsciiFuncs.getBytes(card.toString()); dos.write(b); } FitsUtil.pad(dos, getNumberOfCards() * 80, (byte) ' '); } catch (IOException e) { throw new FitsException("IO Error writing header: " + e); } try { dos.flush(); } catch (IOException e) { } } /** Rewrite the header. */ public void rewrite() throws FitsException, IOException { ArrayDataOutput dos = (ArrayDataOutput) input; if (rewriteable()) { FitsUtil.reposition(dos, fileOffset); write(dos); dos.flush(); } else { throw new FitsException("Invalid attempt to rewrite Header."); } } /** Reset the file pointer to the beginning of the header */ public boolean reset() { try { FitsUtil.reposition(input, fileOffset); return true; } catch (Exception e) { return false; } } /** Can the header be rewritten without rewriting the entire file? */ public boolean rewriteable() { if (fileOffset >= 0 && input instanceof ArrayDataOutput && (cards.size() + 35) / 36 == (originalCardCount + 35) / 36) { return true; } else { return false; } } /** Add or replace a key with the given boolean value and comment. * @param key The header key. * @param val The boolean value. * @param comment A comment to append to the card. * @exception HeaderCardException If the parameters cannot build a * valid FITS card. */ public void addValue(String key, boolean val, String comment) throws HeaderCardException { removeCard(key); iter.add(key, new HeaderCard(key, val, comment)); } /** Add or replace a key with the given double value and comment. * Note that float values will be promoted to doubles. * @param key The header key. * @param val The double value. * @param comment A comment to append to the card. * @exception HeaderCardException If the parameters cannot build a * valid FITS card. */ public void addValue(String key, double val, String comment) throws HeaderCardException { removeCard(key); iter.add(key, new HeaderCard(key, val, comment)); } ; /** Add or replace a key with the given string value and comment. * @param key The header key. * @param val The string value. * @param comment A comment to append to the card. * @exception HeaderCardException If the parameters cannot build a * valid FITS card. */ public void addValue(String key, String val, String comment) throws HeaderCardException { removeCard(key); // Remember that quotes get doubled in the value... if (longStringsEnabled && val.replace("'", "''").length() > 68) { addLongString(key, val, comment); } else { iter.add(key, new HeaderCard(key, val, comment)); } } /** Add or replace a key with the given long value and comment. * Note that int's will be promoted to long's. * @param key The header key. * @param val The long value. * @param comment A comment to append to the card. * @exception HeaderCardException If the parameters cannot build a * valid FITS card. */ public void addValue(String key, long val, String comment) throws HeaderCardException { removeCard(key); iter.add(key, new HeaderCard(key, val, comment)); } private int getAdjustedLength(String in, int max) { // Find the longest string that we can use when // we accommodate needing to double quotes. int size = 0; int i; for (i = 0; i < in.length() && size < max; i += 1) { if (in.charAt(i) == '\'') { size += 2; if (size > max) { break; // Jumped over the edge } } else { size += 1; } } return i; } protected void addLongString(String key, String val, String comment) throws HeaderCardException { // We assume that we've made the test so that // we need to write a long string. We need to // double the quotes in the string value. addValue // takes care of that for us, but we need to do it // ourselves when we are extending into the comments. // We also need to be careful that single quotes don't // make the string too long and that we don't split // in the middle of a quote. int off = getAdjustedLength(val, 67); String curr = val.substring(0, off) + '&'; // No comment here since we're using as much of the card as we can addValue(key, curr, null); val = val.substring(off); while (val != null && val.length() > 0) { off = getAdjustedLength(val, 67); if (off < val.length()) { curr = "'" + val.substring(0, off).replace("'", "''") + "&'"; val = val.substring(off); } else { curr = "'" + val.replace("'", "''") + "' / " + comment; val = null; } iter.add(new HeaderCard("CONTINUE", null, curr)); } } /** Delete a key. * @param key The header key. */ public void removeCard(String key) throws HeaderCardException { if (cards.containsKey(key)) { iter.setKey(key); if (iter.hasNext()) { HeaderCard hc = (HeaderCard) iter.next(); String val = hc.getValue(); boolean delExtensions = longStringsEnabled && val != null && val.endsWith("&"); iter.remove(); while (delExtensions) { hc = (HeaderCard) iter.next(); if (hc == null) { delExtensions = false; } else { if (hc.getKey().equals("CONTINUE")) { String more = hc.getComment(); more = continueString(more); if (more != null) { iter.remove(); delExtensions = more.endsWith("&'"); } else { delExtensions = false; } } else { delExtensions = false; } } } } } } /** Look for the continuation part of a COMMENT. * The comment may also include a 'real' comment, e.g., *
     *  X = 'AB&'
     *  CONTINUE 'CDEF' / ABC
     *  
* Here we are looking for just the 'CDEF' part of the CONTINUE card. */ private String continueString(String input) { if (input == null) { return null; } input = input.trim(); if (input.length() < 2 || input.charAt(0) != '\'') { return null; } for (int i = 1; i < input.length(); i += 1) { char c = input.charAt(i); if (c == '\'') { if (i < input.length() - 1 && input.charAt(i + 1) == c) { // consecutive quotes -> escaped single quote // Get rid of the extra quote. input = input.substring(0, i) + input.substring(i + 1); continue; // Check the next character. } else { // Found closing apostrophe return input.substring(0, i + 1); } } } // Never found a closing apostrophe. return null; } /** Add a line to the header using the COMMENT style, i.e., no '=' * in column 9. * @param header The comment style header. * @param value A string to follow the header. * @exception HeaderCardException If the parameters cannot build a * valid FITS card. */ public void insertCommentStyle(String header, String value) { // Should just truncate strings, so we should never get // an exception... try { iter.add(new HeaderCard(header, null, value)); } catch (HeaderCardException e) { System.err.println("Impossible Exception for comment style:" + header + ":" + value); } } /** Add a COMMENT line. * @param value The comment. * @exception HeaderCardException If the parameter is not a * valid FITS comment. */ public void insertComment(String value) throws HeaderCardException { insertCommentStyle("COMMENT", value); } /** Add a HISTORY line. * @param value The history record. * @exception HeaderCardException If the parameter is not a * valid FITS comment. */ public void insertHistory(String value) throws HeaderCardException { insertCommentStyle("HISTORY", value); } /** Delete the card associated with the given key. * Nothing occurs if the key is not found. * * @param key The header key. */ public void deleteKey(String key) { iter.setKey(key); if (iter.hasNext()) { iter.next(); iter.remove(); } } /** Tests if the specified keyword is present in this table. * @param key the keyword to be found. * @return true if the specified keyword is present in this * table; false otherwise. */ public final boolean containsKey(String key) { return cards.containsKey(key); } /** Create a header for a null image. */ void nullImage() { iter = iterator(); try { addValue("SIMPLE", true, "ntf::header:simple:2"); addValue("BITPIX", 8, "ntf::header:bitpix:2"); addValue("NAXIS", 0, "ntf::header:naxis:2"); addValue("EXTEND", true, "ntf::header:extend:2"); } catch (HeaderCardException e) { } } /** Set the SIMPLE keyword to the given value. * @param val The boolean value -- Should be true for FITS data. */ public void setSimple(boolean val) { deleteKey("SIMPLE"); deleteKey("XTENSION"); // If we're flipping back to and from the primary header // we need to add in the EXTEND keyword whenever we become // a primary, because it's not permitted in the extensions // (at least not where it needs to be in the primary array). if (findCard("NAXIS") != null) { int nax = getIntValue("NAXIS"); iter = iterator(); if (findCard("NAXIS" + nax) != null) { HeaderCard hc = (HeaderCard) iter.next(); try { removeCard("EXTEND"); iter.add("EXTEND", new HeaderCard("EXTEND", true, "ntf::header:extend:1")); } catch (Exception e) { // Ignore the exception } ; } } iter = iterator(); try { iter.add("SIMPLE", new HeaderCard("SIMPLE", val, "ntf::header:simple:1")); } catch (HeaderCardException e) { System.err.println("Impossible exception at setSimple " + e); } } /** Set the XTENSION keyword to the given value. * @param val The name of the extension. "IMAGE" and "BINTABLE" are supported. */ public void setXtension(String val) { deleteKey("SIMPLE"); deleteKey("XTENSION"); deleteKey("EXTEND"); iter = iterator(); try { iter.add("XTENSION", new HeaderCard("XTENSION", val, "ntf::header:xtension:1")); } catch (HeaderCardException e) { System.err.println("Impossible exception at setXtension " + e); } } /** Set the BITPIX value for the header. * The following values are permitted by FITS conventions: *
    *
  • 8 -- signed byte data. Also used for tables.
  • *
  • 16 -- signed short data.
  • *
  • 32 -- signed int data.
  • *
  • 64 -- signed long data.
  • *
  • -32 -- IEEE 32 bit floating point numbers.
  • *
  • -64 -- IEEE 64 bit floating point numbers.
  • *
* @param val The value set by the user. */ public void setBitpix(int val) { iter = iterator(); iter.next(); try { iter.add("BITPIX", new HeaderCard("BITPIX", val, "ntf::header:bitpix:1")); } catch (HeaderCardException e) { System.err.println("Impossible exception at setBitpix " + e); } } /** Set the value of the NAXIS keyword * @param val The dimensionality of the data. */ public void setNaxes(int val) { iter.setKey("BITPIX"); if (iter.hasNext()) { iter.next(); } try { iter.add("NAXIS", new HeaderCard("NAXIS", val, "ntf::header:naxis:1")); } catch (HeaderCardException e) { System.err.println("Impossible exception at setNaxes " + e); } } /** Set the dimension for a given axis. * @param axis The axis being set. * @param dim The dimension */ public void setNaxis(int axis, int dim) { if (axis <= 0) { return; } if (axis == 1) { iter.setKey("NAXIS"); } else if (axis > 1) { iter.setKey("NAXIS" + (axis - 1)); } if (iter.hasNext()) { iter.next(); } try { iter.add("NAXIS" + axis, new HeaderCard("NAXIS" + axis, dim, "ntf::header:naxisN:1")); } catch (HeaderCardException e) { System.err.println("Impossible exception at setNaxis " + e); } } /** Ensure that the header begins with * a valid set of keywords. Note that we * do not check the values of these keywords. */ void checkBeginning() throws FitsException { iter = iterator(); if (!iter.hasNext()) { throw new FitsException("Empty Header"); } HeaderCard card = (HeaderCard) iter.next(); String key = card.getKey(); if (!key.equals("SIMPLE") && !key.equals("XTENSION")) { throw new FitsException("No SIMPLE or XTENSION at beginning of Header"); } boolean isTable = false; boolean isExtension = false; if (key.equals("XTENSION")) { String value = card.getValue(); if (value == null) { throw new FitsException("Empty XTENSION keyword"); } isExtension = true; if (value.equals("BINTABLE") || value.equals("A3DTABLE") || value.equals("TABLE")) { isTable = true; } } cardCheck("BITPIX"); cardCheck("NAXIS"); int nax = getIntValue("NAXIS"); iter.next(); for (int i = 1; i <= nax; i += 1) { cardCheck("NAXIS" + i); } if (isExtension) { cardCheck("PCOUNT"); cardCheck("GCOUNT"); if (isTable) { cardCheck("TFIELDS"); } } // This does not check for the EXTEND keyword which // if present in the primary array must immediately follow // the NAXISn. } /** Check if the given key is the next one available in * the header. */ private void cardCheck(String key) throws FitsException { if (!iter.hasNext()) { throw new FitsException("Header terminates before " + key); } HeaderCard card = (HeaderCard) iter.next(); if (!card.getKey().equals(key)) { throw new FitsException("Key " + key + " not found where expected." + "Found " + card.getKey()); } } /** Ensure that the header has exactly one END keyword in * the appropriate location. */ void checkEnd() { // Ensure we have an END card only at the end of the // header. // iter = iterator(); HeaderCard card; while (iter.hasNext()) { card = (HeaderCard) iter.next(); if (!card.isKeyValuePair() && card.getKey().equals("END")) { iter.remove(); } } try { // End cannot have a comment iter.add(new HeaderCard("END", null, null)); } catch (HeaderCardException e) { } } /** Print the header to a given stream. * @param ps the stream to which the card images are dumped. */ public void dumpHeader(PrintStream ps) { iter = iterator(); while (iter.hasNext()) { ps.println(iter.next()); } } /***** Deprecated methods *******/ /** Find the number of cards in the header * @deprecated see numberOfCards(). The units * of the size of the header may be unclear. */ public int size() { return cards.size(); } /** Get the n'th card image in the header * @return the card image; return null if the n'th card * does not exist. * @deprecated An iterator should be used for sequential * access to the header. */ public String getCard(int n) { if (n >= 0 && n < cards.size()) { iter = cards.iterator(n); HeaderCard c = (HeaderCard) iter.next(); return c.toString(); } return null; } /** Get the n'th key in the header. * @return the card image; return null if the n'th key * does not exist. * @deprecated An iterator should be used for sequential * access to the header. */ public String getKey(int n) { String card = getCard(n); if (card == null) { return null; } String key = card.substring(0, 8); if (key.charAt(0) == ' ') { return ""; } if (key.indexOf(' ') >= 1) { key = key.substring(0, key.indexOf(' ')); } return key; } /** Create a header which points to the * given data object. * @param o The data object to be described. * @exception FitsException if the data was not valid for this header. * @deprecated Use the appropriate Header constructor. */ public void pointToData(Data o) throws FitsException { o.fillHeader(this); } /** Find the end of a set of keywords describing a column or axis * (or anything else terminated by an index. This routine leaves * the header ready to add keywords after any existing keywords * with the index specified. The user should specify a * prefix to a keyword that is guaranteed to be present. */ Cursor positionAfterIndex(String prefix, int col) { String colnum = "" + col; iter.setKey(prefix + colnum); if (iter.hasNext()) { // Bug fix (references to forward) here by Laurent Borges boolean forward = false; String key; while (iter.hasNext()) { key = ((HeaderCard) iter.next()).getKey().trim(); if (key == null || key.length() <= colnum.length() || !key.substring(key.length() - colnum.length()).equals(colnum)) { forward = true; break; } } if (forward) { iter.prev(); // Gone one too far, so skip back an element. } } return iter; } /** Get the next card in the Header using the current iterator */ public HeaderCard nextCard() { if (iter == null) { return null; } if (iter.hasNext()) { return (HeaderCard) iter.next(); } else { return null; } } /** Move after the EXTEND keyword in images. * Used in bug fix noted by V. Forchi */ void afterExtend() { if (findCard("EXTEND") != null) { nextCard(); } } } fits-1.10.0/nom/tam/fits/FitsUtil.java0000664000175000017500000004137312031033644017675 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import nom.tam.util.RandomAccess; import java.io.IOException; import java.io.File; import java.io.FileInputStream; import java.io.FilterInputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.PushbackInputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Constructor; import java.net.URL; import java.net.URLConnection; import java.util.Map; import java.util.List; import java.util.zip.GZIPInputStream; import nom.tam.util.ArrayDataOutput; import nom.tam.util.AsciiFuncs; /** This class comprises static * utility functions used throughout * the FITS classes. */ public class FitsUtil { private static boolean wroteCheckingError = false; /** Reposition a random access stream to a requested offset */ public static void reposition(Object o, long offset) throws FitsException { if (o == null) { throw new FitsException("Attempt to reposition null stream"); } if (!(o instanceof RandomAccess) || offset < 0) { throw new FitsException("Invalid attempt to reposition stream " + o + " of type " + o.getClass().getName() + " to " + offset); } try { ((RandomAccess) o).seek(offset); } catch (IOException e) { throw new FitsException("Unable to repostion stream " + o + " of type " + o.getClass().getName() + " to " + offset + " Exception:" + e); } } /** Find out where we are in a random access file */ public static long findOffset(Object o) { if (o instanceof RandomAccess) { return ((RandomAccess) o).getFilePointer(); } else { return -1; } } /** How many bytes are needed to fill the last 2880 block? */ public static int padding(int size) { return padding((long) size); } public static int padding(long size) { int mod = (int) (size % 2880); if (mod > 0) { mod = 2880 - mod; } return mod; } /** Total size of blocked FITS element */ public static int addPadding(int size) { return size + padding(size); } public static long addPadding(long size) { return size + padding(size); } /** This method decompresses a compressed * input stream. The decompression method is * selected automatically based upon the first two bytes read. * @param compressed The compressed input stram * @return A stream which wraps the input stream and decompresses * it. If the input stream is not compressed, a * pushback input stream wrapping the original stream is returned. */ static InputStream decompress(InputStream compressed) throws FitsException { PushbackInputStream pb = new PushbackInputStream(compressed, 2); int mag1 = -1; int mag2 = -1; try { mag1 = pb.read(); mag2 = pb.read(); if (mag1 == 0x1f && mag2 == 0x8b) { // Push the data back into the stream pb.unread(mag2); pb.unread(mag1); return new GZIPInputStream(pb); } else if (mag1 == 0x1f && mag2 == 0x9d) { // Push the data back into the stream pb.unread(mag2); pb.unread(mag1); return compressInputStream(pb); } else if (mag1 == 'B' && mag2 == 'Z') { if (System.getenv("BZIP_DECOMPRESSOR") != null) { pb.unread(mag2); pb.unread(mag1); return bunzipper(pb); } // Don't pushback String cname = "org.apache.tools.bzip2.CBZip2InputStream"; // Note that we forego generics here since we don't // want any explicit mention of this class so that users // can compile and run without worrying about having the class in hand. try { Constructor con = Class.forName(cname).getConstructor(InputStream.class); return (InputStream) con.newInstance(pb); } catch (Exception e) { System.err.println("Unable to find constructor for BZIP2 decompression. Is the Apache BZIP jar in the classpath?"); throw new FitsException("No CBZip2InputStream class found for bzip2 compressed file"); } } else { // Push the data back into the stream pb.unread(mag2); pb.unread(mag1); return pb; } } catch (IOException e) { // This is probably a prelude to failure... throw new FitsException("Unable to analyze input stream"); } } static InputStream compressInputStream(final InputStream compressed) throws FitsException { try { Process proc = new ProcessBuilder("uncompress", "-c").start(); // This is the input to the process -- but // an output from here. final OutputStream input = proc.getOutputStream(); // Now copy everything in a separate thread. Thread copier = new Thread( new Runnable() { public void run() { try { byte[] buffer = new byte[8192]; int len; while ((len = compressed.read(buffer, 0, buffer.length)) > 0) { input.write(buffer, 0, len); } compressed.close(); input.close(); } catch (IOException e) { return; } } }); copier.start(); return proc.getInputStream(); } catch (Exception e) { throw new FitsException("Unable to read .Z compressed stream.\nIs `uncompress' in the path?\n:" + e); } } /** Is a file compressed? */ public static boolean isCompressed(File test) { InputStream fis = null; try { if (test.exists()) { fis = new FileInputStream(test); int mag1 = fis.read(); int mag2 = fis.read(); fis.close(); if (mag1 == 0x1f && (mag2 == 0x8b || mag2 == 0x9d)) { return true; } else if (mag1 == 'B' && mag2 == 'Z') { return true; } else { return false; } } } catch (IOException e) { // This is probably a prelude to failure... return false; } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { } } } return false; } /** Check if a file seems to be compressed. */ public static boolean isCompressed(String filename) { if (filename == null) { return false; } FileInputStream fis = null; File test = new File(filename); if (test.exists()) { return isCompressed(test); } int len = filename.length(); return len > 2 && (filename.substring(len - 3).equalsIgnoreCase(".gz") || filename.substring(len - 2).equals(".Z")); } /** Get the maximum length of a String in a String array. */ public static int maxLength(String[] o) throws FitsException { int max = 0; for (int i = 0; i < o.length; i += 1) { if (o[i] != null && o[i].length() > max) { max = o[i].length(); } } return max; } /** Copy an array of Strings to bytes.*/ public static byte[] stringsToByteArray(String[] o, int maxLen) { byte[] res = new byte[o.length * maxLen]; for (int i = 0; i < o.length; i += 1) { byte[] bstr = null; if (o[i] == null) { bstr = new byte[0]; } else { bstr = AsciiFuncs.getBytes(o[i]); } int cnt = bstr.length; if (cnt > maxLen) { cnt = maxLen; } System.arraycopy(bstr, 0, res, i * maxLen, cnt); for (int j = cnt; j < maxLen; j += 1) { res[i * maxLen + j] = (byte) ' '; } } return res; } /** Convert bytes to Strings */ public static String[] byteArrayToStrings(byte[] o, int maxLen) { boolean checking = FitsFactory.getCheckAsciiStrings(); // Note that if a String in a binary table contains an internal 0, // the FITS standard says that it is to be considered as terminating // the string at that point, so that software reading the // data back may not include subsequent characters. // No warning of this truncation is given. String[] res = new String[o.length / maxLen]; for (int i = 0; i < res.length; i += 1) { int start = i * maxLen; int end = start + maxLen; // Pre-trim the string to avoid keeping memory // hanging around. (Suggested by J.C. Segovia, ESA). // Note that the FITS standard does not mandate // that we should be trimming the string at all, but // this seems to best meet the desires of the community. for (; start < end; start += 1) { if (o[start] != 32) { break; // Skip only spaces. } } for (; end > start; end -= 1) { if (o[end - 1] != 32) { break; } } // For FITS binary tables, 0 values are supposed // to terminate strings, a la C. [They shouldn't appear in // any other context.] // Other non-printing ASCII characters // should always be an error which we can check for // if the user requests. // The lack of handling of null bytes was noted by Laurent Bourges. boolean errFound = false; for (int j = start; j < end; j += 1) { if (o[j] == 0) { end = j; break; } if (checking) { if (o[j] < 32 || o[j] > 126) { errFound = true; o[j] = 32; } } } res[i] = AsciiFuncs.asciiString(o, start, end - start); if (errFound && !wroteCheckingError) { System.err.println("Warning: Invalid ASCII character[s] detected in string:" + res[i]); System.err.println(" Converted to space[s]. Any subsequent invalid characters will be converted silently"); wroteCheckingError = true; } } return res; } /** Convert an array of booleans to bytes */ static byte[] booleanToByte(boolean[] bool) { byte[] byt = new byte[bool.length]; for (int i = 0; i < bool.length; i += 1) { byt[i] = bool[i] ? (byte) 'T' : (byte) 'F'; } return byt; } /** Convert an array of bytes to booleans */ static boolean[] byteToBoolean(byte[] byt) { boolean[] bool = new boolean[byt.length]; for (int i = 0; i < byt.length; i += 1) { bool[i] = (byt[i] == 'T'); } return bool; } /** Get a stream to a URL accommodating possible redirections. * Note that if a redirection request points to a different * protocol than the original request, then the redirection * is not handled automatically. */ public static InputStream getURLStream(URL url, int level) throws IOException { // Hard coded....sigh if (level > 5) { throw new IOException("Two many levels of redirection in URL"); } URLConnection conn = url.openConnection(); // Map> hdrs = conn.getHeaderFields(); Map hdrs = conn.getHeaderFields(); // Read through the headers and see if there is a redirection header. // We loop (rather than just do a get on hdrs) // since we want to match without regard to case. String[] keys = (String[]) hdrs.keySet().toArray(new String[0]); // for (String key: hdrs.keySet()) { for (int i = 0; i < keys.length; i += 1) { String key = keys[i]; if (key != null && key.toLowerCase().equals("location")) { // String val = hdrs.get(key).get(0); String val = (String) ((List) hdrs.get(key)).get(0); if (val != null) { val = val.trim(); if (val.length() > 0) { // Redirect return getURLStream(new URL(val), level + 1); } } } } // No redirection return conn.getInputStream(); } /** Add padding to an output stream. */ public static void pad(ArrayDataOutput stream, long size) throws FitsException { pad(stream, size, (byte) 0); } /** Add padding to an output stream. */ public static void pad(ArrayDataOutput stream, long size, byte fill) throws FitsException { int len = padding(size); if (len > 0) { byte[] buf = new byte[len]; for (int i = 0; i < len; i += 1) { buf[i] = fill; } try { stream.write(buf); stream.flush(); } catch (Exception e) { throw new FitsException("Unable to write padding", e); } } } static InputStream bunzipper(final InputStream pb) throws FitsException { String cmd = System.getenv("BZIP_DECOMPRESSOR"); // Allow the user to have already specified the - option. if (cmd.indexOf(" -") < 0) { cmd += " -"; } final OutputStream out; String[] flds = cmd.split(" +"); Thread t; Process p; try { p = new ProcessBuilder(flds).start(); out = p.getOutputStream(); t = new Thread(new Runnable() { public void run() { try { byte[] buf = new byte[16384]; int len; long total = 0; while ((len = pb.read(buf)) > 0) { try { out.write(buf, 0, len); } catch (Exception e) { // Skip this. It can happen when we // stop reading the compressed file in mid stream. break; } total += len; } pb.close(); out.close(); } catch (IOException e) { throw new Error("Error reading BZIP compression using: " + System.getenv("BZIP_DECOMPRESSOR"), e); } } }); } catch (Exception e) { throw new FitsException("Error initiating BZIP decompression: " + e); } t.start(); return new CloseIS(p.getInputStream(), pb, out); } } class CloseIS extends FilterInputStream { InputStream i; OutputStream o; CloseIS(InputStream inp, InputStream i, OutputStream o) { super(inp); this.i = i; this.o = o; } public void close() throws IOException { super.close(); o.close(); i.close(); } } fits-1.10.0/nom/tam/fits/FitsHeap.java0000664000175000017500000001402612031033644017630 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import nom.tam.util.*; import java.io.*; /** This class supports the FITS heap. This * is currently used for variable length columns * in binary tables. */ public class FitsHeap implements FitsElement { /** The storage buffer */ private byte[] heap; /** The current used size of the buffer <= heap.length */ private int heapSize; /** The offset within a file where the heap begins */ private long fileOffset = -1; /** Has the heap ever been expanded? */ private boolean expanded = false; /** The stream the last read used */ private ArrayDataInput input; /** Our current offset into the heap. When we read from * the heap we use a byte array input stream. So long * as we continue to read further into the heap, we can * continue to use the same stream, but we need to * recreate the stream whenever we skip backwards. */ private int heapOffset = 0; /** A stream used to read the heap data */ private BufferedDataInputStream bstr; /** Create a heap of a given size. */ FitsHeap(int size) { heap = new byte[size]; heapSize = size; } /** Read the heap */ public void read(ArrayDataInput str) throws FitsException { if (str instanceof RandomAccess) { fileOffset = FitsUtil.findOffset(str); input = str; } if (heap != null) { try { str.read(heap, 0, heapSize); } catch (IOException e) { throw new FitsException("Error reading heap:" + e); } } bstr = null; } /** Write the heap */ public void write(ArrayDataOutput str) throws FitsException { try { str.write(heap, 0, heapSize); } catch (IOException e) { throw new FitsException("Error writing heap:" + e); } } public boolean rewriteable() { return fileOffset >= 0 && input instanceof ArrayDataOutput && !expanded; } /** Attempt to rewrite the heap with the current contents. * Note that no checking is done to make sure that the * heap does not extend past its prior boundaries. */ public void rewrite() throws IOException, FitsException { if (rewriteable()) { ArrayDataOutput str = (ArrayDataOutput) input; FitsUtil.reposition(str, fileOffset); write(str); } else { throw new FitsException("Invalid attempt to rewrite FitsHeap"); } } public boolean reset() { try { FitsUtil.reposition(input, fileOffset); return true; } catch (Exception e) { return false; } } /** Get data from the heap. * @param offset The offset at which the data begins. * @param array The array to be extracted. */ public void getData(int offset, Object array) throws FitsException { try { // Can we reuse the existing byte stream? if (bstr == null || heapOffset > offset) { heapOffset = 0; bstr = new BufferedDataInputStream( new ByteArrayInputStream(heap)); } bstr.skipBytes(offset - heapOffset); heapOffset = offset; heapOffset += bstr.readLArray(array); } catch (IOException e) { throw new FitsException("Error decoding heap area at offset=" + offset + ". Exception: Exception " + e); } } /** Check if the Heap can accommodate a given requirement. * If not expand the heap. */ void expandHeap(int need) { // Invalidate any existing input stream to the heap. bstr = null; if (heapSize + need > heap.length) { expanded = true; int newlen = (heapSize + need) * 2; if (newlen < 16384) { newlen = 16384; } byte[] newHeap = new byte[newlen]; System.arraycopy(heap, 0, newHeap, 0, heapSize); heap = newHeap; } } /** Add some data to the heap. */ int putData(Object data) throws FitsException { long lsize = ArrayFuncs.computeLSize(data); if (lsize > Integer.MAX_VALUE) { throw new FitsException("FITS Heap > 2 G"); } int size = (int) lsize; expandHeap(size); ByteArrayOutputStream bo = new ByteArrayOutputStream(size); try { BufferedDataOutputStream o = new BufferedDataOutputStream(bo); o.writeArray(data); o.flush(); o.close(); } catch (IOException e) { throw new FitsException("Unable to write variable column length data"); } System.arraycopy(bo.toByteArray(), 0, heap, heapSize, size); int oldOffset = heapSize; heapSize += size; return oldOffset; } /** Return the size of the Heap */ public int size() { return heapSize; } /** Return the size of the heap using the more bean compatbile format */ public long getSize() { return size(); } /** Get the file offset of the heap */ public long getFileOffset() { return fileOffset; } } fits-1.10.0/nom/tam/fits/BadHeaderException.java0000664000175000017500000000220312031033644021575 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This exception indicates that an error * was detected while parsing a FITS header record. */ public class BadHeaderException extends FitsException { public BadHeaderException() { super(); } public BadHeaderException(String msg) { super(msg); } } fits-1.10.0/nom/tam/fits/HeaderCard.java0000664000175000017500000005031012031033644020103 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This class describes methods to access and manipulate the individual * cards for a FITS Header. */ public class HeaderCard { /** The keyword part of the card (set to null if there's no keyword) */ private String key; /** The value part of the card (set to null if there's no value) */ private String value; /** The comment part of the card (set to null if there's no comment) */ private String comment; /** Does this card represent a nullable field. ? */ private boolean nullable; /** A flag indicating whether or not this is a string value */ private boolean isString; /** Maximum length of a FITS keyword field */ public static final int MAX_KEYWORD_LENGTH = 8; /** Maximum length of a FITS value field */ public static final int MAX_VALUE_LENGTH = 70; /** padding for building card images */ private static String space80 = " "; /** Create a HeaderCard from its component parts * @param key keyword (null for a comment) * @param value value (null for a comment or keyword without an '=') * @param comment comment * @exception HeaderCardException for any invalid keyword */ public HeaderCard(String key, double value, String comment) throws HeaderCardException { this(key, dblString(value), comment); isString = false; } /** Create a HeaderCard from its component parts * @param key keyword (null for a comment) * @param value value (null for a comment or keyword without an '=') * @param comment comment * @exception HeaderCardException for any invalid keyword */ public HeaderCard(String key, boolean value, String comment) throws HeaderCardException { this(key, value ? "T" : "F", comment); isString = false; } /** Create a HeaderCard from its component parts * @param key keyword (null for a comment) * @param value value (null for a comment or keyword without an '=') * @param comment comment * @exception HeaderCardException for any invalid keyword */ public HeaderCard(String key, int value, String comment) throws HeaderCardException { this(key, String.valueOf(value), comment); isString = false; } /** Create a HeaderCard from its component parts * @param key keyword (null for a comment) * @param value value (null for a comment or keyword without an '=') * @param comment comment * @exception HeaderCardException for any invalid keyword */ public HeaderCard(String key, long value, String comment) throws HeaderCardException { this(key, String.valueOf(value), comment); isString = false; } /** Create a HeaderCard from its component parts * @param key keyword (null for a comment) * @param value value (null for a comment or keyword without an '=') * @param comment comment * @exception HeaderCardException for any invalid keyword or value */ public HeaderCard(String key, String value, String comment) throws HeaderCardException { this(key, value, comment, false); } /** Create a comment style card. * This constructor builds a card which has no value. * This may be either a comment style card in which case the * nullable field should be false, or a value field which * has a null value, in which case the nullable field should be * true. * @param key The key for the comment or nullable field. * @param comment The comment * @param nullable Is this a nullable field or a comment-style card? */ public HeaderCard(String key, String comment, boolean nullable) throws HeaderCardException { this(key, null, comment, nullable); } /** Create a string from a double making sure that it's * not more than 20 characters long. * Probably would be better if we had a way to override this * since we can loose precision for some doubles. */ private static String dblString(double input) { String value = String.valueOf(input); if (value.length() > 20) { value = new java.util.Formatter().format("%20.13G", input).out().toString(); } return value; } /** Create a HeaderCard from its component parts * @param key Keyword (null for a COMMENT) * @param value Value * @param comment Comment * @param nullable Is this a nullable value card? * @exception HeaderCardException for any invalid keyword or value */ public HeaderCard(String key, String value, String comment, boolean nullable) throws HeaderCardException { if (comment != null && comment.startsWith("ntf::")) { String ckey = comment.substring(5); // Get rid of ntf:: prefix comment = HeaderCommentsMap.getComment(ckey); } if (key == null && value != null) { throw new HeaderCardException("Null keyword with non-null value"); } if (key != null && key.length() > MAX_KEYWORD_LENGTH) { if (!FitsFactory.getUseHierarch() || !key.substring(0, 9).equals("HIERARCH.")) { throw new HeaderCardException("Keyword too long"); } } if (value != null) { value = value.replaceAll(" *$", ""); if (value.length() > MAX_VALUE_LENGTH) { throw new HeaderCardException("Value too long"); } if (value.startsWith("'")) { if (value.charAt(value.length() - 1) != '\'') { throw new HeaderCardException("Missing end quote in string value"); } value = value.substring(1, value.length() - 1).trim(); } } this.key = key; this.value = value; this.comment = comment; this.nullable = nullable; isString = true; } /** Create a HeaderCard from a FITS card image * @param card the 80 character card image */ public HeaderCard(String card) { key = null; value = null; comment = null; isString = false; if (card.length() > 80) { card = card.substring(0, 80); } if (FitsFactory.getUseHierarch() && card.length() > 9 && card.substring(0, 9).equals("HIERARCH ")) { hierarchCard(card); return; } // We are going to assume that the value has no blanks in // it unless it is enclosed in quotes. Also, we assume that // a / terminates the string (except inside quotes) // treat short lines as special keywords if (card.length() < 9) { key = card; return; } // extract the key key = card.substring(0, 8).trim(); // if it is an empty key, assume the remainder of the card is a comment if (key.length() == 0) { key = ""; comment = card.substring(8); return; } // Non-key/value pair lines are treated as keyed comments if (key.equals("COMMENT") || key.equals("HISTORY") || !card.substring(8, 10).equals("= ")) { comment = card.substring(8).trim(); return; } // extract the value/comment part of the string String valueAndComment = card.substring(10).trim(); // If there is no value/comment part, we are done. if (valueAndComment.length() == 0) { value = ""; return; } int vend = -1; boolean quote = false; // If we have a ' then find the matching '. if (valueAndComment.charAt(0) == '\'') { int offset = 1; while (offset < valueAndComment.length()) { // look for next single-quote character vend = valueAndComment.indexOf("'", offset); // if the quote character is the last character on the line... if (vend == valueAndComment.length() - 1) { break; } // if we did not find a matching single-quote... if (vend == -1) { // pretend this is a comment card key = null; comment = card; return; } // if this is not an escaped single-quote, we are done if (valueAndComment.charAt(vend + 1) != '\'') { break; } // skip past escaped single-quote offset = vend + 2; } // break apart character string value = valueAndComment.substring(1, vend).trim(); value = value.replace("''", "'"); if (vend + 1 >= valueAndComment.length()) { comment = null; } else { comment = valueAndComment.substring(vend + 1).trim(); if (comment.charAt(0) == '/') { if (comment.length() > 1) { comment = comment.substring(1); } else { comment = ""; } } if (comment.length() == 0) { comment = null; } } isString = true; } else { // look for a / to terminate the field. int slashLoc = valueAndComment.indexOf('/'); if (slashLoc != -1) { comment = valueAndComment.substring(slashLoc + 1).trim(); value = valueAndComment.substring(0, slashLoc).trim(); } else { value = valueAndComment; } } } /** Process HIERARCH style cards... * HIERARCH LEV1 LEV2 ... = value / comment * The keyword for the card will be "HIERARCH.LEV1.LEV2..." * A '/' is assumed to start a comment. */ private void hierarchCard(String card) { String name = ""; String token = null; String separator = ""; int[] tokLimits; int posit = 0; int commStart = -1; // First get the hierarchy levels while ((tokLimits = getToken(card, posit)) != null) { token = card.substring(tokLimits[0], tokLimits[1]); if (!token.equals("=")) { name += separator + token; separator = "."; } else { tokLimits = getToken(card, tokLimits[1]); if (tokLimits != null) { token = card.substring(tokLimits[0], tokLimits[1]); } else { key = name; value = null; comment = null; return; } break; } posit = tokLimits[1]; } key = name; // At the end? if (tokLimits == null) { value = null; comment = null; isString = false; return; } // Really should consolidate the two instances // of this test in this class! if (token.charAt(0) == '\'') { // Find the next undoubled quote... isString = true; if (token.length() > 1 && token.charAt(1) == '\'' && (token.length() == 2 || token.charAt(2) != '\'')) { value = ""; commStart = tokLimits[0] + 2; } else if (card.length() < tokLimits[0] + 2) { value = null; comment = null; isString = false; return; } else { int i; for (i = tokLimits[0] + 1; i < card.length(); i += 1) { if (card.charAt(i) == '\'') { if (i == card.length() - 1) { value = card.substring(tokLimits[0] + 1, i); commStart = i + 1; break; } else if (card.charAt(i + 1) == '\'') { // Doubled quotes. i += 1; continue; } else { value = card.substring(tokLimits[0] + 1, i); commStart = i + 1; break; } } } } if (commStart < 0) { value = null; comment = null; isString = false; return; } for (int i = commStart; i < card.length(); i += 1) { if (card.charAt(i) == '/') { comment = card.substring(i + 1).trim(); break; } else if (card.charAt(i) != ' ') { comment = null; break; } } } else { isString = false; int sl = token.indexOf('/'); if (sl == 0) { value = null; comment = card.substring(tokLimits[0] + 1); } else if (sl > 0) { value = token.substring(0, sl); comment = card.substring(tokLimits[0] + sl + 1); } else { value = token; for (int i = tokLimits[1]; i < card.length(); i += 1) { if (card.charAt(i) == '/') { comment = card.substring(i + 1).trim(); break; } else if (card.charAt(i) != ' ') { comment = null; break; } } } } } /** Get the next token. Can't use StringTokenizer * since we sometimes need to know the position within * the string. */ private int[] getToken(String card, int posit) { int i; for (i = posit; i < card.length(); i += 1) { if (card.charAt(i) != ' ') { break; } } if (i >= card.length()) { return null; } if (card.charAt(i) == '=') { return new int[]{i, i + 1}; } int j; for (j = i + 1; j < card.length(); j += 1) { if (card.charAt(j) == ' ' || card.charAt(j) == '=') { break; } } return new int[]{i, j}; } /** Does this card contain a string value? */ public boolean isStringValue() { return isString; } /** Is this a key/value card? */ public boolean isKeyValuePair() { return (key != null && value != null); } /** Set the key. */ void setKey(String newKey) { key = newKey; } /** Return the keyword from this card */ public String getKey() { return key; } /** Return the value from this card */ public String getValue() { return value; } /** Set the value for this card. */ public void setValue(String update) { value = update; } /** Return the comment from this card */ public String getComment() { return comment; } /** Return the 80 character card image */ public String toString() { StringBuffer buf = new StringBuffer(80); // start with the keyword, if there is one if (key != null) { if (key.length() > 9 && key.substring(0, 9).equals("HIERARCH.")) { return hierarchToString(); } buf.append(key); if (key.length() < 8) { buf.append(space80.substring(0, 8 - buf.length())); } } if (value != null || nullable) { buf.append("= "); if (value != null) { if (isString) { // left justify the string inside the quotes buf.append('\''); buf.append(value.replace("'", "''")); if (buf.length() < 19) { buf.append(space80.substring(0, 19 - buf.length())); } buf.append('\''); // Now add space to the comment area starting at column 40 if (buf.length() < 30) { buf.append(space80.substring(0, 30 - buf.length())); } } else { int offset = buf.length(); if (value.length() < 20) { buf.append(space80.substring(0, 20 - value.length())); } buf.append(value); } } else { // Pad out a null value. buf.append(space80.substring(0, 20)); } // if there is a comment, add a comment delimiter if (comment != null) { buf.append(" / "); } } else if (comment != null && comment.startsWith("= ")) { buf.append(" "); } // finally, add any comment if (comment != null) { buf.append(comment); } // make sure the final string is exactly 80 characters long if (buf.length() > 80) { buf.setLength(80); } else { if (buf.length() < 80) { buf.append(space80.substring(0, 80 - buf.length())); } } return buf.toString(); } private String hierarchToString() { StringBuffer b = new StringBuffer(80); int p = 0; String space = ""; while (p < key.length()) { int q = key.indexOf('.', p); if (q < 0) { b.append(space + key.substring(p)); break; } else { b.append(space + key.substring(p, q)); } space = " "; p = q + 1; } if (value != null || nullable) { b.append("= "); if (value != null) { // Try to align values int avail = 80 - (b.length() + value.length()); if (isString) { avail -= 2; } if (comment != null) { avail -= 3 + comment.length(); } if (avail > 0 && b.length() < 29) { b.append(space80.substring(0, Math.min(avail, 29 - b.length()))); } if (isString) { b.append('\''); } else if (avail > 0 && value.length() < 10) { b.append(space80.substring(0, Math.min(avail, 10 - value.length()))); } b.append(value); if (isString) { b.append('\''); } } else if (b.length() < 30) { // Pad out a null value b.append(space80.substring(0, 30 - b.length())); } } if (comment != null) { b.append(" / " + comment); } if (b.length() < 80) { b.append(space80.substring(0, 80 - b.length())); } String card = new String(b); if (card.length() > 80) { card = card.substring(0, 80); } return card; } } fits-1.10.0/nom/tam/fits/TruncatedFileException.java0000664000175000017500000000220212031033644022526 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This exception is thrown when an EOF is detected in the middle * of an HDU. */ public class TruncatedFileException extends FitsException { public TruncatedFileException() { super(); } public TruncatedFileException(String msg) { super(msg); } } fits-1.10.0/nom/tam/fits/BasicHDU.java0000664000175000017500000003572112031033644017514 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import java.io.IOException; import nom.tam.util.ArrayDataInput; import nom.tam.util.ArrayDataOutput; import java.util.Iterator; import java.util.Date; /** This abstract class is the parent of all HDU types. * It provides basic functionality for an HDU. */ public abstract class BasicHDU implements FitsElement { public static final int BITPIX_BYTE = 8; public static final int BITPIX_SHORT = 16; public static final int BITPIX_INT = 32; public static final int BITPIX_LONG = 64; public static final int BITPIX_FLOAT = -32; public static final int BITPIX_DOUBLE = -64; /** The associated header. */ protected Header myHeader = null; /** The associated data unit. */ protected Data myData = null; /** Is this the first HDU in a FITS file? */ protected boolean isPrimary = false; /** 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 Data object could not be created * from this HDU's Header */ abstract Data manufactureData() throws FitsException; /** Skip the Data object immediately after the given Header object on * the given stream object. * @param stream the stream which contains the data. * @param hdr template indicating length of Data section * @exception IOException if the Data object could not be skipped. */ public static void skipData(ArrayDataInput stream, Header hdr) throws IOException { stream.skipBytes(hdr.getDataSize()); } /** Skip the Data object for this HDU. * @param stream the stream which contains the data. * @exception IOException if the Data object could not be skipped. */ public void skipData(ArrayDataInput stream) throws IOException { skipData(stream, myHeader); } /** Read in the Data object for this HDU. * @param stream the stream from which the data is read. * @exception FitsException if the Data object could not be created * from this HDU's Header */ public void readData(ArrayDataInput stream) throws FitsException { myData = null; try { myData = manufactureData(); } finally { // if we cannot build a Data object, skip this section if (myData == null) { try { skipData(stream, myHeader); } catch (Exception e) { } } } myData.read(stream); } /** Get the associated header */ public Header getHeader() { return myHeader; } /** Get the starting offset of the HDU */ public long getFileOffset() { return myHeader.getFileOffset(); } /** Get the associated Data object*/ public Data getData() { return myData; } /** Get the non-FITS data object */ public Object getKernel() { try { return myData.getKernel(); } catch (FitsException e) { return null; } } /** Get the total size in bytes of the HDU. * @return The size in bytes. */ public long getSize() { int size = 0; if (myHeader != null) { size += myHeader.getSize(); } if (myData != null) { size += myData.getSize(); } return size; } /** Check that this is a valid header for the HDU. * @param header to validate. * @return true if this is a valid header. */ public static boolean isHeader(Header header) { return false; } /** Print out some information about this HDU. */ public abstract void info(); /** Check if a field is present and if so print it out. * @param The header keyword. * @param Was it found in the header? */ boolean checkField(String name) { String value = myHeader.getStringValue(name); if (value == null) { return false; } return true; } /* Read out the HDU from the data stream. This * will overwrite any existing header and data components. */ public void read(ArrayDataInput stream) throws FitsException, IOException { myHeader = Header.readHeader(stream); myData = myHeader.makeData(); myData.read(stream); } /* Write out the HDU * @param stream The data stream to be written to. */ public void write(ArrayDataOutput stream) throws FitsException { if (myHeader != null) { myHeader.write(stream); } if (myData != null) { myData.write(stream); } try { stream.flush(); } catch (java.io.IOException e) { throw new FitsException("Error flushing at end of HDU: " + e.getMessage()); } } /** Is the HDU rewriteable */ public boolean rewriteable() { return myHeader.rewriteable() && myData.rewriteable(); } /** Rewrite the HDU */ public void rewrite() throws FitsException, IOException { if (rewriteable()) { myHeader.rewrite(); myData.rewrite(); } else { throw new FitsException("Invalid attempt to rewrite HDU"); } } /** * Get the String value associated with keyword. * @param keyword the FITS keyword * @return either null or a String with leading/trailing * blanks stripped. */ public String getTrimmedString(String keyword) { String s = myHeader.getStringValue(keyword); if (s != null) { s = s.trim(); } return s; } public int getBitPix() throws FitsException { int bitpix = myHeader.getIntValue("BITPIX", -1); switch (bitpix) { case BITPIX_BYTE: case BITPIX_SHORT: case BITPIX_INT: case BITPIX_FLOAT: case BITPIX_DOUBLE: break; default: throw new FitsException("Unknown BITPIX type " + bitpix); } return bitpix; } public int[] getAxes() throws FitsException { int nAxis = myHeader.getIntValue("NAXIS", 0); if (nAxis < 0) { throw new FitsException("Negative NAXIS value " + nAxis); } if (nAxis > 999) { throw new FitsException("NAXIS value " + nAxis + " too large"); } if (nAxis == 0) { return null; } int[] axes = new int[nAxis]; for (int i = 1; i <= nAxis; i++) { axes[nAxis - i] = myHeader.getIntValue("NAXIS" + i, 0); } return axes; } public int getParameterCount() { return myHeader.getIntValue("PCOUNT", 0); } public int getGroupCount() { return myHeader.getIntValue("GCOUNT", 1); } public double getBScale() { return myHeader.getDoubleValue("BSCALE", 1.0); } public double getBZero() { return myHeader.getDoubleValue("BZERO", 0.0); } public String getBUnit() { return getTrimmedString("BUNIT"); } public int getBlankValue() throws FitsException { if (!myHeader.containsKey("BLANK")) { throw new FitsException("BLANK undefined"); } return myHeader.getIntValue("BLANK"); } /** * Get the FITS file creation date as a Date object. * @return either null or a Date object */ public Date getCreationDate() { try { return new FitsDate(myHeader.getStringValue("DATE")).toDate(); } catch (FitsException e) { return null; } } /** * Get the FITS file observation date as a Date object. * @return either null or a Date object */ public Date getObservationDate() { try { return new FitsDate(myHeader.getStringValue("DATE-OBS")).toDate(); } catch (FitsException e) { return null; } } /** * Get the name of the organization which created this FITS file. * @return either null or a String object */ public String getOrigin() { return getTrimmedString("ORIGIN"); } /** * Get the name of the telescope which was used to acquire the data in * this FITS file. * @return either null or a String object */ public String getTelescope() { return getTrimmedString("TELESCOP"); } /** * Get the name of the instrument which was used to acquire the data in * this FITS file. * @return either null or a String object */ public String getInstrument() { return getTrimmedString("INSTRUME"); } /** * Get the name of the person who acquired the data in this FITS file. * @return either null or a String object */ public String getObserver() { return getTrimmedString("OBSERVER"); } /** * Get the name of the observed object in this FITS file. * @return either null or a String object */ public String getObject() { return getTrimmedString("OBJECT"); } /** * Get the equinox in years for the celestial coordinate system in which * positions given in either the header or data are expressed. * @return either null or a String object */ public double getEquinox() { return myHeader.getDoubleValue("EQUINOX", -1.0); } /** * Get the equinox in years for the celestial coordinate system in which * positions given in either the header or data are expressed. * @return either null or a String object * @deprecated Replaced by getEquinox * @see #getEquinox() */ public double getEpoch() { return myHeader.getDoubleValue("EPOCH", -1.0); } /** * Return the name of the person who compiled the information in * the data associated with this header. * @return either null or a String object */ public String getAuthor() { return getTrimmedString("AUTHOR"); } /** * Return the citation of a reference where the data associated with * this header are published. * @return either null or a String object */ public String getReference() { return getTrimmedString("REFERENC"); } /** * Return the minimum valid value in the array. * @return minimum value. */ public double getMaximumValue() { return myHeader.getDoubleValue("DATAMAX"); } /** * Return the minimum valid value in the array. * @return minimum value. */ public double getMinimumValue() { return myHeader.getDoubleValue("DATAMIN"); } /** Indicate whether HDU can be primary HDU. * This method must be overriden in HDU types which can * appear at the beginning of a FITS file. */ boolean canBePrimary() { return false; } /** Reset the input stream to the beginning of the HDU, i.e., the beginning of the header */ public boolean reset() { return myHeader.reset(); } /** Indicate that an HDU is the first element of a FITS file. */ void setPrimaryHDU(boolean newPrimary) throws FitsException { if (newPrimary && !canBePrimary()) { throw new FitsException("Invalid attempt to make HDU of type:" + this.getClass().getName() + " primary."); } else { this.isPrimary = newPrimary; } // Some FITS readers don't like the PCOUNT and GCOUNT keywords // in a primary array or they EXTEND keyword in extensions. if (isPrimary && !myHeader.getBooleanValue("GROUPS", false)) { myHeader.deleteKey("PCOUNT"); myHeader.deleteKey("GCOUNT"); } if (isPrimary) { HeaderCard card = myHeader.findCard("EXTEND"); if (card == null) { getAxes(); // Leaves the iterator pointing to the last NAXISn card. myHeader.nextCard(); myHeader.addValue("EXTEND", true, "ntf::basichdu:extend:1"); } } if (!isPrimary) { Iterator iter = myHeader.iterator(); int pcount = myHeader.getIntValue("PCOUNT", 0); int gcount = myHeader.getIntValue("GCOUNT", 1); int naxis = myHeader.getIntValue("NAXIS", 0); myHeader.deleteKey("EXTEND"); HeaderCard card; HeaderCard pcard = myHeader.findCard("PCOUNT"); HeaderCard gcard = myHeader.findCard("GCOUNT"); myHeader.getCard(2 + naxis); if (pcard == null) { myHeader.addValue("PCOUNT", pcount, "ntf::basichdu:pcount:1"); } if (gcard == null) { myHeader.addValue("GCOUNT", gcount, "ntf::basichdu:gcount:1"); } iter = myHeader.iterator(); } } /** Add information to the header */ public void addValue(String key, boolean val, String comment) throws HeaderCardException { myHeader.addValue(key, val, comment); } public void addValue(String key, int val, String comment) throws HeaderCardException { myHeader.addValue(key, val, comment); } public void addValue(String key, double val, String comment) throws HeaderCardException { myHeader.addValue(key, val, comment); } public void addValue(String key, String val, String comment) throws HeaderCardException { myHeader.addValue(key, val, comment); } /** Get an HDU without content */ public static BasicHDU getDummyHDU() { try { // Update suggested by Laurent Bourges ImageData img = new ImageData((Object) null); return FitsFactory.HDUFactory(ImageHDU.manufactureHeader(img), img); } catch (FitsException e) { System.err.println("Impossible exception in getDummyHDU"); return null; } } } fits-1.10.0/nom/tam/fits/Data.java0000664000175000017500000001013712031033644016775 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import java.io.*; import nom.tam.util.*; /** This class provides methods to access the data segment of an * HDU. */ public abstract class Data implements FitsElement { /** This is the object which contains the actual data for the HDU. *
    *
  • For images and primary data this is a simple (but possibly * multi-dimensional) primitive array. When group data is * supported it will be a possibly multidimensional array * of group objects. *
  • For ASCII data it is a two dimensional Object array where * each of the constituent objects is a primitive array of length 1. *
  • For Binary data it is a two dimensional Object array where * each of the constituent objects is a primitive array of arbitrary * (more or less) dimensionality. *
*/ /** The starting location of the data when last read */ protected long fileOffset = -1; /** The size of the data when last read */ protected long dataSize; /** The inputstream used. */ protected RandomAccess input; /** Get the file offset */ public long getFileOffset() { return fileOffset; } /** Set the fields needed for a re-read */ protected void setFileOffset(Object o) { if (o instanceof RandomAccess) { fileOffset = FitsUtil.findOffset(o); dataSize = getTrueSize(); input = (RandomAccess) o; } } /** Write the data -- including any buffering needed * @param o The output stream on which to write the data. */ public abstract void write(ArrayDataOutput o) throws FitsException; /** Read a data array into the current object and if needed position * to the beginning of the next FITS block. * @param i The input data stream */ public abstract void read(ArrayDataInput i) throws FitsException; public void rewrite() throws FitsException { if (!rewriteable()) { throw new FitsException("Illegal attempt to rewrite data"); } FitsUtil.reposition(input, fileOffset); write((ArrayDataOutput) input); try { ((ArrayDataOutput) input).flush(); } catch (IOException e) { throw new FitsException("Error in rewrite flush: " + e); } } public boolean reset() { try { FitsUtil.reposition(input, fileOffset); return true; } catch (Exception e) { return false; } } public boolean rewriteable() { if (input == null || fileOffset < 0 || (getTrueSize() + 2879) / 2880 != (dataSize + 2879) / 2880) { return false; } else { return true; } } abstract long getTrueSize(); /** Get the size of the data element in bytes */ public long getSize() { return FitsUtil.addPadding(getTrueSize()); } /** Return the data array object. */ public abstract Object getData() throws FitsException; /** Return the non-FITS data object */ public Object getKernel() throws FitsException { return getData(); } /** Modify a header to point to this data */ abstract void fillHeader(Header head) throws FitsException; } fits-1.10.0/nom/tam/fits/FitsDate.java0000664000175000017500000002626312031033644017636 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * This class was contributed by D. Glowacki. */ import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; import java.text.DecimalFormat; public class FitsDate { private int year = -1; private int month = -1; private int mday = -1; private int hour = -1; private int minute = -1; private int second = -1; private int millisecond = -1; private Date date = null; /** * Convert a FITS date string to a Java Date object. * @param dStr the FITS date * @exception FitsException if dStr does not * contain a valid FITS date. */ public FitsDate(String dStr) throws FitsException { // if the date string is null, we are done if (dStr == null) { return; } // if the date string is empty, we are done dStr = dStr.trim(); if (dStr.length() == 0) { return; } // if string contains at least 8 characters... int len = dStr.length(); if (len >= 8) { int first; // ... and there is a "/" in the string... first = dStr.indexOf('-'); if (first == 4 && first < len) { // ... this must be an new-style date buildNewDate(dStr, first, len); // no "/" found; maybe it is an old-style date... } else { first = dStr.indexOf('/'); if (first > 1 && first < len) { // ... this must be an old-style date buildOldDate(dStr, first, len); } } } if (year == -1) { throw new FitsException("Bad FITS date string \"" + dStr + '"'); } } private void buildOldDate(String dStr, int first, int len) { int middle = dStr.indexOf('/', first + 1); if (middle > first + 2 && middle < len) { try { year = Integer.parseInt(dStr.substring(middle + 1)) + 1900; month = Integer.parseInt(dStr.substring(first + 1, middle)); mday = Integer.parseInt(dStr.substring(0, first)); } catch (NumberFormatException e) { year = month = mday = -1; } } } private void parseTime(String tStr) throws FitsException { int first = tStr.indexOf(':'); if (first < 0) { throw new FitsException("Bad time"); } int len = tStr.length(); int middle = tStr.indexOf(':', first + 1); if (middle > first + 2 && middle < len) { if (middle + 3 < len && tStr.charAt(middle + 3) == '.') { double d = Double.valueOf(tStr.substring(middle + 3)).doubleValue(); millisecond = (int) (d * 1000); len = middle + 3; } try { hour = Integer.parseInt(tStr.substring(0, first)); minute = Integer.parseInt(tStr.substring(first + 1, middle)); second = Integer.parseInt(tStr.substring(middle + 1, len)); } catch (NumberFormatException e) { hour = minute = second = millisecond = -1; } } } private void buildNewDate(String dStr, int first, int len) throws FitsException { // find the middle separator int middle = dStr.indexOf('-', first + 1); if (middle > first + 2 && middle < len) { try { // if this date string includes a time... if (middle + 3 < len && dStr.charAt(middle + 3) == 'T') { // ... try to parse the time try { parseTime(dStr.substring(middle + 4)); } catch (FitsException e) { throw new FitsException("Bad time in FITS date string \"" + dStr + "\""); } // we got the time; mark the end of the date string len = middle + 3; } // parse date string year = Integer.parseInt(dStr.substring(0, first)); month = Integer.parseInt(dStr.substring(first + 1, middle)); mday = Integer.parseInt(dStr.substring(middle + 1, len)); } catch (NumberFormatException e) { // yikes, something failed; reset everything year = month = mday = hour = minute = second = millisecond = -1; } } } /** Get a Java Date object corresponding to this * FITS date. * @return The Java Date object. */ public Date toDate() { if (date == null && year != -1) { TimeZone tz = TimeZone.getTimeZone("GMT"); GregorianCalendar cal = new GregorianCalendar(tz); cal.set(Calendar.YEAR, year); cal.set(Calendar.MONTH, month - 1); cal.set(Calendar.DAY_OF_MONTH, mday); if (hour == -1) { cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); } else { cal.set(Calendar.HOUR_OF_DAY, hour); cal.set(Calendar.MINUTE, minute); cal.set(Calendar.SECOND, second); if (millisecond == -1) { cal.set(Calendar.MILLISECOND, 0); } else { cal.set(Calendar.MILLISECOND, millisecond); } } date = cal.getTime(); } return date; } /** Return the current date in FITS date format */ public static String getFitsDateString() { return getFitsDateString(new Date(), true); } /** Create FITS format date string Java Date object. * @param epoch The epoch to be converted to FITS format. */ public static String getFitsDateString(Date epoch) { return getFitsDateString(epoch, true); } /** Create FITS format date string. * Note that the date is not rounded. * @param epoch The epoch to be converted to FITS format. * @param timeOfDay Should time of day information be included? */ public static String getFitsDateString(Date epoch, boolean timeOfDay) { try { GregorianCalendar cal = new GregorianCalendar( TimeZone.getTimeZone("GMT")); cal.setTime(epoch); StringBuffer fitsDate = new StringBuffer(); DecimalFormat df = new DecimalFormat("0000"); fitsDate.append(df.format(cal.get(Calendar.YEAR))); fitsDate.append("-"); df = new DecimalFormat("00"); fitsDate.append(df.format(cal.get(Calendar.MONTH) + 1)); fitsDate.append("-"); fitsDate.append(df.format(cal.get(Calendar.DAY_OF_MONTH))); if (timeOfDay) { fitsDate.append("T"); fitsDate.append(df.format(cal.get(Calendar.HOUR_OF_DAY))); fitsDate.append(":"); fitsDate.append(df.format(cal.get(Calendar.MINUTE))); fitsDate.append(":"); fitsDate.append(df.format(cal.get(Calendar.SECOND))); fitsDate.append("."); df = new DecimalFormat("000"); fitsDate.append(df.format(cal.get(Calendar.MILLISECOND))); } return new String(fitsDate); } catch (Exception e) { return new String(""); } } public String toString() { if (year == -1) { return ""; } StringBuffer buf = new StringBuffer(23); buf.append(year); buf.append('-'); if (month < 10) { buf.append('0'); } buf.append(month); buf.append('-'); if (mday < 10) { buf.append('0'); } buf.append(mday); if (hour != -1) { buf.append('T'); if (hour < 10) { buf.append('0'); } buf.append(hour); buf.append(':'); if (minute < 10) { buf.append('0'); } buf.append(minute); buf.append(':'); if (second < 10) { buf.append('0'); } buf.append(second); if (millisecond != -1) { buf.append('.'); if (millisecond < 100) { if (millisecond < 10) { buf.append("00"); } else { buf.append('0'); } } buf.append(millisecond); } } return buf.toString(); } public static void testArgs(String args[]) { for (int i = 0; i < args.length; i++) { try { FitsDate fd = new FitsDate(args[i]); System.out.println("\"" + args[i] + "\" => " + fd + " => " + fd.toDate()); } catch (Exception e) { System.err.println("Date \"" + args[i] + "\" threw " + e.getClass().getName() + "(" + e.getMessage() + ")"); } } } public static void autotest() { String[] good = new String[6]; good[0] = "20/09/79"; good[1] = "1997-07-25"; good[2] = "1987-06-05T04:03:02.01"; good[3] = "1998-03-10T16:58:34"; good[4] = null; good[5] = " "; testArgs(good); String[] badOld = new String[4]; badOld[0] = "20/09/"; badOld[1] = "/09/79"; badOld[2] = "09//79"; badOld[3] = "20/09/79/"; testArgs(badOld); String[] badNew = new String[4]; badNew[0] = "1997-07"; badNew[1] = "-07-25"; badNew[2] = "1997--07-25"; badNew[3] = "1997-07-25-"; testArgs(badNew); String[] badMisc = new String[4]; badMisc[0] = "5-Aug-1992"; badMisc[1] = "28/02/91 16:32:00"; badMisc[2] = "18-Feb-1993"; badMisc[3] = "nn/nn/nn"; testArgs(badMisc); } public static void main(String args[]) { if (args.length == 0) { autotest(); } else { testArgs(args); } } } fits-1.10.0/nom/tam/fits/test/0000775000175000017500000000000011656751754016263 5ustar frothmaifrothmaifits-1.10.0/nom/tam/fits/test/test.fits.bz20000664000175000017500000000035511566662566020632 0ustar frothmaifrothmaiBZh91AY&SYà0O莀ÿÿÿÀ@·7g\À>#Ý`0¶C˜&L&Â0ÑM L5P4dÐd%4õ 2h 1AA_TËM¦“޼ú©Ô‚µ¢¯bÌV—m¸d™Bˆ šA™À2X &$uÙð0 yõ« `ÁI¾x®² d_[S|P-`Æ_AÛ5˜Y–VL]e6á]Hü©dÄq $ä'ðÍ@14³`ŒaÄ€ÇÇ?:†>εNц(L/À× Ñü]ÉáBC€Á? fits-1.10.0/nom/tam/fits/test/HeaderCardTest.java0000664000175000017500000001434012031031530021715 0ustar frothmaifrothmaipackage nom.tam.fits.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import org.junit.Test; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import junit.framework.JUnit4TestAdapter; import nom.tam.fits.HeaderCard; import nom.tam.fits.FitsFactory; public class HeaderCardTest { @Test public void test1() throws Exception { HeaderCard p; p = new HeaderCard("SIMPLE = T"); assertEquals("t1", "SIMPLE", p.getKey()); assertEquals("t2", "T", p.getValue()); assertNull("t3", p.getComment()); p = new HeaderCard("VALUE = 123"); assertEquals("t4", "VALUE", p.getKey()); assertEquals("t5", "123", p.getValue()); assertNull("t3", p.getComment()); p = new HeaderCard("VALUE = 1.23698789798798E23 / Comment "); assertEquals("t6", "VALUE", p.getKey()); assertEquals("t7", "1.23698789798798E23", p.getValue()); assertEquals("t8", "Comment", p.getComment()); String lng = "111111111111111111111111111111111111111111111111111111111111111111111111"; p = new HeaderCard("COMMENT " + lng); assertEquals("t9", "COMMENT", p.getKey()); assertNull("t10", p.getValue()); assertEquals("t11", lng, p.getComment()); boolean thrown = false; try { // p = new HeaderCard("VALUE = ' "); } catch (Exception e) { thrown = true; } assertEquals("t12", true, thrown); p = new HeaderCard("COMMENT " + lng + lng); assertEquals("t13", lng, p.getComment()); HeaderCard z = new HeaderCard("TTTT", 1.234567891234567891234567e101, "a comment"); assertTrue("t14", z.toString().indexOf("E") > 0); } @Test public void test3() throws Exception { HeaderCard p = new HeaderCard("KEY", "VALUE", "COMMENT"); assertEquals("x1", "KEY = 'VALUE ' / COMMENT ", p.toString()); p = new HeaderCard("KEY", 123, "COMMENT"); assertEquals("x2", "KEY = 123 / COMMENT ", p.toString()); p = new HeaderCard("KEY", 1.23, "COMMENT"); assertEquals("x3", "KEY = 1.23 / COMMENT ", p.toString()); p = new HeaderCard("KEY", true, "COMMENT"); assertEquals("x4", "KEY = T / COMMENT ", p.toString()); boolean thrown = false; try { p = new HeaderCard("LONGKEYWORD", 123, "COMMENT"); } catch (Exception e) { thrown = true; } assertEquals("x5", true, thrown); thrown = false; String lng = "00000000001111111111222222222233333333334444444444555555555566666666667777777777"; try { p = new HeaderCard("KEY", lng, "COMMENT"); } catch (Exception e) { thrown = true; } assertEquals("x6", true, thrown); // Only trailing spaces are stripped. p = new HeaderCard("STRING", "VALUE", null); assertEquals("x6", "VALUE", p.getValue()); p = new HeaderCard("STRING", "VALUE ", null); assertEquals("x7", "VALUE", p.getValue()); p = new HeaderCard("STRING", " VALUE", null); assertEquals("x8", " VALUE", p.getValue()); p = new HeaderCard("STRING", " VALUE ", null); assertEquals("x9", " VALUE", p.getValue()); p = new HeaderCard("QUOTES", "ABC'DEF", null); assertEquals("x10", "ABC'DEF", p.getValue()); assertEquals("x10b", p.toString().indexOf("''") > 0, true); p = new HeaderCard("QUOTES", "ABC''DEF", null); assertEquals("x11", "ABC''DEF", p.getValue()); assertEquals("x10b", p.toString().indexOf("''''") > 0, true); } @Test public void testHierarch() throws Exception { HeaderCard hc; String key = "HIERARCH.TEST1.TEST2.INT"; boolean thrown = false; try { hc = new HeaderCard(key, 123, "Comment"); } catch (Exception e) { thrown = true; } assertEquals("h1", true, thrown); String card = "HIERARCH TEST1 TEST2 INT= 123 / Comment "; hc = new HeaderCard(card); assertEquals("h2", "HIERARCH", hc.getKey()); assertNull("h3", hc.getValue()); assertEquals("h4", "TEST1 TEST2 INT= 123 / Comment", hc.getComment()); FitsFactory.setUseHierarch(true); hc = new HeaderCard(key, 123, "Comment"); assertEquals("h5", key, hc.getKey()); assertEquals("h6", "123", hc.getValue()); assertEquals("h7", "Comment", hc.getComment()); hc = new HeaderCard(card); assertEquals("h8", key, hc.getKey()); assertEquals("h9", "123", hc.getValue()); assertEquals("h10", "Comment", hc.getComment()); } @Test public void testLongDoubles() throws Exception { // Check to see if we make long double values // fit in the recommended space. HeaderCard hc = new HeaderCard("TEST", -1.234567890123456789e-123, "dummy"); String val = hc.getValue(); assertEquals("tld1", val.length(), 20); } } fits-1.10.0/nom/tam/fits/test/test_dup.fits0000664000175000017500000002640011656751616021000 0ustar frothmaifrothmaiSIMPLE = T /Primary Header created by MWRFITS v1.4 BITPIX = 16 / NAXIS = 2 / NAXIS1 = 5 / NAXIS2 = 5 / EXTEND = T /Extensions may be present CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / CARD = 1 / END  fits-1.10.0/nom/tam/fits/test/HeaderTest.java0000664000175000017500000003023612031031530021125 0ustar frothmaifrothmaipackage nom.tam.fits.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import nom.tam.fits.*; import nom.tam.util.*; import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; public class HeaderTest { /** Check out header manipulation. */ @Test public void simpleImages() throws Exception { float[][] img = new float[300][300]; Fits f = new Fits(); ImageHDU hdu = (ImageHDU) Fits.makeHDU(img); BufferedFile bf = new BufferedFile("ht1.fits", "rw"); f.addHDU(hdu); f.write(bf); bf.close(); f = new Fits("ht1.fits"); hdu = (ImageHDU) f.getHDU(0); Header hdr = hdu.getHeader(); assertEquals("NAXIS", 2, hdr.getIntValue("NAXIS")); assertEquals("NAXIS1", 300, hdr.getIntValue("NAXIS1")); assertEquals("NAXIS2", 300, hdr.getIntValue("NAXIS2")); assertEquals("NAXIS2a", 300, hdr.getIntValue("NAXIS2", -1)); assertEquals("NAXIS3", -1, hdr.getIntValue("NAXIS3", -1)); assertEquals("BITPIX", -32, hdr.getIntValue("BITPIX")); Cursor c = hdr.iterator(); HeaderCard hc = (HeaderCard) c.next(); assertEquals("SIMPLE_1", "SIMPLE", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("BITPIX_2", "BITPIX", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("NAXIS_3", "NAXIS", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("NAXIS1_4", "NAXIS1", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("NAXIS2_5", "NAXIS2", hc.getKey()); } /** Confirm initial location versus EXTEND keyword (V. Forchi). */ @Test public void extendTest() throws Exception { Fits f = new Fits("ht1.fits"); Header h = f.getHDU(0).getHeader(); h.addValue("TESTKEY", "TESTVAL", "TESTCOMM"); h.rewrite(); f.getStream().close(); f = new Fits("ht1.fits"); h = f.getHDU(0).getHeader(); // We should be pointed after the EXTEND and before TESTKEY h.addValue("TESTKEY2", "TESTVAL2", null); // Should precede TESTKEY Cursor c = h.iterator(); assertEquals("E1", ((HeaderCard) c.next()).getKey(), "SIMPLE"); assertEquals("E2", ((HeaderCard) c.next()).getKey(), "BITPIX"); assertEquals("E3", ((HeaderCard) c.next()).getKey(), "NAXIS"); assertEquals("E4", ((HeaderCard) c.next()).getKey(), "NAXIS1"); assertEquals("E5", ((HeaderCard) c.next()).getKey(), "NAXIS2"); assertEquals("E6", ((HeaderCard) c.next()).getKey(), "EXTEND"); assertEquals("E7", ((HeaderCard) c.next()).getKey(), "TESTKEY2"); assertEquals("E8", ((HeaderCard) c.next()).getKey(), "TESTKEY"); } @Test public void cursorTest() throws Exception { Fits f = new Fits("ht1.fits"); ImageHDU hdu = (ImageHDU) f.getHDU(0); Header hdr = hdu.getHeader(); Cursor c = hdr.iterator(); c.setKey("XXX"); c.add("CTYPE1", new HeaderCard("CTYPE1", "GLON-CAR", "Galactic Longitude")); c.add("CTYPE2", new HeaderCard("CTYPE2", "GLAT-CAR", "Galactic Latitude")); c.setKey("CTYPE1"); // Move before CTYPE1 c.add("CRVAL1", new HeaderCard("CRVAL1", 0., "Longitude at reference")); c.setKey("CTYPE2"); // Move before CTYPE2 c.add("CRVAL2", new HeaderCard("CRVAL2", -90., "Latitude at reference")); c.setKey("CTYPE1"); // Just practicing moving around!! c.add("CRPIX1", new HeaderCard("CRPIX1", 150.0, "Reference Pixel X")); c.setKey("CTYPE2"); c.add("CRPIX2", new HeaderCard("CRPIX2", 0., "Reference pixel Y")); c.add("INV2", new HeaderCard("INV2", true, "Invertible axis")); c.add("SYM2", new HeaderCard("SYM2", "YZ SYMMETRIC", "Symmetries...")); assertEquals("CTYPE1", "GLON-CAR", hdr.getStringValue("CTYPE1")); assertEquals("CRPIX2", 0., hdr.getDoubleValue("CRPIX2", -2.), 0); c.setKey("CRVAL1"); HeaderCard hc = (HeaderCard) c.next(); assertEquals("CRVAL1_c", "CRVAL1", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("CRPIX1_c", "CRPIX1", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("CTYPE1_c", "CTYPE1", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("CRVAL2_c", "CRVAL2", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("CRPIX2_c", "CRPIX2", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("INV2_c", "INV2", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("SYM2_c", "SYM2", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("CTYPE2_c", "CTYPE2", hc.getKey()); hdr.findCard("CRPIX1"); hdr.addValue("INTVAL1", 1, "An integer value"); hdr.addValue("LOG1", true, "A true value"); hdr.addValue("LOGB1", false, "A false value"); hdr.addValue("FLT1", 1.34, "A float value"); hdr.addValue("FLT2", -1.234567890e-134, "A very long float"); hdr.insertComment("Comment after flt2"); c.setKey("INTVAL1"); hc = (HeaderCard) c.next(); assertEquals("INTVAL1", "INTVAL1", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("LOG1", "LOG1", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("LOGB1", "LOGB1", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("FLT1", "FLT1", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("FLT2", "FLT2", hc.getKey()); c.next(); // Skip comment hc = (HeaderCard) c.next(); assertEquals("CRPIX1x", "CRPIX1", hc.getKey()); assertEquals("FLT1", 1.34, hdr.getDoubleValue("FLT1", 0), 0); c.setKey("FLT1"); c.next(); c.remove(); assertEquals("FLT1", 0., hdr.getDoubleValue("FLT1", 0), 0); c.setKey("LOGB1"); hc = (HeaderCard) c.next(); assertEquals("AftDel1", "LOGB1", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("AftDel2", "FLT2", hc.getKey()); hc = (HeaderCard) c.next(); assertEquals("AftDel3", "Comment after flt2", hc.getComment()); } @Test public void testBadHeader() throws Exception { Fits f = new Fits("ht1.fits"); ImageHDU hdu = (ImageHDU) f.getHDU(0); Header hdr = hdu.getHeader(); Cursor c = hdr.iterator(); c = hdr.iterator(); c.next(); c.next(); c.remove(); boolean thrown = false; try { hdr.rewrite(); } catch (Exception e) { thrown = true; } assertEquals("BITPIX delete", true, thrown); } @Test public void testUpdateHeaderComments() throws Exception { byte[][] z = new byte[4][4]; Fits f = new Fits(); f.addHDU(FitsFactory.HDUFactory(z)); BufferedFile bf = new BufferedFile("hx1.fits", "rw"); f.write(bf); bf.close(); f = new Fits("hx1.fits"); HeaderCard c1 = f.getHDU(0).getHeader().findCard("SIMPLE"); assertEquals("tuhc1", c1.getComment(), HeaderCommentsMap.getComment("header:simple:1")); c1 = f.getHDU(0).getHeader().findCard("BITPIX"); assertEquals("tuhc2", c1.getComment(), HeaderCommentsMap.getComment("header:bitpix:1")); HeaderCommentsMap.updateComment("header:bitpix:1", "A byte array"); HeaderCommentsMap.deleteComment("header:simple:1"); f = new Fits(); f.addHDU(FitsFactory.HDUFactory(z)); bf = new BufferedFile("hx2.fits", "rw"); f.write(bf); bf.close(); f = new Fits("hx2.fits"); c1 = f.getHDU(0).getHeader().findCard("SIMPLE"); assertEquals("tuhc1", c1.getComment(), null); c1 = f.getHDU(0).getHeader().findCard("BITPIX"); assertEquals("tuhc2", c1.getComment(), "A byte array"); } @Test public void testRewrite() throws Exception { // Should be rewriteable until we add enough cards to // start a new block. Fits f = new Fits("ht1.fits"); ImageHDU hdu = (ImageHDU) f.getHDU(0); Header hdr = hdu.getHeader(); Cursor c = hdr.iterator(); int nc = hdr.getNumberOfCards(); int nb = (nc - 1) / 36; while (hdr.rewriteable()) { int nbx = (hdr.getNumberOfCards() - 1) / 36; assertEquals("Rewrite:" + nbx, nb == nbx, hdr.rewriteable()); c.add(new HeaderCard("DUMMY" + nbx, null, null)); } } @Test public void longStringTest() throws Exception { Header hdr = new Fits("ht1.fits").getHDU(0).getHeader(); String seq = "0123456789"; String lng = ""; for (int i = 0; i < 20; i += 1) { lng += seq; } assertEquals("Initial state:", false, Header.getLongStringsEnabled()); Header.setLongStringsEnabled(true); assertEquals("Set state:", true, Header.getLongStringsEnabled()); hdr.addValue("LONG1", lng, "Here is a comment"); hdr.addValue("LONG2", "xx'yy'zz" + lng, "Another comment"); hdr.addValue("SHORT", "A STRING ENDING IN A &", null); hdr.addValue("LONGISH", lng + "&", null); hdr.addValue("LONGSTRN", "OGIP 1.0", "Uses long strings"); String sixty = seq + seq + seq + seq + seq + seq; hdr.addValue("APOS1", sixty + "''''''''''", "Should be 70 chars long"); hdr.addValue("APOS2", sixty + " ''''''''''", "Should be 71 chars long"); // Now try to read the values back. BufferedFile bf = new BufferedFile("ht4.hdr", "rw"); hdr.write(bf); bf.close(); String val = hdr.getStringValue("LONG1"); assertEquals("LongT1", val, lng); val = hdr.getStringValue("LONG2"); assertEquals("LongT2", val, "xx'yy'zz" + lng); assertEquals("APOS1", hdr.getStringValue("APOS1").length(), 70); assertEquals("APOS2", hdr.getStringValue("APOS2").length(), 71); Header.setLongStringsEnabled(false); val = hdr.getStringValue("LONG1"); assertEquals("LongT3", true, !val.equals(lng)); assertEquals("Longt4", true, val.length() <= 70); assertEquals("longamp1", hdr.getStringValue("SHORT"), "A STRING ENDING IN A &"); bf = new BufferedFile("ht4.hdr", "r"); hdr = new Header(bf); assertEquals("Set state2:", true, Header.getLongStringsEnabled()); val = hdr.getStringValue("LONG1"); assertEquals("LongT5", val, lng); val = hdr.getStringValue("LONG2"); assertEquals("LongT6", val, "xx'yy'zz" + lng); assertEquals("longamp2", hdr.getStringValue("LONGISH"), lng + "&"); assertEquals("APOS1b", hdr.getStringValue("APOS1").length(), 70); assertEquals("APOS2b", hdr.getStringValue("APOS2").length(), 71); assertEquals("APOS2c", hdr.getStringValue("APOS2"), sixty + " ''''''''''"); assertEquals("longamp1b", hdr.getStringValue("SHORT"), "A STRING ENDING IN A &"); assertEquals("longamp2b", hdr.getStringValue("LONGISH"), lng + "&"); int cnt = hdr.getNumberOfCards(); // This should remove all three cards associated with // LONG1 hdr.removeCard("LONG1"); assertEquals("deltest", cnt - 3, hdr.getNumberOfCards()); Header.setLongStringsEnabled(false); // With long strings disabled this should only remove one more card. hdr.removeCard("LONG2"); assertEquals("deltest2", cnt - 4, hdr.getNumberOfCards()); } } fits-1.10.0/nom/tam/fits/test/AsciiTableTest.java0000664000175000017500000002742012031031530021736 0ustar frothmaifrothmaipackage nom.tam.fits.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; import nom.tam.util.*; import nom.tam.fits.*; /** This class tests the AsciiTableHDU and AsciiTable FITS * classes and implicitly the ByteFormatter and ByteParser * classes in the nam.tam.util library. * Tests include: * Create columns of every type * Read columns of every type * Create a table column by column * Create a table row by row * Use deferred input on rows * Use deferred input on elements * Read rows, columns and elements from in-memory kernel. * Specify width of columns. * Rewrite data/header in place. * Set and read null elements. */ public class AsciiTableTest { Object[] getSampleCols() { float[] realCol = new float[50]; for (int i = 0; i < realCol.length; i += 1) { realCol[i] = 10000.F * (i) * (i) * (i) + 1; } int[] intCol = (int[]) ArrayFuncs.convertArray(realCol, int.class); long[] longCol = (long[]) ArrayFuncs.convertArray(realCol, long.class); double[] doubleCol = (double[]) ArrayFuncs.convertArray(realCol, double.class); String[] strCol = new String[realCol.length]; for (int i = 0; i < realCol.length; i += 1) { strCol[i] = "ABC" + String.valueOf(realCol[i]) + "CDE"; } return new Object[]{realCol, intCol, longCol, doubleCol, strCol}; } Fits makeAsciiTable() throws Exception { Object[] cols = getSampleCols(); // Create the new ASCII table. Fits f = new Fits(); f.addHDU(Fits.makeHDU(cols)); return f; } public void writeFile(Fits f, String name) throws Exception { BufferedFile bf = new BufferedFile(name, "rw"); f.write(bf); bf.flush(); bf.close(); } @Test public void test() throws Exception { createByColumn(); createByRow(); readByRow(); readByColumn(); readByElement(); modifyTable(); delete(); } public void createByColumn() throws Exception { Fits f = makeAsciiTable(); writeFile(f, "at1.fits"); // Read back the data from the file. f = new Fits("at1.fits"); AsciiTableHDU hdu = (AsciiTableHDU) f.getHDU(1); Object[] inputs = getSampleCols(); Object[] outputs = (Object[]) hdu.getKernel(); for (int i = 0; i < 50; i += 1) { ((String[]) outputs[4])[i] = ((String[]) outputs[4])[i].trim(); } for (int j = 0; j < 5; j += 1) { assertEquals("ByCol:" + j, true, ArrayFuncs.arrayEquals(inputs[j], outputs[j], 1.e-6, 1.e-14)); } } Object[] getRow(int i) { return new Object[]{ new int[]{i}, new float[]{i}, new String[]{"Str" + i} }; } Object[] getRowBlock(int max) { Object[] o = new Object[]{new int[max], new float[max], new String[max]}; for (int i = 0; i < max; i += 1) { ((int[]) o[0])[i] = i; ((float[]) o[1])[i] = i; ((String[]) o[2])[i] = "Str" + i; } return o; } public void createByRow() throws Exception { // Create a table row by row . Fits f = new Fits(); AsciiTable data = new AsciiTable(); Object[] row = new Object[4]; for (int i = 0; i < 50; i += 1) { data.addRow(getRow(i)); } f.addHDU(Fits.makeHDU(data)); writeFile(f, "at2.fits"); // Read it back. f = new Fits("at2.fits"); Object[] output = (Object[]) f.getHDU(1).getKernel(); Object[] input = getRowBlock(50); for (int i = 0; i < 50; i += 1) { String[] str = (String[]) output[2]; String[] istr = (String[]) input[2]; int len1 = str[1].length(); str[i] = str[i].trim(); // The first row would have set the length for all the // remaining rows... if (istr[i].length() > len1) { istr[i] = istr[i].substring(0, len1); } } for (int j = 0; j < 3; j += 1) { assertEquals("ByRow:" + j, true, ArrayFuncs.arrayEquals(input[j], output[j], 1.e-6, 1.e-14)); } } public void readByRow() throws Exception { Fits f = new Fits("at1.fits"); Object[] cols = getSampleCols(); AsciiTableHDU hdu = (AsciiTableHDU) f.getHDU(1); AsciiTable data = (AsciiTable) hdu.getData(); for (int i = 0; i < data.getNRows(); i += 1) { assertEquals("Rows:" + i, 50, data.getNRows()); Object[] row = data.getRow(i); assertEquals("Ascii Rows: float" + i, 1.F, ((float[]) cols[0])[i] / ((float[]) row[0])[0], 1.e-6); assertEquals("Ascii Rows: int" + i, ((int[]) cols[1])[i], ((int[]) row[1])[0]); assertEquals("Ascii Rows: long" + i, ((long[]) cols[2])[i], ((long[]) row[2])[0]); assertEquals("Ascii Rows: double" + i, 1., ((double[]) cols[3])[i] / ((double[]) row[3])[0], 1.e-14); String[] st = (String[]) row[4]; st[0] = st[0].trim(); assertEquals("Ascii Rows: Str" + i, ((String[]) cols[4])[i], ((String[]) row[4])[0]); } } public void readByColumn() throws Exception { Fits f = new Fits("at1.fits"); AsciiTableHDU hdu = (AsciiTableHDU) f.getHDU(1); AsciiTable data = (AsciiTable) hdu.getData(); Object[] cols = getSampleCols(); assertEquals("Number of rows", data.getNRows(), 50); assertEquals("Number of columns", data.getNCols(), 5); for (int j = 0; j < data.getNCols(); j += 1) { Object col = data.getColumn(j); if (j == 4) { String[] st = (String[]) col; for (int i = 0; i < st.length; i += 1) { st[i] = st[i].trim(); } } assertEquals("Ascii Columns:" + j, true, ArrayFuncs.arrayEquals(cols[j], col, 1.e-6, 1.e-14)); } } public void readByElement() throws Exception { Fits f = new Fits("at2.fits"); AsciiTableHDU hdu = (AsciiTableHDU) f.getHDU(1); AsciiTable data = (AsciiTable) hdu.getData(); for (int i = 0; i < data.getNRows(); i += 1) { Object[] row = (Object[]) data.getRow(i); for (int j = 0; j < data.getNCols(); j += 1) { Object val = data.getElement(i, j); assertEquals("Ascii readElement", true, ArrayFuncs.arrayEquals(val, row[j])); } } } public void modifyTable() throws Exception { Fits f = new Fits("at1.fits"); Object[] samp = getSampleCols(); AsciiTableHDU hdu = (AsciiTableHDU) f.getHDU(1); AsciiTable data = (AsciiTable) hdu.getData(); float[] f1 = (float[]) data.getColumn(0); float[] f2 = (float[]) f1.clone(); for (int i = 0; i < f2.length; i += 1) { f2[i] = 2 * f2[i]; } data.setColumn(0, f2); f1 = new float[]{3.14159f}; data.setElement(3, 0, f1); hdu.setNullString(0, "**INVALID**"); data.setNull(5, 0, true); data.setNull(6, 0, true); Object[] row = new Object[5]; row[0] = new float[]{6.28f}; row[1] = new int[]{22}; row[2] = new long[]{0}; row[3] = new double[]{-3}; row[4] = new String[]{"A string"}; data.setRow(5, row); data.setElement(4, 2, new long[]{54321}); BufferedFile bf = new BufferedFile("at1x.fits", "rw"); f.write(bf); f = new Fits("at1x.fits"); AsciiTable tab = (AsciiTable) f.getHDU(1).getData(); Object[] kern = (Object[]) tab.getKernel(); float[] fx = (float[]) kern[0]; int[] ix = (int[]) kern[1]; long[] lx = (long[]) kern[2]; double[] dx = (double[]) kern[3]; String[] sx = (String[]) kern[4]; float[] fy = (float[]) samp[0]; int[] iy = (int[]) samp[1]; long[] ly = (long[]) samp[2]; double[] dy = (double[]) samp[3]; String[] sy = (String[]) samp[4]; assertEquals("Null", true, tab.isNull(6, 0)); assertEquals("Null2", false, tab.isNull(5, 0)); for (int i = 0; i < data.getNRows(); i += 1) { if (i != 5) { if (i != 6) { // Null assertEquals("f" + i, 1., f2[i] / fx[i], 1.e-6); } assertEquals("i" + i, iy[i], ix[i]); if (i == 4) { assertEquals("l4", 54321L, lx[i]); } else { assertEquals("l" + i, ly[i], lx[i]); } assertEquals("d" + i, 1., dy[i] / dx[i], 1.e-14); assertEquals("s" + i, sy[i], sx[i].trim()); } } Object[] r5 = (Object[]) data.getRow(5); String[] st = (String[]) r5[4]; st[0] = st[0].trim(); assertEquals("row5", true, ArrayFuncs.arrayEquals(row, r5, 1.e-6, 1.e-14)); } public void delete() throws Exception { Fits f = new Fits("at1.fits"); TableHDU th = (TableHDU) f.getHDU(1); assertEquals("delrBef", 50, th.getNRows()); th.deleteRows(2, 2); assertEquals("delrAft", 48, th.getNRows()); BufferedFile bf = new BufferedFile("at1y.fits", "rw"); f.write(bf); bf.close(); f = new Fits("at1y.fits"); th = (TableHDU) f.getHDU(1); assertEquals("delrAft2", 48, th.getNRows()); assertEquals("delcBef", 5, th.getNCols()); th.deleteColumnsIndexZero(3, 2); assertEquals("delcAft1", 3, th.getNCols()); th.deleteColumnsIndexZero(0, 2); assertEquals("delcAft2", 1, th.getNCols()); bf = new BufferedFile("at1z.fits", "rw"); f.write(bf); bf.close(); f = new Fits("at1z.fits"); th = (TableHDU) f.getHDU(1); assertEquals("delcAft3", 1, th.getNCols()); } // Make sure that null ASCII strings still // have a least one character in the output column. @Test public void nullAscii() throws Exception { BufferedFile bf = new BufferedFile("at3.fits", "rw"); Object[] o = new Object[]{ new String[]{null,null, null}, new String[]{"","", ""}, new String[]{null, "", null}, new String[]{" ", " ", " "}, new String[]{"abc", "def", null} }; BasicHDU ahdu = FitsFactory.HDUFactory(o); Fits f = new Fits(); f.addHDU(ahdu); f.write(bf); bf.close(); f = new Fits("at3.fits"); BasicHDU bhdu = f.getHDU(1); Header hdr = bhdu.getHeader(); assertEquals(hdr.getStringValue("TFORM1"), "A1"); assertEquals(hdr.getStringValue("TFORM2"), "A1"); assertEquals(hdr.getStringValue("TFORM3"), "A1"); assertEquals(hdr.getStringValue("TFORM4"), "A1"); assertEquals(hdr.getStringValue("TFORM5"), "A3"); } } fits-1.10.0/nom/tam/fits/test/CompressTest.java0000664000175000017500000001640112031031530021526 0ustar frothmaifrothmaipackage nom.tam.fits.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; import nom.tam.image.*; import nom.tam.util.*; import nom.tam.fits.*; import java.net.URL; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; /** Test reading .Z and .gz compressed files. */ public class CompressTest { @Test public void testgz() throws Exception { File fil = new File("."); System.out.println("File is:" + fil.getCanonicalPath()); Fits f = new Fits("http://heasarc.gsfc.nasa.gov/FTP/asca/data/rev2/43021000/images/ad43021000gis25670_lo.totsky.gz"); BasicHDU h = f.readHDU(); int[][] data = (int[][]) h.getKernel(); double sum = 0; for (int i = 0; i < data.length; i += 1) { for (int j = 0; j < data[i].length; j += 1) { sum += data[i][j]; } } assertEquals("ZCompress", sum, 296915., 0); } @Test public void testZ() throws Exception { Fits f = new Fits("http://heasarc.gsfc.nasa.gov/FTP/rosat/data/pspc/processed_data/600000/rp600245n00/rp600245n00_im1.fits.Z"); BasicHDU h = f.readHDU(); short[][] data = (short[][]) h.getKernel(); double sum = 0; for (int i = 0; i < data.length; i += 1) { for (int j = 0; j < data[i].length; j += 1) { sum += data[i][j]; } } assertEquals("ZCompress", sum, 91806., 0); } @Test public void testStream() throws Exception { InputStream is; is = new FileInputStream("test.fits"); assertEquals("Stream1", 300, streamRead(is, false, false)); is = new FileInputStream("test.fits.Z"); assertEquals("Stream2", 300, streamRead(is, false, false)); is = new FileInputStream("test.fits.gz"); assertEquals("Stream3", 300, streamRead(is, false, false)); is = new FileInputStream("test.fits"); assertEquals("Stream4", 300, streamRead(is, false, true)); is = new FileInputStream("test.fits.Z"); assertEquals("Stream5", 300, streamRead(is, false, true)); is = new FileInputStream("test.fits.gz"); assertEquals("Stream6", 300, streamRead(is, false, true)); is = new FileInputStream("test.fits.Z"); assertEquals("Stream7", 300, streamRead(is, true, true)); is = new FileInputStream("test.fits.gz"); assertEquals("Stream8", 300, streamRead(is, true, true)); is = new FileInputStream("test.fits.bz2"); assertEquals("Stream9", 300, streamRead(is, true, true)); } @Test public void testFile() throws Exception { File is = new File("test.fits"); assertEquals("File1", 300, fileRead(is, false, false)); is = new File("test.fits.Z"); assertEquals("File2", 300, fileRead(is, false, false)); is = new File("test.fits.gz"); assertEquals("File3", 300, fileRead(is, false, false)); is = new File("test.fits"); assertEquals("File4", 300, fileRead(is, false, true)); is = new File("test.fits.Z"); assertEquals("File7", 300, fileRead(is, true, true)); is = new File("test.fits.gz"); assertEquals("File8", 300, fileRead(is, true, true)); is = new File("test.fits.bz2"); assertEquals("File9", 300, fileRead(is, true, true)); } @Test public void testString() throws Exception { String is = "test.fits"; assertEquals("String1", 300, stringRead(is, false, false)); is = "test.fits.Z"; assertEquals("String2", 300, stringRead(is, false, false)); is = "test.fits.gz"; assertEquals("String3", 300, stringRead(is, false, false)); is = "test.fits"; assertEquals("String4", 300, stringRead(is, false, true)); is = "test.fits.Z"; assertEquals("String7", 300, stringRead(is, true, true)); is = "test.fits.gz"; assertEquals("String8", 300, stringRead(is, true, true)); is = "test.fits.bz2"; assertEquals("String8", 300, stringRead(is, true, true)); } @Test public void testURL() throws Exception { String is = "test.fits"; assertEquals("String1", 300, urlRead(is, false, false)); is = "test.fits.Z"; assertEquals("String2", 300, urlRead(is, false, false)); is = "test.fits.gz"; assertEquals("String3", 300, urlRead(is, false, false)); is = "test.fits"; assertEquals("String4", 300, urlRead(is, false, true)); is = "test.fits.Z"; assertEquals("String7", 300, urlRead(is, true, true)); is = "test.fits.gz"; assertEquals("String8", 300, urlRead(is, true, true)); is = "test.fits.bz2"; assertEquals("String8", 300, urlRead(is, true, true)); } int urlRead(String is, boolean comp, boolean useComp) throws Exception { File fil = new File(is); String path = fil.getCanonicalPath(); URL u = new URL("file://" + path); Fits f; if (useComp) { f = new Fits(u, comp); } else { f = new Fits(u); } short[][] data = (short[][]) f.readHDU().getKernel(); return total(data); } int streamRead(InputStream is, boolean comp, boolean useComp) throws Exception { Fits f; if (useComp) { f = new Fits(is, comp); } else { f = new Fits(is); } short[][] data = (short[][]) f.readHDU().getKernel(); is.close(); return total(data); } int fileRead(File is, boolean comp, boolean useComp) throws Exception { Fits f; if (useComp) { f = new Fits(is, comp); } else { f = new Fits(is); } short[][] data = (short[][]) f.readHDU().getKernel(); return total(data); } int stringRead(String is, boolean comp, boolean useComp) throws Exception { Fits f; if (useComp) { f = new Fits(is, comp); } else { f = new Fits(is); } short[][] data = (short[][]) f.readHDU().getKernel(); return total(data); } int total(short[][] data) { int total = 0; for (int i = 0; i < data.length; i += 1) { for (int j = 0; j < data[i].length; j += 1) { total += data[i][j]; } } return total; } } fits-1.10.0/nom/tam/fits/test/test.fits0000664000175000017500000001320011566662566020127 0ustar frothmaifrothmaiSIMPLE = T /Primary Header created by MWRFITS v1.4 BITPIX = 16 / NAXIS = 2 / NAXIS1 = 5 / NAXIS2 = 5 / EXTEND = T /Extensions may be present END  fits-1.10.0/nom/tam/fits/test/DateTester.java0000664000175000017500000000457712031031530021152 0ustar frothmaifrothmaipackage nom.tam.fits.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; import nom.tam.fits.FitsDate; /** Test the FITS date class. * This class is derived from the internal testing utilities * in FitsDate written by David Glowacki. */ public class DateTester { @Test public void test() { assertEquals("t1", true, testArg("20/09/79")); assertEquals("t1", true, testArg("1997-07-25")); assertEquals("t1", true, testArg("1987-06-05T04:03:02.01")); assertEquals("t1", true, testArg("1998-03-10T16:58:34")); assertEquals("t1", true, testArg(null)); assertEquals("t1", true, testArg(" ")); assertEquals("t1", false, testArg("20/09/")); assertEquals("t1", false, testArg("/09/79")); assertEquals("t1", false, testArg("09//79")); assertEquals("t1", false, testArg("20/09/79/")); assertEquals("t1", false, testArg("1997-07")); assertEquals("t1", false, testArg("-07-25")); assertEquals("t1", false, testArg("1997--07-25")); assertEquals("t1", false, testArg("1997-07-25-")); assertEquals("t1", false, testArg("5-Aug-1992")); assertEquals("t1", false, testArg("28/02/91 16:32:00")); assertEquals("t1", false, testArg("18-Feb-1993")); assertEquals("t1", false, testArg("nn/nn/nn")); } boolean testArg(String arg) { try { FitsDate fd = new FitsDate(arg); return true; } catch (Exception e) { return false; } } } fits-1.10.0/nom/tam/fits/test/DupTest.java0000664000175000017500000000516312031031530020466 0ustar frothmaifrothmaipackage nom.tam.fits.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import junit.framework.JUnit4TestAdapter; import nom.tam.image.*; import nom.tam.util.*; import nom.tam.fits.*; import java.io.File; import java.util.List; /** Test adding a little junk after a valid image. * We wish to test three scenarios: * Junk at the beginning (should continue to fail) * Short (<80 byte) junk after valid HDU * Long (>80 byte) junk after valid HDU * The last two should succeed after FitsFactory.setAllowTerminalJunk(true). */ public class DupTest { @Test public void test() throws Exception { Fits f = new Fits("test_dup.fits"); Header hdr = f.readHDU().getHeader(); assertEquals("Internal size:", hdr.getSize(), 2880); assertEquals("External size:", hdr.getOriginalSize(), 8640); assertTrue("Has duplicates:", hdr.hadDuplicates()); List dups = hdr.getDuplicates(); System.out.println("Number of duplicates:"+dups.size()); assertTrue("Has dups:", dups != null && dups.size() > 0); assertTrue("Not rewriteable:", !hdr.rewriteable()); BufferedFile bf = new BufferedFile("created_dup.fits", "rw"); f.write(bf); hdr.resetOriginalSize(); assertEquals("External size, after reset", hdr.getOriginalSize(), 2880); Fits g = new Fits("created_dup.fits"); hdr = g.readHDU().getHeader(); assertEquals("Internal size, after rewrite", hdr.getSize(), 2880); assertEquals("External size, after rewrite", hdr.getOriginalSize(), 2880); assertTrue("Now rewriteable", hdr.rewriteable()); assertTrue("No duplicates", !hdr.hadDuplicates()); assertTrue("Dups is null", hdr.getDuplicates() == null); } } fits-1.10.0/nom/tam/fits/test/test.fits.gz0000664000175000017500000000033311566662566020551 0ustar frothmaifrothmai‹“wNHíÑ=OƒPÆñ§¶jm«Õú:žOPBc»9hÄô&Bˆ”Ê:€æBLùö“ 86Ÿß~þç$'P®ÿîˆ80 byte) junk after valid HDU * The last two should succeed after FitsFactory.setAllowTerminalJunk(true). */ public class JunkTest { @Test public void test() throws Exception { Fits f = new Fits(); byte[] bimg = new byte[40]; for (int i = 10; i < bimg.length; i += 1) { bimg[i] = (byte)i; } // Make HDUs of various types. f.addHDU(Fits.makeHDU(bimg)); // Write a FITS file. // Valid FITS with one HDU BufferedFile bf = new BufferedFile("j1.fits", "rw"); f.write(bf); bf.flush(); bf.close(); // Invalid junk with no valid FITS. bf = new BufferedFile("j2.fits", "rw"); bf.write(new byte[10]); bf.close(); // Valid FITS followed by short junk. bf = new BufferedFile("j3.fits", "rw"); f.write(bf); bf.write("JUNKJUNK".getBytes()); bf.close(); // Valid FITS followed by long junk. bf = new BufferedFile("j4.fits", "rw"); f.write(bf); for (int i=0; i<100; i += 1) { bf.write("A random string".getBytes()); } bf.close(); int pos = 0; try { f = new Fits("j1.fits"); f.read(); } catch (Exception e) { pos = 1; } assertTrue("Junk Test: Valid File OK,Dft", readSuccess("j1.fits")); assertTrue("Junk Test: Invalid File Fails, Dft", !readSuccess("j2.fits")); assertTrue("Junk Test: Short junk fails, Dft", !readSuccess("j3.fits")); assertTrue("Junk Test: Long junk fails, Dft", !readSuccess("j4.fits")); FitsFactory.setAllowTerminalJunk(true); assertTrue("Junk Test: Valid File OK,with junk", readSuccess("j1.fits")); assertTrue("Junk Test: Invalid File Fails, with junk", !readSuccess("j2.fits")); assertTrue("Junk Test: Short junk OK, with junk", readSuccess("j3.fits")); assertTrue("Junk Test: Long junk OK, with junk", readSuccess("j4.fits")); FitsFactory.setAllowTerminalJunk(false); assertTrue("Junk Test: Valid File OK,No junk", readSuccess("j1.fits")); assertTrue("Junk Test: Invalid File Fails, No junk", !readSuccess("j2.fits")); assertTrue("Junk Test: Short junk fails, No junk", !readSuccess("j3.fits")); assertTrue("Junk Test: Long junk fails, No junk", !readSuccess("j4.fits")); } boolean readSuccess(String file) { try { Fits f = new Fits(file); f.read(); return true; } catch (Exception e) { return false; } } } fits-1.10.0/nom/tam/fits/test/TilerTest.java0000664000175000017500000000525312031031526021022 0ustar frothmaifrothmaipackage nom.tam.fits.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import nom.tam.fits.*; import nom.tam.util.*; import nom.tam.image.ImageTiler; import java.io.*; import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; /** This class tests the ImageTiler. It * first creates a FITS file and then reads * it back and allows the user to select * tiles. The values of the corner and center * pixels for the selected tile are displayed. * Both file and memory tiles are checked. */ public class TilerTest { void doTile(String test, float[][] data, ImageTiler t, int x, int y, int nx, int ny) throws Exception { float[] tile = new float[nx * ny]; t.getTile(tile, new int[]{y, x}, new int[]{ny, nx}); float sum0 = 0; float sum1 = 0; for (int i = 0; i < nx; i += 1) { for (int j = 0; j < ny; j += 1) { sum0 += tile[i + j * nx]; sum1 += data[j + y][i + x]; } } assertEquals("Tiler" + test, sum0, sum1, 0); } @Test public void test() throws Exception { float[][] data = new float[300][300]; for (int i = 0; i < 300; i += 1) { for (int j = 0; j < 300; j += 1) { data[i][j] = 1000 * i + j; } } Fits f = new Fits(); BufferedFile bf = new BufferedFile("tiler1.fits", "rw"); f.addHDU(Fits.makeHDU(data)); f.write(bf); bf.close(); f = new Fits("tiler1.fits"); ImageHDU h = (ImageHDU) f.readHDU(); ImageTiler t = h.getTiler(); doTile("t1", data, t, 200, 200, 50, 50); doTile("t2", data, t, 133, 133, 72, 26); Object o = h.getData().getKernel(); doTile("t3", data, t, 200, 200, 50, 50); doTile("t4", data, t, 133, 133, 72, 26); } } fits-1.10.0/nom/tam/fits/test/ImageTest.java0000664000175000017500000001360412031031526020764 0ustar frothmaifrothmaipackage nom.tam.fits.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; import nom.tam.image.*; import nom.tam.util.*; import nom.tam.fits.*; import java.io.File; /** Test the ImageHDU, ImageData and ImageTiler classes. * - multiple HDU's in a single file * - deferred input of HDUs * - creating and reading arrays of all permitted types. * - Tiles of 1, 2 and 3 dimensions * - from a file * - from internal data * - Multiple tiles extracted from an image. */ public class ImageTest { @Test public void test() throws Exception { Fits f = new Fits(); byte[][] bimg = new byte[40][40]; for (int i = 10; i < 30; i += 1) { for (int j = 10; j < 30; j += 1) { bimg[i][j] = (byte) (i + j); } } short[][] simg = (short[][]) ArrayFuncs.convertArray(bimg, short.class); int[][] iimg = (int[][]) ArrayFuncs.convertArray(bimg, int.class); long[][] limg = (long[][]) ArrayFuncs.convertArray(bimg, long.class); float[][] fimg = (float[][]) ArrayFuncs.convertArray(bimg, float.class); double[][] dimg = (double[][]) ArrayFuncs.convertArray(bimg, double.class); int[][][] img3 = new int[10][20][30]; for (int i = 0; i < 10; i += 1) { for (int j = 0; j < 20; j += 1) { for (int k = 0; k < 30; k += 1) { img3[i][j][k] = i + j + k; } } } double[] img1 = (double[]) ArrayFuncs.flatten(dimg); // Make HDUs of various types. f.addHDU(Fits.makeHDU(bimg)); f.addHDU(Fits.makeHDU(simg)); f.addHDU(Fits.makeHDU(iimg)); f.addHDU(Fits.makeHDU(limg)); f.addHDU(Fits.makeHDU(fimg)); f.addHDU(Fits.makeHDU(dimg)); f.addHDU(Fits.makeHDU(img3)); f.addHDU(Fits.makeHDU(img1)); assertEquals("HDU count before", f.getNumberOfHDUs(), 8); // Write a FITS file. BufferedFile bf = new BufferedFile("image1.fits", "rw"); f.write(bf); bf.flush(); bf.close(); bf = null; f = null; bf = new BufferedFile("image1.fits"); // Read a FITS file f = new Fits("image1.fits"); BasicHDU[] hdus = f.read(); assertEquals("HDU count after", f.getNumberOfHDUs(), 8); assertEquals("byte image", true, ArrayFuncs.arrayEquals(bimg, hdus[0].getData().getKernel())); assertEquals("short image", true, ArrayFuncs.arrayEquals(simg, hdus[1].getData().getKernel())); assertEquals("int image", true, ArrayFuncs.arrayEquals(iimg, hdus[2].getData().getKernel())); assertEquals("long image", true, ArrayFuncs.arrayEquals(limg, hdus[3].getData().getKernel())); assertEquals("float image", true, ArrayFuncs.arrayEquals(fimg, hdus[4].getData().getKernel())); assertEquals("double image", true, ArrayFuncs.arrayEquals(dimg, hdus[5].getData().getKernel())); assertEquals("int3 image", true, ArrayFuncs.arrayEquals(img3, hdus[6].getData().getKernel())); assertEquals("double1 image", true, ArrayFuncs.arrayEquals(img1, hdus[7].getData().getKernel())); } @Test public void fileTest() throws Exception { byte[][] bimg = new byte[40][40]; for (int i = 10; i < 30; i += 1) { for (int j = 10; j < 30; j += 1) { bimg[i][j] = (byte) (i + j); } } short[][] simg = (short[][]) ArrayFuncs.convertArray(bimg, short.class); int[][] iimg = (int[][]) ArrayFuncs.convertArray(bimg, int.class); long[][] limg = (long[][]) ArrayFuncs.convertArray(bimg, long.class); float[][] fimg = (float[][]) ArrayFuncs.convertArray(bimg, float.class); double[][] dimg = (double[][]) ArrayFuncs.convertArray(bimg, double.class); int[][][] img3 = new int[10][20][30]; for (int i = 0; i < 10; i += 1) { for (int j = 0; j < 20; j += 1) { for (int k = 0; k < 30; k += 1) { img3[i][j][k] = i + j + k; } } } double[] img1 = (double[]) ArrayFuncs.flatten(dimg); Fits f = new Fits(new File("image1.fits")); BasicHDU[] hdus = f.read(); assertEquals("fbyte image", true, ArrayFuncs.arrayEquals(bimg, hdus[0].getData().getKernel())); assertEquals("fshort image", true, ArrayFuncs.arrayEquals(simg, hdus[1].getData().getKernel())); assertEquals("fint image", true, ArrayFuncs.arrayEquals(iimg, hdus[2].getData().getKernel())); assertEquals("flong image", true, ArrayFuncs.arrayEquals(limg, hdus[3].getData().getKernel())); assertEquals("ffloat image", true, ArrayFuncs.arrayEquals(fimg, hdus[4].getData().getKernel())); assertEquals("fdouble image", true, ArrayFuncs.arrayEquals(dimg, hdus[5].getData().getKernel())); assertEquals("fint3 image", true, ArrayFuncs.arrayEquals(img3, hdus[6].getData().getKernel())); assertEquals("fdouble1 image", true, ArrayFuncs.arrayEquals(img1, hdus[7].getData().getKernel())); } } fits-1.10.0/nom/tam/fits/test/PaddingTest.java0000664000175000017500000001270112031031530021300 0ustar frothmaifrothmaipackage nom.tam.fits.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; import nom.tam.image.*; import nom.tam.util.*; import nom.tam.fits.*; import java.io.File; /** Test that we can read files that fail due to lack of padding in the final HDU. */ public class PaddingTest { @Test public void test1() throws Exception { Fits f = new Fits(); byte[][] bimg = new byte[20][20]; for (int i = 0; i < 20; i += 1) { for (int j = 0; j < 20; j += 1) { bimg[i][j] = (byte) (i + j); } } BasicHDU hdu = Fits.makeHDU(bimg); Header hdr = hdu.getHeader(); hdr.addValue("NEWKEY", "TESTVALUE", "Test keyword"); BufferedFile bf = new BufferedFile("padding1.fits", "rw"); hdr.write(bf); bf.writeArray(bimg); // The data but no following padding. bf.flush(); bf.close(); // Now try reading this back. f = new Fits("padding1.fits"); try { f.read(); } catch (PaddingException e) { assertEquals("HDUCount", 0, f.getNumberOfHDUs()); f.addHDU(e.getTruncatedHDU()); assertEquals("HDUCount2", 1, f.getNumberOfHDUs()); } ImageHDU hdu0 = (ImageHDU) (f.getHDU(0)); byte[][] aa = (byte[][]) hdu0.getKernel(); int miss = 0; int match = 0; for (int i = 0; i < 20; i += 1) { for (int j = 0; j < 20; j += 1) { if (aa[i][j] != (byte) (i + j)) { miss += 1; } else { match += 1; } } } assertEquals("PadMiss1:", miss, 0); assertEquals("PadMatch1:", match, 400); // Make sure we got the real header and not the one generated strictly from the data. assertEquals("Update header:", hdu0.getHeader().getStringValue("NEWKEY"), "TESTVALUE"); nom.tam.image.ImageTiler it = hdu0.getTiler(); // Remember that the tile is always a flattened // 1-D representation of the data. byte[] data = (byte[]) it.getTile(new int[]{2, 2}, new int[]{2, 2}); assertEquals("tilet1:", data.length, 4); assertEquals("tilet2:", data[0] + 0, 4); assertEquals("tilet3:", data[1] + 0, 5); assertEquals("tilet4:", data[2] + 0, 5); assertEquals("tilet5:", data[3] + 0, 6); } @Test public void test2() throws Exception { Fits f = new Fits(); byte[][] bimg = new byte[20][20]; for (int i = 0; i < 20; i += 1) { for (int j = 0; j < 20; j += 1) { bimg[i][j] = (byte) (i + j); } } BasicHDU hdu = Fits.makeHDU(bimg); f.addHDU(hdu); // First create a FITS file with a truncated second HDU. BufferedFile bf = new BufferedFile("padding2.fits", "rw"); f.write(bf); hdu.getHeader().setXtension("IMAGE"); Cursor curs = hdu.getHeader().iterator(); int cnt = 0; // Write the header while (curs.hasNext()) { bf.write(((HeaderCard) curs.next()).toString().getBytes()); cnt += 1; } // The padding between header and data byte[] b = new byte[(36 - cnt) * 80]; // Assuming fewer than 36 cards. for (int i = 0; i < b.length; i += 1) { b[i] = 32; // i.e., a blank } bf.write(b); for (int i = 0; i < 20; i += 1) { for (int j = 0; j < 20; j += 1) { bimg[i][j] = (byte) (2 * (i + j)); } } bf.writeArray(bimg); // The data but no following padding. bf.flush(); bf.close(); // Now try reading this back. f = new Fits("padding2.fits"); try { f.read(); } catch (PaddingException e) { assertEquals("HDUCount", 1, f.getNumberOfHDUs()); f.addHDU(e.getTruncatedHDU()); assertEquals("HDUCount2", 2, f.getNumberOfHDUs()); } ImageHDU hdu0 = (ImageHDU) (f.getHDU(0)); ImageHDU hdu1 = (ImageHDU) (f.getHDU(1)); byte[][] aa = (byte[][]) hdu0.getKernel(); byte[][] bb = (byte[][]) hdu1.getKernel(); int miss = 0; int match = 0; for (int i = 0; i < 20; i += 1) { for (int j = 0; j < 20; j += 1) { if (bb[i][j] != (byte) (2 * aa[i][j])) { miss += 1; } else { match += 1; } } } assertEquals("PadMiss2:", miss, 0); assertEquals("PadMatch2:", match, 400); } } fits-1.10.0/nom/tam/fits/test/ChecksumTest.java0000664000175000017500000000324312031031530021475 0ustar frothmaifrothmai package nom.tam.fits.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; import nom.tam.fits.*; import nom.tam.util.*; import java.io.*; /** * * @author tmcglynn */ public class ChecksumTest { @Test public void testChecksum() throws Exception { int[][] data = new int[][] {{1,2}, {3,4}, {5,6}}; Fits f = new Fits(); BasicHDU bhdu = FitsFactory.HDUFactory(data); f.addHDU(bhdu); Fits.setChecksum(bhdu); ByteArrayOutputStream bs = new ByteArrayOutputStream(); BufferedDataOutputStream bdos = new BufferedDataOutputStream(bs); f.write(bdos); bdos.close(); byte[] stream = bs.toByteArray(); long chk = Fits.checksum(stream); int val = (int)chk; assertEquals("CheckSum test", -1, val); } } fits-1.10.0/nom/tam/fits/test/test.fits.Z0000664000175000017500000000056011566662566020344 0ustar frothmaifrothmaiS’4¤=*\Ȱ!/ ÈIÓ&Œœ< ” C¦Œcäl¤S† 1›\‘b$ •) ìÄpA£áB!.¡$Ár0¡ÍŸ cØ€´¨Ñ£ Á’fO¤6e…JµêA¥L§Äxjõ`©]ÃÅÚT*B±__ˆ]k³*EœáÚõá‹"xHº™“æÍ^1Š)ŽÈ9eÜÐa "î\Æ#KžL¹²å˘3kÞ̹³çÏ C‹Mº´éÓ¨S«^ͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀ×€P€ € 0Ѐ@€PP€`N¿¾ýûøóëßÏ¿¿ÿÿ(à€hà&¨à‚ 6èàƒF(á„Vhá…f¨á†vèᇠ†(âˆ$–hâ‰(¦¨âŠ,¶èâ‹0Æ(ãŒ4Öhã8æ¨ãŽ)fits-1.10.0/nom/tam/fits/test/BinaryTableTest.java0000664000175000017500000006507712031031530022144 0ustar frothmaifrothmaipackage nom.tam.fits.test; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import nom.tam.util.*; import nom.tam.fits.*; import java.io.*; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; import junit.framework.JUnit4TestAdapter; import java.lang.reflect.*; /** This class tests the binary table classes for * the Java FITS library, notably BinaryTableHDU, * BinaryTable, FitsHeap and the utility class ColumnTable. * Tests include: *
 *     Reading and writing data of all valid types.
 *     Reading and writing variable length da
 *     Creating binary tables from:
 *        Object[][] array
 *        Object[] array
 *        ColumnTable
 *        Column x Column
 *        Row x Row
 *     Read binary table
 *        Row x row
 *        Element x element
 *     Modify
 *        Row, column, element
 *     Rewrite binary table in place
 * 
*/ public class BinaryTableTest { byte[] bytes = new byte[50]; byte[][] bits = new byte[50][2]; boolean[] bools = new boolean[50]; short[][] shorts = new short[50][3]; int[] ints = new int[50]; float[][][] floats = new float[50][4][4]; double[] doubles = new double[50]; long[] longs = new long[50]; String[] strings = new String[50]; float[][] vf = new float[50][]; short[][] vs = new short[50][]; double[][] vd = new double[50][]; boolean[][] vbool = new boolean[50][]; float[][][] vc = new float[50][][]; double[][][] vdc = new double[50][][]; float[][] complex = new float[50][2]; float[][][] complex_arr = new float[50][4][2]; double[][] dcomplex = new double[50][2]; double[][][] dcomplex_arr = new double[50][4][2]; @Before public void initialize() { for (int i = 0; i < bytes.length; i += 1) { bytes[i] = (byte) (2 * i); bits[i][0] = bytes[i]; bits[i][1] = (byte) (~bytes[i]); bools[i] = (bytes[i] % 8) == 0 ? true : false; shorts[i][0] = (short) (2 * i); shorts[i][1] = (short) (3 * i); shorts[i][2] = (short) (4 * i); ints[i] = i * i; for (int j = 0; j < 4; j += 1) { for (int k = 0; k < 4; k += 1) { floats[i][j][k] = (float) (i + j * Math.exp(k)); } } doubles[i] = 3 * Math.sin(i); longs[i] = i * i * i * i; strings[i] = "abcdefghijklmnopqrstuvwxzy".substring(0, i % 20); vf[i] = new float[i + 1]; vf[i][i / 2] = i * 3; vs[i] = new short[i / 10 + 1]; vs[i][i / 10] = (short) -i; vd[i] = new double[i % 2 == 0 ? 1 : 2]; vd[i][0] = 99.99; vbool[i] = new boolean[i / 10]; if (i >= 10) { vbool[i][0] = i % 2 == 1; } int m5 = i % 5; vc[i] = new float[m5][]; for (int j = 0; j < m5; j += 1) { vc[i][j] = new float[2]; vc[i][j][0] = i; vc[i][j][1] = -j; } vdc[i] = new double[m5][]; for (int j = 0; j < m5; j += 1) { vdc[i][j] = new double[2]; vdc[i][j][0] = -j; vdc[i][j][1] = i; } double rad = 2 * i * Math.PI / bytes.length; complex[i][0] = (float) (Math.cos(rad)); complex[i][1] = (float) (Math.sin(rad)); dcomplex[i][0] = complex[i][0]; dcomplex[i][1] = complex[i][1]; for (int j = 0; j < 4; j += 1) { complex_arr[i][j][0] = (j + 1) * complex[i][0]; complex_arr[i][j][1] = (j + 1) * complex[i][1]; dcomplex_arr[i][j][0] = (j + 1) * complex[i][0]; dcomplex_arr[i][j][1] = (j + 1) * complex[i][1]; } } } @Test public void testSimpleIO() throws Exception { FitsFactory.setUseAsciiTables(false); Fits f = new Fits(); Object[] data = new Object[]{bytes, bits, bools, shorts, ints, floats, doubles, longs, strings, complex, dcomplex, complex_arr, dcomplex_arr}; f.addHDU(Fits.makeHDU(data)); BinaryTableHDU bhdu = (BinaryTableHDU) f.getHDU(1); bhdu.setColumnName(0, "bytes", null); bhdu.setColumnName(1, "bits", "bits later on"); bhdu.setColumnName(6, "doubles", null); bhdu.setColumnName(5, "floats", "4 x 4 array"); BufferedFile bf = new BufferedFile("bt1.fits", "rw"); f.write(bf); bf.flush(); bf.close(); f = new Fits("bt1.fits"); f.read(); assertEquals("NHDU", 2, f.getNumberOfHDUs()); BinaryTableHDU thdu = (BinaryTableHDU) f.getHDU(1); Header hdr = thdu.getHeader(); assertEquals("HDR1", data.length, hdr.getIntValue("TFIELDS")); assertEquals("HDR2", 2, hdr.getIntValue("NAXIS")); assertEquals("HDR3", 8, hdr.getIntValue("BITPIX")); assertEquals("HDR4", "BINTABLE", hdr.getStringValue("XTENSION")); assertEquals("HDR5", "bytes", hdr.getStringValue("TTYPE1")); assertEquals("HDR6", "doubles", hdr.getStringValue("TTYPE7")); for (int i = 0; i < data.length; i += 1) { Object col = thdu.getColumn(i); if (i == 8) { String[] st = (String[]) col; for (int j = 0; j < st.length; j += 1) { st[j] = st[j].trim(); } } assertEquals("Data" + i, true, ArrayFuncs.arrayEquals(data[i], col)); } } @Test public void testSimpleComplex() throws Exception { try { FitsFactory.setUseAsciiTables(false); Fits f = new Fits(); Object[] data = new Object[]{bytes, bits, bools, shorts, ints, floats, doubles, longs, strings, complex, dcomplex, complex_arr, dcomplex_arr}; BinaryTableHDU bhdu = (BinaryTableHDU) Fits.makeHDU(data); bhdu.setComplexColumn(9); bhdu.setComplexColumn(10); bhdu.setComplexColumn(11); bhdu.setComplexColumn(12); f.addHDU(bhdu); bhdu.setColumnName(9, "Complex1", null); BufferedFile bf = new BufferedFile("bt1c.fits", "rw"); f.write(bf); bf.flush(); bf.close(); f = new Fits("bt1c.fits"); f.read(); assertEquals("NHDUc", 2, f.getNumberOfHDUs()); BinaryTableHDU thdu = null; thdu = (BinaryTableHDU) f.getHDU(1); Header hdr = thdu.getHeader(); for (int i = 0; i < data.length; i += 1) { Object col = thdu.getColumn(i); if (i == 8) { String[] st = (String[]) col; for (int j = 0; j < st.length; j += 1) { st[j] = st[j].trim(); } } int n = Array.getLength(data[i]); assertEquals("DataC" + i, true, ArrayFuncs.arrayEquals(data[i], col)); } } catch (Exception e) { e.printStackTrace(System.err); throw e; } } @Test public void testRowDelete() throws Exception { Fits f = new Fits("bt1.fits"); f.read(); BinaryTableHDU thdu = (BinaryTableHDU) f.getHDU(1); assertEquals("Del1", 50, thdu.getNRows()); thdu.deleteRows(10, 20); assertEquals("Del2", 30, thdu.getNRows()); double[] dbl = (double[]) thdu.getColumn(6); assertEquals("del3", dbl[9], doubles[9], 0); assertEquals("del4", dbl[10], doubles[30], 0); BufferedFile bf = new BufferedFile("bt1x.fits", "rw"); f.write(bf); bf.close(); f = new Fits("bt1x.fits"); f.read(); thdu = (BinaryTableHDU) f.getHDU(1); dbl = (double[]) thdu.getColumn(6); assertEquals("del5", 30, thdu.getNRows()); assertEquals("del6", 13, thdu.getNCols()); assertEquals("del7", dbl[9], doubles[9], 0); assertEquals("del8", dbl[10], doubles[30], 0); thdu.deleteRows(20); assertEquals("del9", 20, thdu.getNRows()); dbl = (double[]) thdu.getColumn(6); assertEquals("del10", 20, dbl.length); assertEquals("del11", dbl[0], doubles[0], 0); assertEquals("del12", dbl[19], doubles[39], 0); } @Test public void testVar() throws Exception { try { Object[] data = new Object[]{floats, vf, vs, vd, shorts, vbool, vc, vdc}; BasicHDU hdu = Fits.makeHDU(data); Fits f = new Fits(); f.addHDU(hdu); BufferedDataOutputStream bdos = new BufferedDataOutputStream(new FileOutputStream("bt2.fits")); f.write(bdos); bdos.close(); f = new Fits("bt2.fits"); f.read(); BinaryTableHDU bhdu = (BinaryTableHDU) f.getHDU(1); Header hdr = bhdu.getHeader(); assertEquals("var1", true, hdr.getIntValue("PCOUNT") > 0); assertEquals("var2", data.length, hdr.getIntValue("TFIELDS")); for (int i = 0; i < data.length; i += 1) { assertEquals("vardata" + i, true, ArrayFuncs.arrayEquals(data[i], bhdu.getColumn(i))); } } catch (Exception e) { e.printStackTrace(System.err); throw e; } } @Test public void testSet() throws Exception { Fits f = new Fits("bt2.fits"); f.read(); BinaryTableHDU bhdu = (BinaryTableHDU) f.getHDU(1); Header hdr = bhdu.getHeader(); // Check the various set methods on variable length data. float[] dta = (float[]) bhdu.getElement(4, 1); dta = new float[]{22, 21, 20}; bhdu.setElement(4, 1, dta); BufferedDataOutputStream bdos = new BufferedDataOutputStream(new FileOutputStream("bt2a.fits")); f.write(bdos); bdos.close(); f = new Fits("bt2a.fits"); bhdu = (BinaryTableHDU) f.getHDU(1); float[] xdta = (float[]) bhdu.getElement(4, 1); assertEquals("ts1", true, ArrayFuncs.arrayEquals(dta, xdta)); assertEquals("ts2", true, ArrayFuncs.arrayEquals(bhdu.getElement(3, 1), vf[3])); assertEquals("ts4", true, ArrayFuncs.arrayEquals(bhdu.getElement(5, 1), vf[5])); assertEquals("ts5", true, ArrayFuncs.arrayEquals(bhdu.getElement(4, 1), dta)); float tvf[] = new float[]{101, 102, 103, 104}; vf[4] = tvf; bhdu.setColumn(1, vf); assertEquals("ts6", true, ArrayFuncs.arrayEquals(bhdu.getElement(3, 1), vf[3])); assertEquals("ts7", true, ArrayFuncs.arrayEquals(bhdu.getElement(4, 1), vf[4])); assertEquals("ts8", true, ArrayFuncs.arrayEquals(bhdu.getElement(5, 1), vf[5])); bdos = new BufferedDataOutputStream(new FileOutputStream("bt2b.fits")); f.write(bdos); bdos.close(); f = new Fits("bt2b.fits"); bhdu = (BinaryTableHDU) f.getHDU(1); assertEquals("ts9", true, ArrayFuncs.arrayEquals(bhdu.getElement(3, 1), vf[3])); assertEquals("ts10", true, ArrayFuncs.arrayEquals(bhdu.getElement(4, 1), vf[4])); assertEquals("ts11", true, ArrayFuncs.arrayEquals(bhdu.getElement(5, 1), vf[5])); Object[] rw = bhdu.getRow(4); float[] trw = new float[]{-1, -2, -3, -4, -5, -6}; rw[1] = trw; bhdu.setRow(4, rw); assertEquals("ts12", true, ArrayFuncs.arrayEquals(bhdu.getElement(3, 1), vf[3])); assertEquals("ts13", false, ArrayFuncs.arrayEquals(bhdu.getElement(4, 1), vf[4])); assertEquals("ts14", true, ArrayFuncs.arrayEquals(bhdu.getElement(4, 1), trw)); assertEquals("ts15", true, ArrayFuncs.arrayEquals(bhdu.getElement(5, 1), vf[5])); bdos = new BufferedDataOutputStream(new FileOutputStream("bt2c.fits")); f.write(bdos); bdos.close(); f = new Fits("bt2c.fits"); bhdu = (BinaryTableHDU) f.getHDU(1); assertEquals("ts16", true, ArrayFuncs.arrayEquals(bhdu.getElement(3, 1), vf[3])); assertEquals("ts17", false, ArrayFuncs.arrayEquals(bhdu.getElement(4, 1), vf[4])); assertEquals("ts18", true, ArrayFuncs.arrayEquals(bhdu.getElement(4, 1), trw)); assertEquals("ts19", true, ArrayFuncs.arrayEquals(bhdu.getElement(5, 1), vf[5])); } @Test public void buildByColumn() throws Exception { BinaryTable btab = new BinaryTable(); btab.addColumn(floats); btab.addColumn(vf); btab.addColumn(strings); btab.addColumn(vbool); btab.addColumn(ints); btab.addColumn(vc); btab.addColumn(complex); Fits f = new Fits(); f.addHDU(Fits.makeHDU(btab)); BufferedDataOutputStream bdos = new BufferedDataOutputStream(new FileOutputStream("bt3.fits")); f.write(bdos); f = new Fits("bt3.fits"); BinaryTableHDU bhdu = (BinaryTableHDU) f.getHDU(1); btab = (BinaryTable) bhdu.getData(); assertEquals("col1", true, ArrayFuncs.arrayEquals(floats, bhdu.getColumn(0))); assertEquals("col2", true, ArrayFuncs.arrayEquals(vf, bhdu.getColumn(1))); assertEquals("col6", true, ArrayFuncs.arrayEquals(vc, bhdu.getColumn(5))); assertEquals("col7", true, ArrayFuncs.arrayEquals(complex, bhdu.getColumn(6))); String[] col = (String[]) bhdu.getColumn(2); for (int i = 0; i < col.length; i += 1) { col[i] = col[i].trim(); } assertEquals("coi3", true, ArrayFuncs.arrayEquals(strings, col)); assertEquals("col4", true, ArrayFuncs.arrayEquals(vbool, bhdu.getColumn(3))); assertEquals("col5", true, ArrayFuncs.arrayEquals(ints, bhdu.getColumn(4))); } @Test public void buildByRow() throws Exception { Fits f = new Fits("bt2.fits"); f.read(); BinaryTableHDU bhdu = (BinaryTableHDU) f.getHDU(1); Header hdr = bhdu.getHeader(); BinaryTable btab = (BinaryTable) bhdu.getData(); for (int i = 0; i < 50; i += 1) { Object[] row = btab.getRow(i); float[] qx = (float[]) row[1]; float[][] p = (float[][]) row[0]; p[0][0] = (float) (i * Math.sin(i)); btab.addRow(row); } f = new Fits(); f.addHDU(Fits.makeHDU(btab)); BufferedFile bf = new BufferedFile("bt4.fits", "rw"); f.write(bf); bf.flush(); bf.close(); f = new Fits("bt4.fits"); btab = (BinaryTable) f.getHDU(1).getData(); assertEquals("row1", 100, btab.getNRows()); // Try getting data before we read in the table. float[][][] xf = (float[][][]) btab.getColumn(0); assertEquals("row2", (float) 0., xf[50][0][0], 0); assertEquals("row3", (float) (49 * Math.sin(49)), xf[99][0][0], 0); for (int i = 0; i < xf.length; i += 3) { boolean[] ba = (boolean[]) btab.getElement(i, 5); float[] fx = (float[]) btab.getElement(i, 1); int trow = i % 50; assertEquals("row4", true, ArrayFuncs.arrayEquals(ba, vbool[trow])); assertEquals("row6", true, ArrayFuncs.arrayEquals(fx, vf[trow])); } float[][][] cmplx = (float[][][]) btab.getColumn(6); for (int i = 0; i < vc.length; i += 1) { for (int j = 0; j < vc[i].length; j += 1) { assertEquals("rowvc" + i + "_" + j, true, ArrayFuncs.arrayEquals(vc[i][j], cmplx[i + vc.length][j])); } } // Fill the table. f.getHDU(1).getData(); xf = (float[][][]) btab.getColumn(0); assertEquals("row7", 0.F, xf[50][0][0], 0); assertEquals("row8", (float) (49 * Math.sin(49)), xf[99][0][0], 0); for (int i = 0; i < xf.length; i += 3) { boolean[] ba = (boolean[]) btab.getElement(i, 5); float[] fx = (float[]) btab.getElement(i, 1); int trow = i % 50; assertEquals("row9", true, ArrayFuncs.arrayEquals(ba, vbool[trow])); assertEquals("row11", true, ArrayFuncs.arrayEquals(fx, vf[trow])); } } @Test public void testObj() throws Exception { /*** Create a binary table from an Object[][] array */ Object[][] x = new Object[5][3]; for (int i = 0; i < 5; i += 1) { x[i][0] = new float[]{i}; x[i][1] = new String("AString" + i); x[i][2] = new int[][]{{i, 2 * i}, {3 * i, 4 * i}}; } Fits f = new Fits(); BasicHDU hdu = Fits.makeHDU(x); f.addHDU(hdu); BufferedFile bf = new BufferedFile("bt5.fits", "rw"); f.write(bf); bf.close(); /** Now get rid of some columns */ BinaryTableHDU xhdu = (BinaryTableHDU) hdu; // First column assertEquals("delcol1", 3, xhdu.getNCols()); xhdu.deleteColumnsIndexOne(1, 1); assertEquals("delcol2", 2, xhdu.getNCols()); xhdu.deleteColumnsIndexZero(1, 1); assertEquals("delcol3", 1, xhdu.getNCols()); bf = new BufferedFile("bt6.fits", "rw"); f.write(bf); f = new Fits("bt6.fits"); xhdu = (BinaryTableHDU) f.getHDU(1); assertEquals("delcol4", 1, xhdu.getNCols()); } @Test public void testDegenerate() throws Exception { String[] sa = new String[10]; int[][] ia = new int[10][0]; Fits f = new Fits(); for (int i = 0; i < sa.length; i += 1) { sa[i] = ""; } Object[] data = new Object[]{sa, ia}; BinaryTableHDU bhdu = (BinaryTableHDU) Fits.makeHDU(data); Header hdr = bhdu.getHeader(); f.addHDU(bhdu); BufferedFile bf = new BufferedFile("bt7.fits", "rw"); f.write(bf); bf.close(); assertEquals("degen1", 2, hdr.getIntValue("TFIELDS")); assertEquals("degen2", 10, hdr.getIntValue("NAXIS2")); assertEquals("degen3", 0, hdr.getIntValue("NAXIS1")); f = new Fits("bt7.fits"); bhdu = (BinaryTableHDU) f.getHDU(1); hdr = bhdu.getHeader(); assertEquals("degen4", 2, hdr.getIntValue("TFIELDS")); assertEquals("degen5", 10, hdr.getIntValue("NAXIS2")); assertEquals("degen6", 0, hdr.getIntValue("NAXIS1")); } @Test public void testDegen2() throws Exception { FitsFactory.setUseAsciiTables(false); Object[] data = new Object[]{ new String[]{"a", "b", "c", "d", "e", "f"}, new int[]{1, 2, 3, 4, 5, 6}, new float[]{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}, new String[]{"", "", "", "", "", ""}, new String[]{"a", "", "c", "", "e", "f"}, new String[]{"", "b", "c", "d", "e", "f"}, new String[]{"a", "b", "c", "d", "e", ""}, new String[]{null, null, null, null, null, null}, new String[]{"a", null, "c", null, "e", "f"}, new String[]{null, "b", "c", "d", "e", "f"}, new String[]{"a", "b", "c", "d", "e", null} }; Fits f = new Fits(); f.addHDU(Fits.makeHDU(data)); BufferedFile ff = new BufferedFile("bt8.fits", "rw"); f.write(ff); f = new Fits("bt8.fits"); BinaryTableHDU bhdu = (BinaryTableHDU) f.getHDU(1); assertEquals("deg21", "e", bhdu.getElement(4, data.length - 1)); assertEquals("deg22", "", bhdu.getElement(5, data.length - 1)); String[] col = (String[]) bhdu.getColumn(0); assertEquals("deg23", "a", col[0]); assertEquals("deg24", "f", col[5]); col = (String[]) bhdu.getColumn(3); assertEquals("deg25", "", col[0]); assertEquals("deg26", "", col[5]); col = (String[]) bhdu.getColumn(7); // All nulls assertEquals("deg27", "", col[0]); assertEquals("deg28", "", col[5]); col = (String[]) bhdu.getColumn(8); assertEquals("deg29", "a", col[0]); assertEquals("deg210", "", col[1]); } @Test public void testMultHDU() throws Exception { BufferedFile ff = new BufferedFile("bt9.fits", "rw"); Object[] data = new Object[]{bytes, bits, bools, shorts, ints, floats, doubles, longs, strings}; Fits f = new Fits(); // Add two identical HDUs f.addHDU(Fits.makeHDU(data)); f.addHDU(Fits.makeHDU(data)); f.write(ff); ff.close(); f = new Fits("bt9.fits"); f.readHDU(); BinaryTableHDU hdu; // This would fail before... int count = 0; while ((hdu = (BinaryTableHDU) f.readHDU()) != null) { int nrow = hdu.getHeader().getIntValue("NAXIS2"); count += 1; assertEquals(nrow, 50); for (int i = 0; i < nrow; i += 1) { Object o = hdu.getRow(i); } } assertEquals(count, 2); } @Test public void testByteArray() { String[] sarr = {"abc", " de", "f"}; byte[] barr = {'a', 'b', 'c', ' ', 'b', 'c', 'a', 'b', ' '}; byte[] obytes = nom.tam.fits.FitsUtil.stringsToByteArray(sarr, 3); assertEquals("blen", obytes.length, 9); assertEquals("b1", obytes[0], (byte) 'a'); assertEquals("b1", obytes[1], (byte) 'b'); assertEquals("b1", obytes[2], (byte) 'c'); assertEquals("b1", obytes[3], (byte) ' '); assertEquals("b1", obytes[4], (byte) 'd'); assertEquals("b1", obytes[5], (byte) 'e'); assertEquals("b1", obytes[6], (byte) 'f'); assertEquals("b1", obytes[7], (byte) ' '); assertEquals("b1", obytes[8], (byte) ' '); String[] ostrings = nom.tam.fits.FitsUtil.byteArrayToStrings(barr, 3); assertEquals("slen", ostrings.length, 3); assertEquals("s1", ostrings[0], "abc"); assertEquals("s2", ostrings[1], "bc"); assertEquals("s3", ostrings[2], "ab"); } @Test public void columnMetaTest() throws Exception { Object[] data = new Object[]{shorts, ints, floats, doubles}; Fits f = new Fits(); // Add two identical HDUs BinaryTableHDU bhdu = (BinaryTableHDU) Fits.makeHDU(data); f.addHDU(bhdu); // makeHDU creates the TFORM keywords and sometimes // the TDIM keywords. Let's add some additional // column metadata. For each column we'll want a TTYPE, TCOMM, // TUNIT and TX and TY // value and we want the final header to be in this order // TTYPE, TCOMM, TFORM, [TDIM,] TUNIT, TX, TY int oldNCols = bhdu.getNCols(); for (int i = 0; i < bhdu.getNCols(); i += 1) { bhdu.setColumnMeta(i, "TTYPE", "NAM" + (i + 1), null, false); bhdu.setColumnMeta(i, "TCOMM", true, "Comment in comment", false); bhdu.setColumnMeta(i, "TUNIT", "UNIT" + (i + 1), null, true); bhdu.setColumnMeta(i, "TX", (i + 1), null, true); bhdu.setColumnMeta(i, "TY", 2. * (i + 1), null, true); } BufferedFile ff = new BufferedFile("bt10.fits", "rw"); f.write(ff); ff.close(); f = new Fits("bt10.fits"); bhdu = (BinaryTableHDU) f.getHDU(1); Header hdr = bhdu.getHeader(); assertEquals("metaCount", oldNCols, bhdu.getNCols()); for (int i = 0; i < bhdu.getNCols(); i += 1) { // If this worked, the first header should be the TTYPE hdr.findCard("TTYPE" + (i + 1)); HeaderCard hc = hdr.nextCard(); assertEquals("M" + i + "0", "TTYPE" + (i + 1), hc.getKey()); hc = hdr.nextCard(); assertEquals("M" + i + "A", "TCOMM" + (i + 1), hc.getKey()); hc = hdr.nextCard(); assertEquals("M" + i + "B", "TFORM" + (i + 1), hc.getKey()); hc = hdr.nextCard(); // There may have been a TDIM keyword inserted automatically. Let's // skip it if it was. It should only appear immediately after the // TFORM keyword. if (hc.getKey().startsWith("TDIM")) { hc = hdr.nextCard(); } assertEquals("M" + i + "C", "TUNIT" + (i + 1), hc.getKey()); hc = hdr.nextCard(); assertEquals("M" + i + "D", "TX" + (i + 1), hc.getKey()); hc = hdr.nextCard(); assertEquals("M" + i + "E", "TY" + (i + 1), hc.getKey()); } } @Test public void specialStringsTest() throws Exception { String[] strings = new String[]{ "abc", "abc\000", "abc\012abc", "abc\000abc", "abc\177", "abc\001def\002ghi\003" }; String[] results1 = new String[]{ strings[0], strings[0], strings[2], strings[0], strings[4], strings[5] }; String[] results2 = new String[]{ strings[0], strings[0], "abc abc", strings[0], "abc ", "abc def ghi " }; FitsFactory.setUseAsciiTables(false); FitsFactory.setCheckAsciiStrings(false); Fits f = new Fits(); Object[] objs = new Object[]{strings}; BinaryTableHDU bhdu = (BinaryTableHDU) Fits.makeHDU(objs); f.addHDU(bhdu); BufferedFile bf = new BufferedFile("bt11a.fits", "rw"); f.write(bf); bf.close(); f = new Fits("bt11a.fits"); bhdu = (BinaryTableHDU) f.getHDU(1); String[] vals = (String[]) bhdu.getColumn(0); for (int i = 0; i < strings.length; i += 1) { assertEquals("ssa" + i, results1[i], vals[i]); } FitsFactory.setCheckAsciiStrings(true); System.err.println(" A warning about invalid ASCII strings should follow."); f = new Fits(); bhdu = (BinaryTableHDU) Fits.makeHDU(objs); f.addHDU(bhdu); bf = new BufferedFile("bt11b.fits", "rw"); f.write(bf); bf.close(); f = new Fits("bt11b.fits"); bhdu = (BinaryTableHDU) f.getHDU(1); vals = (String[]) bhdu.getColumn(0); for (int i = 0; i < strings.length; i += 1) { assertEquals("ssb" + i, results2[i], vals[i]); } FitsFactory.setCheckAsciiStrings(false); } } fits-1.10.0/nom/tam/fits/HeaderOrder.java0000664000175000017500000001164612031033644020316 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This class implements a comparator which ensures * that FITS keywords are written out in a proper order. */ public class HeaderOrder implements java.util.Comparator { /** Can two cards be exchanged when being written out? */ public boolean equals(Object a, Object b) { return compare(a, b) == 0; } /** Which order should the cards indexed by these keys * be written out? This method assumes that the * arguments are either the FITS Header keywords as * strings, and some other type (or null) for comment * style keywords. * * @return -1 if the first argument should be written first
* 1 if the second argument should be written first
* 0 if either is legal. */ public int compare(Object a, Object b) { // Note that we look at each of the ordered FITS keywords in the required // order. String c1, c2; if (a != null && a instanceof String) { c1 = (String) a; } else { c1 = " "; } if (b != null && b instanceof String) { c2 = (String) b; } else { c2 = " "; } // Equals are equal if (c1.equals(c2)) { return 0; } // Now search in the order in which cards must appear // in the header. if (c1.equals("SIMPLE") || c1.equals("XTENSION")) { return -1; } if (c2.equals("SIMPLE") || c2.equals("XTENSION")) { return 1; } if (c1.equals("BITPIX")) { return -1; } if (c2.equals("BITPIX")) { return 1; } if (c1.equals("NAXIS")) { return -1; } if (c2.equals("NAXIS")) { return 1; } // Check the NAXISn cards. These must // be in axis order. if (naxisN(c1) > 0) { if (naxisN(c2) > 0) { if (naxisN(c1) < naxisN(c2)) { return -1; } else { return 1; } } return -1; } if (naxisN(c2) > 0) { return 1; } // The EXTEND keyword is no longer required in the FITS standard // but in earlier versions of the standard it was required to // be here if present in the primary data array. if (c1.equals("EXTEND")) { return -1; } if (c2.equals("EXTEND")) { return 1; } if (c1.equals("PCOUNT")) { return -1; } if (c2.equals("PCOUNT")) { return 1; } if (c1.equals("GCOUNT")) { return -1; } if (c2.equals("GCOUNT")) { return 1; } if (c1.equals("TFIELDS")) { return -1; } if (c2.equals("TFIELDS")) { return 1; } // In principal this only needs to be in the first 36 cards, // but we put it here since it's convenient. BLOCKED is // deprecated currently. if (c1.equals("BLOCKED")) { return -1; } if (c2.equals("BLOCKED")) { return 1; } // Note that this must be at the end, so the // values returned are inverted. if (c1.equals("END")) { return 1; } if (c2.equals("END")) { return -1; } // All other cards can be in any order. return 0; } /** Find the index for NAXISn keywords */ private int naxisN(String key) { if (key.length() > 5 && key.substring(0, 5).equals("NAXIS")) { for (int i = 5; i < key.length(); i += 1) { boolean number = true; char c = key.charAt(i); if ('0' > c || c > '9') { number = false; break; } if (number) { return Integer.parseInt(key.substring(5)); } } } return -1; } } fits-1.10.0/nom/tam/fits/PaddingException.java0000664000175000017500000000506512031033644021355 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** * This exception is thrown if an error is found * reading the padding following a valid FITS HDU. * This padding is required by the FITS standard, but * some FITS writes forego writing it. To access such data * users can use something like: * * * Fits f = new Fits("somefile"); * try { * f.read(); * } catch (PaddingException e) { * f.addHDU(e.getHDU()); * } * * to ensure that a truncated HDU is included in the FITS object. * Generally the FITS file have already added any HDUs prior * to the truncatd one. */ public class PaddingException extends FitsException { /** The HDU where the error happened. */ private BasicHDU truncatedHDU; /** * When the error is thrown, the data object being * read must be supplied. We initially create a dummy * header for this. If someone is reading the entire * HDU, then they can trap the exception and set the header * to the appropriate value. */ public PaddingException(Data datum) throws FitsException { truncatedHDU = FitsFactory.HDUFactory(datum.getKernel()); // We want to use the original Data object... so truncatedHDU = FitsFactory.HDUFactory(truncatedHDU.getHeader(), datum); } public PaddingException(String msg, Data datum) throws FitsException { super(msg); truncatedHDU = FitsFactory.HDUFactory(datum.getKernel()); truncatedHDU = FitsFactory.HDUFactory(truncatedHDU.getHeader(), datum); } void updateHeader(Header hdr) throws FitsException { truncatedHDU = FitsFactory.HDUFactory(hdr, truncatedHDU.getData()); } public BasicHDU getTruncatedHDU() { return truncatedHDU; } } fits-1.10.0/nom/tam/fits/ImageData.java0000664000175000017500000002502012031033644017735 0ustar frothmaifrothmaipackage nom.tam.fits; import java.lang.reflect.Array; import nom.tam.image.ImageTiler; import nom.tam.util.*; import java.io.*; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This class instantiates FITS primary HDU and IMAGE extension data. * Essentially these data are a primitive multi-dimensional array. *

* Starting in version 0.9 of the FITS library, this routine * allows users to defer the reading of images if the FITS * data is being read from a file. An ImageTiler object is * supplied which can return an arbitrary subset of the image * as a one dimensional array -- suitable for manipulation by * standard Java libraries. A call to the getData() method * will still return a multi-dimensional array, but the * image data will not be read until the user explicitly requests. * it. */ public class ImageData extends Data { /** The size of the data */ long byteSize; /** The actual array of data. This * is normally a multi-dimensional primitive array. * It may be null until the getData() routine is * invoked, or it may be filled by during the read * call when a non-random access device is used. */ Object dataArray; /** This class describes an array */ protected class ArrayDesc { int[] dims; Class type; ArrayDesc(int[] dims, Class type) { this.dims = dims; this.type = type; } } /** A description of what the data should look like */ ArrayDesc dataDescription; /** This inner class allows the ImageTiler * to see if the user has read in the data. */ protected class ImageDataTiler extends nom.tam.image.ImageTiler { ImageDataTiler(RandomAccess o, long offset, ArrayDesc d) { super(o, offset, d.dims, d.type); } public Object getMemoryImage() { return dataArray; } } /** The image tiler associated with this image. */ private ImageTiler tiler; /** Create an array from a header description. * This is typically how data will be created when reading * FITS data from a file where the header is read first. * This creates an empty array. * @param h header to be used as a template. * @exception FitsException if there was a problem with the header description. */ public ImageData(Header h) throws FitsException { dataDescription = parseHeader(h); } protected ArrayDesc parseHeader(Header h) throws FitsException { int bitpix; int type; int ndim; int[] dims; int i; Object dataArray; Class baseClass; int gCount = h.getIntValue("GCOUNT", 1); int pCount = h.getIntValue("PCOUNT", 0); if (gCount > 1 || pCount != 0) { throw new FitsException("Group data treated as images"); } bitpix = h.getIntValue("BITPIX", 0); if (bitpix == 8) { baseClass = Byte.TYPE; } else if (bitpix == 16) { baseClass = Short.TYPE; } else if (bitpix == 32) { baseClass = Integer.TYPE; } else if (bitpix == 64) { baseClass = Long.TYPE; } else if (bitpix == -32) { baseClass = Float.TYPE; } else if (bitpix == -64) { baseClass = Double.TYPE; } else { throw new FitsException("Invalid BITPIX:" + bitpix); } ndim = h.getIntValue("NAXIS", 0); dims = new int[ndim]; // Note that we have to invert the order of the axes // for the FITS file to get the order in the array we // are generating. byteSize = 1; for (i = 0; i < ndim; i += 1) { int cdim = h.getIntValue("NAXIS" + (i + 1), 0); if (cdim < 0) { throw new FitsException("Invalid array dimension:" + cdim); } byteSize *= cdim; dims[ndim - i - 1] = cdim; } byteSize *= Math.abs(bitpix) / 8; if (ndim == 0) { byteSize = 0; } return new ArrayDesc(dims, baseClass); } /** Create the equivalent of a null data element. */ public ImageData() { dataArray = new byte[0]; byteSize = 0; } /** Create an ImageData object using the specified object to * initialize the data array. * @param x The initial data array. This should be a primitive * array but this is not checked currently. */ public ImageData(Object x) { dataArray = x; byteSize = ArrayFuncs.computeLSize(x); } /** Fill header with keywords that describe * image data. * @param head The FITS header * @exception FitsException if the object does not contain * valid image data. */ protected void fillHeader(Header head) throws FitsException { if (dataArray == null) { head.nullImage(); return; } String classname = dataArray.getClass().getName(); int[] dimens = ArrayFuncs.getDimensions(dataArray); if (dimens == null || dimens.length == 0) { throw new FitsException("Image data object not array"); } int bitpix; switch (classname.charAt(dimens.length)) { case 'B': bitpix = 8; break; case 'S': bitpix = 16; break; case 'I': bitpix = 32; break; case 'J': bitpix = 64; break; case 'F': bitpix = -32; break; case 'D': bitpix = -64; break; default: throw new FitsException("Invalid Object Type for FITS data:" + classname.charAt(dimens.length)); } // if this is neither a primary header nor an image extension, // make it a primary header head.setSimple(true); head.setBitpix(bitpix); head.setNaxes(dimens.length); for (int i = 1; i <= dimens.length; i += 1) { if (dimens[i - 1] == -1) { throw new FitsException("Unfilled array for dimension: " + i); } head.setNaxis(i, dimens[dimens.length - i]); } head.addValue("EXTEND", true,"ntf::imagedata:extend:1"); // Just in case! head.addValue("PCOUNT", 0, "ntf::imagedata:pcount:1"); head.addValue("GCOUNT", 1, "ntf::imagedata:gcount:1"); } public void read(ArrayDataInput i) throws FitsException { // Don't need to read null data (noted by Jens Knudstrup) if (byteSize == 0) { return; } setFileOffset(i); if (i instanceof RandomAccess) { tiler = new ImageDataTiler((RandomAccess) i, ((RandomAccess) i).getFilePointer(), dataDescription); try { // Handle long skips. i.skipBytes(byteSize); } catch (IOException e) { throw new FitsException("Unable to skip over image:" + e); } } else { dataArray = ArrayFuncs.newInstance(dataDescription.type, dataDescription.dims); try { i.readLArray(dataArray); } catch (IOException e) { throw new FitsException("Unable to read image data:" + e); } tiler = new ImageDataTiler(null, 0, dataDescription); } int pad = FitsUtil.padding(getTrueSize()); try { i.skipBytes(pad); } catch (EOFException e) { throw new PaddingException("Error skipping padding after image", this); } catch (IOException e) { throw new FitsException("Error skipping padding after image"); } } public void write(ArrayDataOutput o) throws FitsException { // Don't need to write null data (noted by Jens Knudstrup) if (byteSize == 0) { return; } if (dataArray == null) { if (tiler != null) { // Need to read in the whole image first. try { dataArray = tiler.getCompleteImage(); } catch (IOException e) { throw new FitsException("Error attempting to fill image"); } } else if (dataArray == null && dataDescription != null) { // Need to create an array to match a specified header. dataArray = ArrayFuncs.newInstance(dataDescription.type, dataDescription.dims); } else { // This image isn't ready to be written! throw new FitsException("Null image data"); } } try { o.writeArray(dataArray); } catch (IOException e) { throw new FitsException("IO Error on image write" + e); } FitsUtil.pad(o, getTrueSize()); } /** Get the size in bytes of the data */ protected long getTrueSize() { return byteSize; } /** Return the actual data. * Note that this may return a null when * the data is not readable. It might be better * to throw a FitsException, but this is * a very commonly called method and we prefered * not to change how users must invoke it. */ public Object getData() { if (dataArray == null && tiler != null) { try { dataArray = tiler.getCompleteImage(); } catch (Exception e) { return null; } } return dataArray; } void setTiler(ImageTiler tiler) { this.tiler = tiler; } public ImageTiler getTiler() { return tiler; } } fits-1.10.0/nom/tam/fits/FitsException.java0000664000175000017500000000231112031033644020703 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This is a general exception class allow us to distinguish * issues detected by this library. */ public class FitsException extends Exception { public FitsException() { super(); } public FitsException(String msg) { super(msg); } public FitsException(String msg, Exception reason) { super(msg, reason); } } fits-1.10.0/nom/tam/fits/UndefinedHDU.java0000664000175000017500000000653512031033644020375 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import nom.tam.util.ArrayFuncs; /** Holder for unknown data types. */ public class UndefinedHDU extends BasicHDU { /** Build an image HDU using the supplied data. * @param h the header for this HDU * @param d the data used to build the image. * @exception FitsException if there was a problem with the data. */ public UndefinedHDU(Header h, Data d) throws FitsException { myData = d; myHeader = h; } /* Check if we can find the length of the data for this * header. * @return true if this HDU has a valid header. */ public static boolean isHeader(Header hdr) { if (hdr.getStringValue("XTENSION") != null && hdr.getIntValue("NAXIS", -1) >= 0) { return true; } return false; } /** Check if we can use the following object as * in an Undefined FITS block. We allow this * so long as computeLSize can get a size. Note * that computeLSize may be wrong! * @param o The Object being tested. */ public static boolean isData(Object o) { return ArrayFuncs.computeLSize(o) > 0; } /** 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 image extension could not be created. */ public Data manufactureData() throws FitsException { return manufactureData(myHeader); } public static Data manufactureData(Header hdr) throws FitsException { return new UndefinedData(hdr); } /** Create a header that describes the given * image data. * @param d The image to be described. * @exception FitsException if the object does not contain * valid image data. */ public static Header manufactureHeader(Data d) throws FitsException { Header h = new Header(); d.fillHeader(h); return h; } /** Encapsulate an object as an ImageHDU. */ public static Data encapsulate(Object o) throws FitsException { return new UndefinedData(o); } /** Print out some information about this HDU. */ public void info() { System.out.println(" Unhandled/Undefined/Unknown Type"); System.out.println(" XTENSION=" + myHeader.getStringValue("XTENSION").trim()); System.out.println(" Apparent size:" + myData.getTrueSize()); } } fits-1.10.0/nom/tam/fits/RandomGroupsHDU.java0000664000175000017500000002177412031033644021116 0ustar frothmaifrothmaipackage nom.tam.fits; import nom.tam.util.ArrayFuncs; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** Random groups HDUs. Note that the internal storage of random * groups is a Object[ngroup][2] array. The first element of * each group is the parameter data from that group. The second element * is the data. The parameters should be a one dimensional array * of the primitive types byte, short, int, long, float or double. * The second element is a n-dimensional array of the same type. * When analyzing group data structure only the first group is examined, * but for a valid FITS file all groups must have the same structure. */ public class RandomGroupsHDU extends BasicHDU { Object dataArray; /** Create an HDU from the given header and data */ public RandomGroupsHDU(Header h, Data d) { myHeader = h; myData = d; } /** Indicate that a RandomGroupsHDU can come at * the beginning of a FITS file. */ protected boolean canBePrimary() { return true; } /** Move a RandomGroupsHDU to or from the beginning * of a FITS file. Note that the FITS standard only * supports Random Groups data at the beginning * of the file, but we allow it within Image extensions. */ protected void setPrimaryHDU(boolean status) { try { super.setPrimaryHDU(status); } catch (FitsException e) { System.err.println("Unreachable catch in RandomGroupsHDU"); } if (status) { myHeader.setSimple(true); } else { myHeader.setXtension("IMAGE"); } } /** Make a header point to the given object. * @param odata The random groups data the header should describe. */ static Header manufactureHeader(Data d) throws FitsException { if (d == null) { throw new FitsException("Attempt to create null Random Groups data"); } Header h = new Header(); d.fillHeader(h); return h; } /** Is this a random groups header? * @param hdr The header to be tested. */ public static boolean isHeader(Header hdr) { if (hdr.getBooleanValue("SIMPLE")) { return hdr.getBooleanValue("GROUPS"); } String s = hdr.getStringValue("XTENSION"); if (s.trim().equals("IMAGE")) { return hdr.getBooleanValue("GROUPS"); } return false; } /** Check that this HDU has a valid header. * @return true if this HDU has a valid header. */ public boolean isHeader() { return isHeader(myHeader); } /** Check if this data is compatible with Random Groups structure. * Must be an Object[ngr][2] structure with both elements of each * group having the same base type and the first element being * a simple primitive array. We do not check anything but * the first row. */ public static boolean isData(Object oo) { if (oo instanceof Object[][]) { Object[][] o = (Object[][]) oo; if (o.length > 0) { if (o[0].length == 2) { if (ArrayFuncs.getBaseClass(o[0][0]) == ArrayFuncs.getBaseClass(o[0][1])) { String cn = o[0][0].getClass().getName(); if (cn.length() == 2 && cn.charAt(1) != 'Z' || cn.charAt(1) != 'C') { return true; } } } } } return false; } /** Create a FITS Data object corresponding to * this HDU header. */ public Data manufactureData() throws FitsException { return manufactureData(myHeader); } /** Create FITS data object corresponding to a given header. */ public static Data manufactureData(Header hdr) throws FitsException { int gcount = hdr.getIntValue("GCOUNT", -1); int pcount = hdr.getIntValue("PCOUNT", -1); if (!hdr.getBooleanValue("GROUPS") || hdr.getIntValue("NAXIS1", -1) != 0 || gcount < 0 || pcount < 0 || hdr.getIntValue("NAXIS") < 2) { throw new FitsException("Invalid Random Groups Parameters"); } // Allocate the object. Object[][] dataArray; if (gcount > 0) { dataArray = new Object[gcount][2]; } else { dataArray = new Object[0][]; } Object[] sampleRow = generateSampleRow(hdr); for (int i = 0; i < gcount; i += 1) { ((Object[][]) dataArray)[i][0] = ((Object[]) nom.tam.util.ArrayFuncs.deepClone(sampleRow))[0]; ((Object[][]) dataArray)[i][1] = ((Object[]) nom.tam.util.ArrayFuncs.deepClone(sampleRow))[1]; } return new RandomGroupsData(dataArray); } static Object[] generateSampleRow(Header h) throws FitsException { int ndim = h.getIntValue("NAXIS", 0) - 1; int[] dims = new int[ndim]; int bitpix = h.getIntValue("BITPIX", 0); Class baseClass; switch (bitpix) { case 8: baseClass = Byte.TYPE; break; case 16: baseClass = Short.TYPE; break; case 32: baseClass = Integer.TYPE; break; case 64: baseClass = Long.TYPE; break; case -32: baseClass = Float.TYPE; break; case -64: baseClass = Double.TYPE; break; default: throw new FitsException("Invalid BITPIX:" + bitpix); } // Note that we have to invert the order of the axes // for the FITS file to get the order in the array we // are generating. Also recall that NAXIS1=0, so that // we have an 'extra' dimension. for (int i = 0; i < ndim; i += 1) { long cdim = h.getIntValue("NAXIS" + (i + 2), 0); if (cdim < 0) { throw new FitsException("Invalid array dimension:" + cdim); } dims[ndim - i - 1] = (int) cdim; } Object[] sample = new Object[2]; sample[0] = ArrayFuncs.newInstance(baseClass, h.getIntValue("PCOUNT")); sample[1] = ArrayFuncs.newInstance(baseClass, dims); return sample; } public static Data encapsulate(Object o) throws FitsException { if (o instanceof Object[][]) { return new RandomGroupsData((Object[][]) o); } else { throw new FitsException("Attempt to encapsulate invalid data in Random Group"); } } /** Display structural information about the current HDU. */ public void info() { System.out.println("Random Groups HDU"); if (myHeader != null) { System.out.println(" HeaderInformation:"); System.out.println(" Ngroups:" + myHeader.getIntValue("GCOUNT")); System.out.println(" Npar: " + myHeader.getIntValue("PCOUNT")); System.out.println(" BITPIX: " + myHeader.getIntValue("BITPIX")); System.out.println(" NAXIS: " + myHeader.getIntValue("NAXIS")); for (int i = 0; i < myHeader.getIntValue("NAXIS"); i += 1) { System.out.println(" NAXIS" + (i + 1) + "= " + myHeader.getIntValue("NAXIS" + (i + 1))); } } else { System.out.println(" No Header Information"); } Object[][] data = null; if (myData != null) { try { data = (Object[][]) myData.getData(); } catch (FitsException e) { data = null; } } if (data == null || data.length < 1 || data[0].length != 2) { System.out.println(" Invalid/unreadable data"); } else { System.out.println(" Number of groups:" + data.length); System.out.println(" Parameters: " + nom.tam.util.ArrayFuncs.arrayDescription(data[0][0])); System.out.println(" Data:" + nom.tam.util.ArrayFuncs.arrayDescription(data[0][1])); } } } fits-1.10.0/nom/tam/fits/AsciiTable.java0000664000175000017500000007445212031033644020136 0ustar frothmaifrothmai/* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ package nom.tam.fits; import java.io.EOFException; import java.io.IOException; import java.lang.reflect.Array; import nom.tam.util.*; /** This class represents the data in an ASCII table */ public class AsciiTable extends Data implements TableData { /** The number of rows in the table */ private int nRows; /** The number of fields in the table */ private int nFields; /** The number of bytes in a row */ private int rowLen; /** The null string for the field */ private String[] nulls; /** The type of data in the field */ private Class[] types; /** The offset from the beginning of the row at which the field starts */ private int[] offsets; /** The number of bytes in the field */ private int[] lengths; /** The byte buffer used to read/write the ASCII table */ private byte[] buffer; /** Markers indicating fields that are null */ private boolean[] isNull; /** An array of arrays giving the data in the table in * binary numbers */ private Object[] data; /** The parser used to convert from buffer to data. */ ByteParser bp; /** The actual stream used to input data */ ArrayDataInput currInput; /** Create an ASCII table given a header * @param hdr The header describing the table * @throws FitsException */ public AsciiTable(Header hdr) throws FitsException { nRows = hdr.getIntValue("NAXIS2"); nFields = hdr.getIntValue("TFIELDS"); rowLen = hdr.getIntValue("NAXIS1"); types = new Class[nFields]; offsets = new int[nFields]; lengths = new int[nFields]; nulls = new String[nFields]; for (int i = 0; i < nFields; i += 1) { offsets[i] = hdr.getIntValue("TBCOL" + (i + 1)) - 1; String s = hdr.getStringValue("TFORM" + (i + 1)); if (offsets[i] < 0 || s == null) { throw new FitsException("Invalid Specification for column:" + (i + 1)); } s = s.trim(); char c = s.charAt(0); s = s.substring(1); if (s.indexOf('.') > 0) { s = s.substring(0, s.indexOf('.')); } lengths[i] = Integer.parseInt(s); switch (c) { case 'A': types[i] = String.class; break; case 'I': if (lengths[i] > 10) { types[i] = long.class; } else { types[i] = int.class; } break; case 'F': case 'E': types[i] = float.class; break; case 'D': types[i] = double.class; break; } nulls[i] = hdr.getStringValue("TNULL" + (i + 1)); if (nulls[i] != null) { nulls[i] = nulls[i].trim(); } } } /** Create an empty ASCII table */ public AsciiTable() { data = new Object[0]; buffer = null; nFields = 0; nRows = 0; rowLen = 0; types = new Class[0]; lengths = new int[0]; offsets = new int[0]; nulls = new String[0]; } /** Read in an ASCII table. Reading is deferred if * we are reading from a random access device * @param str * @throws FitsException */ public void read(ArrayDataInput str) throws FitsException { setFileOffset(str); currInput = str; if (str instanceof RandomAccess) { try { str.skipBytes((long) nRows * rowLen); } catch (IOException e) { throw new FitsException("Error skipping data: " + e); } } else { try { if ((long) rowLen * nRows > Integer.MAX_VALUE) { throw new FitsException("Cannot read ASCII table > 2 GB"); } getBuffer(rowLen * nRows, 0); } catch (IOException e) { throw new FitsException("Error reading ASCII table:" + e); } } try { str.skipBytes(FitsUtil.padding(nRows * rowLen)); } catch (EOFException e) { throw new PaddingException("EOF skipping padding after ASCII Table:" + e, this); } catch (IOException e) { throw new FitsException("Error skipping padding after ASCII Table:" + e); } } /** Read some data into the buffer. */ private void getBuffer(int size, long offset) throws IOException, FitsException { if (currInput == null) { throw new IOException("No stream open to read"); } buffer = new byte[size]; if (offset != 0) { FitsUtil.reposition(currInput, offset); } currInput.readFully(buffer); bp = new ByteParser(buffer); } /** Get the ASCII table information. * This will actually do the read if it had previously been deferred * @return The table data as an Object[] array. * @throws FitsException */ public Object getData() throws FitsException { if (data == null) { data = new Object[nFields]; for (int i = 0; i < nFields; i += 1) { data[i] = ArrayFuncs.newInstance(types[i], nRows); } if (buffer == null) { long newOffset = FitsUtil.findOffset(currInput); try { getBuffer(nRows * rowLen, fileOffset); } catch (IOException e) { throw new FitsException("Error in deferred read -- file closed prematurely?:" + e); } FitsUtil.reposition(currInput, newOffset); } bp.setOffset(0); int rowOffset; for (int i = 0; i < nRows; i += 1) { rowOffset = rowLen * i; for (int j = 0; j < nFields; j += 1) { if (!extractElement(rowOffset + offsets[j], lengths[j], data, j, i, nulls[j])) { if (isNull == null) { isNull = new boolean[nRows * nFields]; } isNull[j + i * nFields] = true; } } } } return data; } /** Move an element from the buffer into a data array. * @param offset The offset within buffer at which the element starts. * @param length The number of bytes in the buffer for the element. * @param array An array of objects, each of which is a simple array. * @param col Which element of array is to be modified? * @param row Which index into that element is to be modified? * @param nullFld What string signifies a null element? */ private boolean extractElement(int offset, int length, Object[] array, int col, int row, String nullFld) throws FitsException { bp.setOffset(offset); if (nullFld != null) { String s = bp.getString(length); if (s.trim().equals(nullFld)) { return false; } bp.skip(-length); } try { if (array[col] instanceof String[]) { ((String[]) array[col])[row] = bp.getString(length); } else if (array[col] instanceof int[]) { ((int[]) array[col])[row] = bp.getInt(length); } else if (array[col] instanceof float[]) { ((float[]) array[col])[row] = bp.getFloat(length); } else if (array[col] instanceof double[]) { ((double[]) array[col])[row] = bp.getDouble(length); } else if (array[col] instanceof long[]) { ((long[]) array[col])[row] = bp.getLong(length); } else { throw new FitsException("Invalid type for ASCII table conversion:" + array[col]); } } catch (FormatException e) { throw new FitsException("Error parsing data at row,col:" + row + "," + col + " " + e); } return true; } /** Get a column of data * @param col The 0-indexed column to be returned. * @return The column object -- typically as a 1-d array. * @throws FitsException */ public Object getColumn(int col) throws FitsException { if (data == null) { getData(); } return data[col]; } /** Get a row. If the data has not yet been read just * read this row. * @param row The 0-indexed row to be returned. * @return A row of data. * @throws FitsException */ public Object[] getRow(int row) throws FitsException { if (data != null) { return singleRow(row); } else { return parseSingleRow(row); } } /** Get a single element as a one-d array. * We return String's as arrays for consistency though * they could be returned as a scalar. * @param row The 0-based row * @param col The 0-based column * @return The requested cell data. * @throws FitsException when unable to get the data. */ public Object getElement(int row, int col) throws FitsException { if (data != null) { return singleElement(row, col); } else { return parseSingleElement(row, col); } } /** Extract a single row from a table. This returns * an array of Objects each of which is an array of length 1. */ private Object[] singleRow(int row) { Object[] res = new Object[nFields]; for (int i = 0; i < nFields; i += 1) { if (isNull == null || !isNull[row * nFields + i]) { res[i] = ArrayFuncs.newInstance(types[i], 1); System.arraycopy(data[i], row, res[i], 0, 1); } } return res; } /** Extract a single element from a table. This returns * an array of length 1. */ private Object singleElement(int row, int col) { Object res = null; if (isNull == null || !isNull[row * nFields + col]) { res = ArrayFuncs.newInstance(types[col], 1); System.arraycopy(data[col], row, res, 0, 1); } return res; } /** Read a single row from the table. This returns * a set of arrays of dimension 1. */ private Object[] parseSingleRow(int row) throws FitsException { int offset = row * rowLen; Object[] res = new Object[nFields]; try { getBuffer(rowLen, fileOffset + row * rowLen); } catch (IOException e) { throw new FitsException("Unable to read row"); } for (int i = 0; i < nFields; i += 1) { res[i] = ArrayFuncs.newInstance(types[i], 1); if (!extractElement(offsets[i], lengths[i], res, i, 0, nulls[i])) { res[i] = null; } } // Invalidate buffer for future use. buffer = null; return res; } /** Read a single element from the table. This returns * an array of dimension 1. */ private Object parseSingleElement(int row, int col) throws FitsException { Object[] res = new Object[1]; try { getBuffer(lengths[col], fileOffset + row * rowLen + offsets[col]); } catch (IOException e) { buffer = null; throw new FitsException("Unable to read element"); } res[0] = ArrayFuncs.newInstance(types[col], 1); if (extractElement(0, lengths[col], res, 0, 0, nulls[col])) { buffer = null; return res[0]; } else { buffer = null; return null; } } /** Write the data to an output stream. * @param str The output stream to be written to * @throws FitsException if any IO exception is found or * some inconsistency the FITS file arises. */ public void write(ArrayDataOutput str) throws FitsException { // Make sure we have the data in hand. getData(); // If buffer is still around we can just reuse it, // since nothing we've done has invalidated it. if (buffer == null) { if (data == null) { throw new FitsException("Attempt to write undefined ASCII Table"); } if ((long) nRows * rowLen > Integer.MAX_VALUE) { throw new FitsException("Cannot write ASCII table > 2 GB"); } buffer = new byte[nRows * rowLen]; bp = new ByteParser(buffer); for (int i = 0; i < buffer.length; i += 1) { buffer[i] = (byte) ' '; } ByteFormatter bf = new ByteFormatter(); bf.setTruncationThrow(false); bf.setTruncateOnOverflow(true); for (int i = 0; i < nRows; i += 1) { for (int j = 0; j < nFields; j += 1) { int offset = i * rowLen + offsets[j]; int len = lengths[j]; try { if (isNull != null && isNull[i * nFields + j]) { if (nulls[j] == null) { throw new FitsException("No null value set when needed"); } bf.format(nulls[j], buffer, offset, len); } else { if (types[j] == String.class) { String[] s = (String[]) data[j]; bf.format(s[i], buffer, offset, len); } else if (types[j] == int.class) { int[] ia = (int[]) data[j]; bf.format(ia[i], buffer, offset, len); } else if (types[j] == float.class) { float[] fa = (float[]) data[j]; bf.format(fa[i], buffer, offset, len); } else if (types[j] == double.class) { double[] da = (double[]) data[j]; bf.format(da[i], buffer, offset, len); } else if (types[j] == long.class) { long[] la = (long[]) data[j]; bf.format(la[i], buffer, offset, len); } } } catch (TruncationException e) { System.err.println("Ignoring truncation error:" + i + "," + j); } } } } // Now write the buffer. try { str.write(buffer); FitsUtil.pad(str, buffer.length, (byte) ' '); } catch (IOException e) { throw new FitsException("Error writing ASCII Table data"); } } /** Replace a column with new data. * @param col The 0-based index to the column * @param newData The column data. This is typically a 1-d array. * @throws FitsException */ public void setColumn(int col, Object newData) throws FitsException { if (data == null) { getData(); } if (col < 0 || col >= nFields || newData.getClass() != data[col].getClass() || Array.getLength(newData) != Array.getLength(data[col])) { throw new FitsException("Invalid column/column mismatch:" + col); } data[col] = newData; // Invalidate the buffer. buffer = null; } /** Modify a row in the table * @param row The 0-based index of the row * @param newData The new data. Each element of this array is typically * a primitive[1] array. * @throws FitsException */ public void setRow(int row, Object[] newData) throws FitsException { if (row < 0 || row > nRows) { throw new FitsException("Invalid row in setRow"); } if (data == null) { getData(); } for (int i = 0; i < nFields; i += 1) { try { System.arraycopy(newData[i], 0, data[i], row, 1); } catch (Exception e) { throw new FitsException("Unable to modify row: incompatible data:" + row); } setNull(row, i, false); } // Invalidate the buffer buffer = null; } /** Modify an element in the table * @param row the 0-based row * @param col the 0-based column * @param newData The new value for the column. Typically a primitive[1] array. * @throws FitsException */ public void setElement(int row, int col, Object newData) throws FitsException { if (data == null) { getData(); } try { System.arraycopy(newData, 0, data[col], row, 1); } catch (Exception e) { throw new FitsException("Incompatible element:" + row + "," + col); } setNull(row, col, false); // Invalidate the buffer buffer = null; } /** Mark (or unmark) an element as null. Note that if this FITS file is latter * written out, a TNULL keyword needs to be defined in the corresponding * header. This routine does not add an element for String columns. * @param row The 0-based row. * @param col The 0-based column. * @param flag True if the element is to be set to null. */ public void setNull(int row, int col, boolean flag) { if (flag) { if (isNull == null) { isNull = new boolean[nRows * nFields]; } isNull[col + row * nFields] = true; } else if (isNull != null) { isNull[col + row * nFields] = false; } // Invalidate the buffer buffer = null; } /** See if an element is null. * @param row The 0-based row * @param col The 0-based column * @return if the given element has been nulled. */ public boolean isNull(int row, int col) { if (isNull != null) { return isNull[row * nFields + col]; } else { return false; } } /** Add a column to the table. Users should be cautious * of calling this routine directly rather than the corresponding * routine in AsciiTableHDU since this routine knows nothing * of the FITS header modifications required. * @param newCol the new column information. This is typically a * primitive[n] or String[n] array. * @return the number of fields in the table * @throws FitsException */ public int addColumn(Object newCol) throws FitsException { int maxLen = 1; if (newCol instanceof String[]) { String[] sa = (String[]) newCol; for (int i = 0; i < sa.length; i += 1) { if (sa[i] != null && sa[i].length() > maxLen) { maxLen = sa[i].length(); } } } else if (newCol instanceof double[]) { maxLen = 24; } else if (newCol instanceof int[]) { maxLen = 10; } else if (newCol instanceof long[]) { maxLen = 20; } else if (newCol instanceof float[]) { maxLen = 16; } else { throw new FitsException("Adding invalid type to ASCII table"); } addColumn(newCol, maxLen); // Invalidate the buffer buffer = null; return nFields; } /** This version of addColumn allows the user to override * the default length associated with each column type. * @param newCol The new column data * @param length the requested length for the column * @return the number of columns after this one is added. * @throws FitsException */ public int addColumn(Object newCol, int length) throws FitsException { if (nFields > 0 && Array.getLength(newCol) != nRows) { throw new FitsException("New column has different number of rows"); } if (nFields == 0) { nRows = Array.getLength(newCol); } Object[] newData = new Object[nFields + 1]; int[] newOffsets = new int[nFields + 1]; int[] newLengths = new int[nFields + 1]; Class[] newTypes = new Class[nFields + 1]; String[] newNulls = new String[nFields + 1]; System.arraycopy(data, 0, newData, 0, nFields); System.arraycopy(offsets, 0, newOffsets, 0, nFields); System.arraycopy(lengths, 0, newLengths, 0, nFields); System.arraycopy(types, 0, newTypes, 0, nFields); System.arraycopy(nulls, 0, newNulls, 0, nFields); data = newData; offsets = newOffsets; lengths = newLengths; types = newTypes; nulls = newNulls; newData[nFields] = newCol; offsets[nFields] = rowLen + 1; lengths[nFields] = length; types[nFields] = ArrayFuncs.getBaseClass(newCol); rowLen += length + 1; if (isNull != null) { boolean[] newIsNull = new boolean[nRows * (nFields + 1)]; // Fix the null pointers. int add = 0; for (int i = 0; i < isNull.length; i += 1) { if (i % nFields == 0) { add += 1; } if (isNull[i]) { newIsNull[i + add] = true; } } isNull = newIsNull; } nFields += 1; // Invalidate the buffer buffer = null; return nFields; } /** Add a row to the FITS table. * @param newRow The new row data. * @return The number of rows after this is added. * @throws FitsException */ public int addRow(Object[] newRow) throws FitsException { // If there are no fields, then this is the // first row. We need to add in each of the columns // to get the descriptors set up. if (nFields == 0) { for (int i = 0; i < newRow.length; i += 1) { addColumn(newRow[i]); } } else { for (int i = 0; i < nFields; i += 1) { try { Object o = ArrayFuncs.newInstance(types[i], nRows + 1); System.arraycopy(data[i], 0, o, 0, nRows); System.arraycopy(newRow[i], 0, o, nRows, 1); data[i] = o; } catch (Exception e) { throw new FitsException("Error adding row:" + e); } } nRows += 1; } // Invalidate the buffer buffer = null; return nRows; } /** Delete rows from a FITS table * @param start The first (0-indexed) row to be deleted. * @param len The number of rows to be deleted. * @throws FitsException */ public void deleteRows(int start, int len) throws FitsException { if (nRows == 0 || start < 0 || start >= nRows || len <= 0) { return; } if (start + len > nRows) { len = nRows - start; } getData(); try { for (int i = 0; i < nFields; i += 1) { Object o = ArrayFuncs.newInstance(types[i], nRows - len); System.arraycopy(data[i], 0, o, 0, start); System.arraycopy(data[i], start + len, o, start, nRows - len - start); data[i] = o; } nRows -= len; } catch (Exception e) { throw new FitsException("Error deleting row:" + e); } } /** Set the null string for a columns. * This is not a public method since we * want users to call the method in AsciiTableHDU * and update the header also. */ void setNullString(int col, String newNull) { if (col >= 0 && col < nulls.length) { nulls[col] = newNull; } } /** Return the size of the data section * @return The size in bytes of the data section, not includeing the padding. */ protected long getTrueSize() { return (long) (nRows) * rowLen; } /** Fill in a header with information that points to this * data. * @param hdr The header to be updated with information appropriate * to the current table data. */ public void fillHeader(Header hdr) { try { hdr.setXtension("TABLE"); hdr.setBitpix(8); hdr.setNaxes(2); hdr.setNaxis(1, rowLen); hdr.setNaxis(2, nRows); Cursor iter = hdr.iterator(); iter.setKey("NAXIS2"); iter.next(); iter.add("PCOUNT", new HeaderCard("PCOUNT", 0,"ntf::asciitable:pcount:1")); iter.add("GCOUNT", new HeaderCard("GCOUNT", 1, "ntf::asciitable:gcount:1")); iter.add("TFIELDS", new HeaderCard("TFIELDS", nFields, "ntf::asciitable:tfields:1")); for (int i = 0; i < nFields; i += 1) { addColInfo(i, iter); } } catch (HeaderCardException e) { System.err.println("ImpossibleException in fillHeader:" + e); } } int addColInfo(int col, Cursor iter) throws HeaderCardException { String tform = null; if (types[col] == String.class) { tform = "A" + lengths[col]; } else if (types[col] == int.class || types[col] == long.class) { tform = "I" + lengths[col]; } else if (types[col] == float.class) { tform = "E" + lengths[col] + ".0"; } else if (types[col] == double.class) { tform = "D" + lengths[col] + ".0"; } String key; key = "TFORM" + (col + 1); iter.add(key, new HeaderCard(key, tform, "ntf::asciitable:tformN:1")); key = "TBCOL" + (col + 1); iter.add(key, new HeaderCard(key, offsets[col] + 1, "ntf::asciitable:tbcolN:1")); return lengths[col]; } /** Get the number of rows in the table * @return The number of rows. */ public int getNRows() { return nRows; } /** Get the number of columns in the table * @return The number of columns */ public int getNCols() { return nFields; } /** Get the number of bytes in a row * @return The number of bytes for a single row in the table. */ public int getRowLen() { return rowLen; } /** Delete columns from the table. * @param start The first, 0-indexed, column to be deleted. * @param len The number of columns to be deleted. * @throws FitsException */ public void deleteColumns(int start, int len) throws FitsException { getData(); Object[] newData = new Object[nFields - len]; int[] newOffsets = new int[nFields - len]; int[] newLengths = new int[nFields - len]; Class[] newTypes = new Class[nFields - len]; String[] newNulls = new String[nFields - len]; // Copy in the initial stuff... System.arraycopy(data, 0, newData, 0, start); // Don't do the offsets here. System.arraycopy(lengths, 0, newLengths, 0, start); System.arraycopy(types, 0, newTypes, 0, start); System.arraycopy(nulls, 0, newNulls, 0, start); // Copy in the final System.arraycopy(data, start + len, newData, start, nFields - start - len); // Don't do the offsets here. System.arraycopy(lengths, start + len, newLengths, start, nFields - start - len); System.arraycopy(types, start + len, newTypes, start, nFields - start - len); System.arraycopy(nulls, start + len, newNulls, start, nFields - start - len); for (int i = start; i < start + len; i += 1) { rowLen -= (lengths[i] + 1); } data = newData; offsets = newOffsets; lengths = newLengths; types = newTypes; nulls = newNulls; if (isNull != null) { boolean found = false; boolean[] newIsNull = new boolean[nRows * (nFields - len)]; for (int i = 0; i < nRows; i += 1) { int oldOff = nFields * i; int newOff = (nFields - len) * i; for (int col = 0; col < start; col += 1) { newIsNull[newOff + col] = isNull[oldOff + col]; found = found || isNull[oldOff + col]; } for (int col = start + len; col < nFields; col += 1) { newIsNull[newOff + col - len] = isNull[oldOff + col]; found = found || isNull[oldOff + col]; } } if (found) { isNull = newIsNull; } else { isNull = null; } } // Invalidate the buffer buffer = null; nFields -= len; } /** This is called after we delete columns. The HDU * doesn't know how to update the TBCOL entries. * @param oldNCol The number of columns we had before deletion. * @param hdr The associated header. * @throws FitsException */ public void updateAfterDelete(int oldNCol, Header hdr) throws FitsException { int offset = 0; for (int i = 0; i < nFields; i += 1) { offsets[i] = offset; hdr.addValue("TBCOL" + (i + 1), offset + 1, "ntf::asciitable:tbcolN:2"); offset += lengths[i] + 1; } for (int i = nFields; i < oldNCol; i += 1) { hdr.deleteKey("TBCOL" + (i + 1)); } hdr.addValue("NAXIS1", rowLen, "ntf::asciitable:naxis1:1"); } } fits-1.10.0/nom/tam/fits/FitsElement.java0000664000175000017500000000420212031033644020337 0ustar frothmaifrothmai/* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This inteface describes allows uses to easily perform * basic I/O operations * on a FITS element. */ package nom.tam.fits; import nom.tam.util.*; import java.io.IOException; public interface FitsElement { /** Read the contents of the element from an input source. * @param in The input source. */ public void read(ArrayDataInput in) throws FitsException, IOException; /** Write the contents of the element to a data sink. * @param out The data sink. */ public void write(ArrayDataOutput out) throws FitsException, IOException; /** Rewrite the contents of the element in place. * The data must have been orignally read from a random * access device, and the size of the element may not have changed. */ public void rewrite() throws FitsException, IOException; /** Get the byte at which this element begins. * This is only available if the data is originally read from * a random access medium. */ public long getFileOffset(); /** Can this element be rewritten? */ public boolean rewriteable(); /** The size of this element in bytes */ public long getSize(); /** Reset the input stream to point to the beginning of this element * @return True if the reset succeeded. */ public boolean reset(); } fits-1.10.0/nom/tam/fits/HeaderCommentsMap.java0000664000175000017500000001067112031033644021463 0ustar frothmaifrothmai/* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /* * This class provides a modifiable map in which the comment fields for FITS * header keywords * produced by this library are set. The map is a simple String -> String * map where the key Strings are normally class:keyword:id where class is * the class name where the keyword is set, keyword is the keyword set and id * is an integer used to distinguish multiple instances. * * Most users need not worry about this class, but users who wish to customize * the appearance of FITS files may update the map. The code itself is likely * to be needed to understand which values in the map must be modified. * * Note that the Header writing utilities look for the prefix ntf:: in comments * and if this is found, the comment is replaced by looking in this map for * a key given by the remainder of the original comment. */ package nom.tam.fits; import java.util.HashMap; import java.util.Map; public class HeaderCommentsMap { private static Map commentMap = new HashMap(); static { commentMap.put("header:extend:1", "Extensions are permitted"); commentMap.put("header:simple:1", "Java FITS: "+ new java.util.Date()); commentMap.put("header:xtension:1", "Java FITS: "+ new java.util.Date()); commentMap.put("header:naxis:1", "Dimensionality"); commentMap.put("header:extend:2", "Extensions are permitted"); commentMap.put("asciitable:pcount:1", "No group data"); commentMap.put("asciitable:gcount:1", "One group"); commentMap.put("asciitable:tfields:1", "Number of fields in table"); commentMap.put("asciitable:tbcolN:1", "Column offset"); commentMap.put("asciitable:naxis1:1", "Size of row in bytes"); commentMap.put("undefineddata:naxis1:1","Number of Bytes"); commentMap.put("undefineddata:extend:1","Extensions are permitted"); commentMap.put("binarytablehdu:pcount:1", "Includes heap"); commentMap.put("binarytable:naxis1:1", "Bytes per row"); commentMap.put("fits:checksum:1", "as of " + FitsDate.getFitsDateString()); commentMap.put("basichdu:extend:1", "Allow extensions"); commentMap.put("basichdu:gcount:1", "Required value"); commentMap.put("basichdu:pcount:1", "Required value"); commentMap.put("imagedata:extend:1", "Extension permitted"); commentMap.put("imagedata:pcount:1", "No extra parameters"); commentMap.put("imagedata:gcount:1", "One group"); commentMap.put("tablehdu:tfields:1", "Number of table fields"); /* Null entries: * header:bitpix:1 * header:simple:2 * header:bitpix:2 * header:naxisN:1 * header:naxis:2 * undefineddata:pcount:1 * undefineddata:gcount:1 * randomgroupsdata:naxis1:1 * randomgroupsdata:naxisN:1 * randomgroupsdata:groups:1 * randomgroupsdata:gcount:1 * randomgroupsdata:pcount:1 * binarytablehdu:theap:1 * binarytablehdu:tdimN:1 * asciitable:tformN:1 * asciitablehdu:tnullN:1 * asciitablehdu:tfields:1 * binarytable:pcount:1 * binarytable:gcount:1 * binarytable:tfields:1 * binarytable:tformN:1 * binarytable:tdimN:1 * tablehdu:naxis2:1 */ } public static String getComment(String key) { return commentMap.get(key); } public static void updateComment(String key, String comment) { commentMap.put(key, comment); } public static void deleteComment(String key) { commentMap.remove(key); } } fits-1.10.0/nom/tam/fits/Fits.java0000664000175000017500000013121512031034146017030 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import java.io.*; import java.net.*; import java.util.*; import nom.tam.util.*; /** This class provides access to routines to allow users * to read and write FITS files. *

* *

* Description of the Package *

* This FITS package attempts to make using FITS files easy, * but does not do exhaustive error checking. Users should * not assume that just because a FITS file can be read * and written that it is necessarily legal FITS. These classes * try to make it easy to transform between arrays of Java primitives * and their FITS encodings. * * *

    *
  • The Fits class provides capabilities to * read and write data at the HDU level, and to * add and delete HDU's from the current Fits object. * A large number of constructors are provided which * allow users to associate the Fits object with * some form of external data. This external * data may be in a compressed format. *
  • The HDU class is a factory class which is used to * create HDUs. HDU's can be of a number of types * derived from the abstract class BasicHDU. * The hierarchy of HDUs is: *
      *
    • BasicHDU *
        *
      • ImageHDU *
      • RandomGroupsHDU *
      • TableHDU *
          *
        • BinaryTableHDU *
        • AsciiTableHDU *
        *
      *
    * *
  • The Header class provides many functions to * add, delete and read header keywords in a variety * of formats. *
  • The HeaderCard class provides access to the structure * of a FITS header card. *
  • The Data class is an abstract class which provides * the basic methods for reading and writing FITS data. * Users will likely only be interested in the getData * method which returns that actual FITS data. *
  • The TableHDU class provides a large number of * methods to access and modify information in * tables. *
  • The Column class * combines the Header information and Data corresponding to * a given column. *
* * * @version 1.10 September 27, 2012 */ public class Fits { /** The input stream associated with this Fits object. */ private ArrayDataInput dataStr; /** A vector of HDUs that have been added to this * Fits object. */ private Vector hduList = new Vector(); /** Has the input stream reached the EOF? */ private boolean atEOF; /** The last offset we reached. * A -1 is used to indicate that we * cannot use the offset. */ private long lastFileOffset = -1; /** Indicate the version of these classes */ public static String version() { // Version 0.1: Original test FITS classes -- 9/96 // Version 0.2: Pre-alpha release 10/97 // Complete rewrite using BufferedData*** and // ArrayFuncs utilities. // Version 0.3: Pre-alpha release 1/98 // Incorporation of HDU hierarchy developed // by Dave Glowacki and various bug fixes. // Version 0.4: Alpha-release 2/98 // BinaryTable classes revised to use // ColumnTable classes. // Version 0.5: Random Groups Data 3/98 // Version 0.6: Handling of bad/skipped FITS, FitsDate (D. Glowacki) 3/98 // Version 0.9: ASCII tables, Tiled images, Faux, Bad and SkippedHDU class // deleted. 12/99 // Version 0.91: Changed visibility of some methods. // Minor fixes. // Version 0.92: Fix bug in BinaryTable when reading from stream. // Version 0.93: Supports HIERARCH header cards. Added FitsElement interface. // Several bug fixes especially for null HDUs. // Version 0.96: Address issues with mandatory keywords. // Fix problem where some keywords were not properly keyed. // Version 0.96a: Update version in FITS // Version 0.99: Added support for Checksums (thanks to RJ Mathar). // Fixed bug with COMMENT and HISTORY keywords (Rose Early) // Changed checking for compression and fixed bug with TFORM // handling in binary tables (Laurent Michel) // Distinguished arrays of length 1 from scalars in // binary tables (Jorgo Bakker) // Fixed bug in handling of length 0 values in headers (Fred Romerfanger, Jorgo Bakker) // Truncated BufferedFiles when finishing write (but only // for FITS file as a whole.) // Fixed bug writing binary tables with deferred reads. // Made addLine methods in Header public. // Changed ArrayFuncs.newInstance to handle inputs with dimensionality of 0. // Version 0.99.1 // Added deleteRows and deleteColumns functionality to all tables. // This includes changes // to TableData, TableHDU, AsciiTable, BinaryTable and util/ColumnTable. // Row deletions were suggested by code of R. Mathar but this works // on all types of tables and implements the deletions at a lower level. // Completely revised util.HashedList to use more standard features from // Collections. The HashedList now melds a HashMap and ArrayList // Added sort to HashedList function to enable sorting of the list. // The logic now uses a simple index for the iterators rather than // traversing a linked list. // Added sort before write in Header to ensure keywords are in correct order. // This uses a new HeaderOrder class which implements java.util.Comparator to // indicate the required order for FITS keywords. Users should now // be able to write required keywords anywhere without getting errors // later when they try to write out the FITS file. // Fixed bug in setColumn in util.Column table where the new column // was not being pointed to. Any new column resets the table. // Several fixes to BinaryTable to address errors in variable length // array handling. // Several fixes to the handling of variable length array in binary tables. // (noted by Guillame Belanger). // Several fixes and changes suggested by Richard Mathar mostly // in BinaryTable. // Version 0.99.2 // Revised test routines to use Junit. Note that Junit tests // use annotations and require Java 1.5. // Added ArrayFuncs.arrayEquals() methods to compare // arbitrary arrays. // Fixed bugs in handling of 0 length columns and table update. // Version 0.99.3 // Additional fixes for 0 length strings. // Version 0.99.4 // Changed handling of constructor for File objects // 0.99.5 // Add ability to handle FILE, HTTPS and FTP URLs and to // handle redirects amongst different protocols. // 0.99.5 // Fixes to String handling (A. Kovacs) // Truncating long doubles to fit in // standard header. // Made some methods public in FitsFactory // Added Set // 0.99.6 // Fix to BinaryTable (L. Michel) // Version 1.00.0 // Support for .Z compressed data. // Better detection of compressed data streams // Bug fix for binary tables (A. Kovacs) // Version 1.00.1 (2/09) // Fix for exponential format in header keywords // Version 1.00.2 (3/09) // Fixed offsets when users read rows or elements // within multiHDU files. // Version 1.01.0 // Fixes bugs and adds some more graceful // error handling for situations where arrays // could exceed 2G. More work is needed here though. // Data.getTrueSize() now returns a long. // // Fixed bug with initial blanks in HIERARCH // values. // Version 1.02.0 (7/09) // Fixes bugs in ASCII tables where integer and real // fields that are blank should be read as 0 per the FITS // standard. (L. Michel) // // Adds PaddingException to allow users to read // improperly padded last HDU in FITS file. (suggested by L. Michel) // This required changes to the Fits.java and all of the Data subclasses // as well as the new exception classes. // Version 1.03.0 (7/09) // Many changes to support long (>2GB) arrays in // reads and size computations: // ArrayDataInput.readArray deprecated in // favor of ArrayDataInput.readLArray // ArrayUtil.computeSize -> ArrayUtil.computeLSize // ArrayUtil.nElements -> ArrayUtil.nLElements // The skipBytes method in ArrayDataInput is overloaded // to take a long argument and return a long value (in // addition to the method inherited from DataInput // the takes and returns an int). // // Corresponding changes in FITS classes. // [Note that there are still many restrictions // due to the array size limits in Java.] // // A number of obsolete comments regarding BITPIX=64 being non-standard // were removed. // If errors are found in reading the Header of an HDU // an IOException is now returned in some situations // where an Error was being returned. // // A bug in the new PaddingException was fixed that // lets truncated ImageHDUs have Tilers. // // Version 1.03.1 (7/09) // Changed FitsUtil.byteArrayToStrings to make // sure that deleted white space is eligible for // garbage collection. (J.C. Segovia) // // Version 1.04.0 (12/09) // // Added support for the long string convention // (see JavaDocs for Header). // Fixed errors in handling of strings with embedded // quotes. // Other minor bugs. // Version 1.05.0 (12/10) // Several fixes suggested by Laurent Bourges // - Better handling of strings in binary tables // including handling of truncated strings (with // embedded nuls) and detection of illegal // non-printing characters. // - New table metadata functions in TableHDU // - Handling of complex data including variable // length arrays. // Added a number of convenience methods // - FitsUtil.pad() is used to write padding // rather than separate code in many classes // - FitsUtil.HDUFactory will now create // an HDU from a Header or input data. // - reset() method added to FitsElement to simplify // reading of Fits data using low level access. // This is implemented in many classes. // - dumpArray method in ArrayFuncs for convenience // in debugging. // Version 1.05.1 (2/11) // Fixed error in Long string support where the // COMMENT keyword was being used instead of the // correct CONTINUE. (V. Forchi) // An error in the positioning of the Header cursor // for primary images was noted by V. Forchi. Updates // to the header could easily result in writing // records before the EXTEND keyword which is a violation // of the FITS standard. // Version 1.06.0 (5/11) // Substantial reworking of compression to accommodate // BZIP2 compression. The Apache Bzip library is used or // since this is very slow, the user can specify a local // command to do the decompression using the BZIP_DECOMPRESSOR // environment variable. This is assumed to require a // '-' argument which is added if not supplied by the user. // The decompressor should act as a filter between standard input // and output. // // User compression flags are now completely // ignored and the compression and the compression // is determined entirely by the content of the stream. // The Apache library will be needed in the // classpath to accommodate BZIP2 inputs if the user // does not supply the BZIP_DECOMPRESSOR. // // Adding additional compression methods should be much easier and // may only involve adding a couple of lines in the // FitsUtil.decompress function if a decompressor class // is available. // // One subtle consequence of how compression is now handled // is that there is no advantage for users to // create their own BufferedDataInputStream's. // Users should just provide a standard input stream // and allow the FITS library to wrap it in a // BufferedDataInputStream. // // A bug in the UndefinedData class was detected // Vincenzo Forzi and has been corrected. // // The nom.tam.util.AsciiFuncs class now handles // ASCII encoding to more cleanly separate this // functionality from the FITS library and to enable // Java 1.5 compatibitity. (Suggested by changes of L.Bourges) // Some other V1.5 incompatiblities removed. // // The HeaderCommentsMap class is now provided to enable // users to control the comments that are generated in system // generated header cards. The map is initialized to values // that should be the same as the current defaults. This // should allow users to emulate the comments of other packages. // // All Java code has been passed through NetBeans formatter // so that it should have a more uniform appearance. // // Version 1.07.0 (11/11/11) // Allow FitsFactory.setAllowTerminalJunk to // let there be junk after a valid FITS file. // [Several changes to header. After changes from Booth Hartley (IPAC)] // // Allow users to find original size of headers in source // data which may differ from the internal version due to // the elimination of duplicate keys. (Booth Hartley) // // Fix related bug in Header.rewriteable(). // // Fix location of EXTEND keyword in HeaderOrder (Booth Hartley) // No longer required by FITS standard but allows compatibility // with readers using earlier standard. // // JavaDoc fixes and updates (many from Booth Hartley). // // Fits.setChecksum will now set both the CHECKSUM and DATASUM keywords. // // Version 1.08.0 (1/20/12) // // Added hadDuplicates() and getDuplicates() methods // to Header to allow users to determine if there were duplicate // entries in a header and if so what they were. // // Version 1.08.1 (1/30/12) // Fixed error in the writing of ASCII table columns where // all of the elements of the table were null or 0 length // strings. Previously we would write a column with TFORM A0. // This is not supported by CFITSIO and since it is not // valid Fortran is of dubious legality for FITS. Such columns // are now written with A1 (issue noted by Jason Weiss, UCLA). // // AsciiTable did not check if columns were of a valid type // (if the FitsFactory methods were used, then a BinaryTable // would be written, but a user can explicitly instantiate // an AsciiTable). A FitsException is now returned if // a column other than a String, double, int or long array is // used. // Version 1.10 (9/27/12) // Updated all internal documentation to indicate that code is in // the public domamin return "1.10"; } /** Create an empty Fits object which is not * associated with an input stream. */ public Fits() { } /** Create a Fits object associated with * the given data stream. * Compression is determined from the first few bytes of the stream. * @param str The data stream. */ public Fits(InputStream str) throws FitsException { streamInit(str, false); } /** Create a Fits object associated with a data stream. * @param str The data stream. * @param compressed Is the stream compressed? This is currently ignored. * Compression is determined from the first two bytes in the stream. */ public Fits(InputStream str, boolean compressed) throws FitsException { streamInit(str); } /** Initialize the stream. * @param str The user specified input stream * @param seekable ignored */ protected void streamInit(InputStream str, boolean seekable) throws FitsException { streamInit(str); } /** Do the stream initialization. * * @param str The input stream. * @param compressed Is this data compressed? This flag * is ignored. The compression is determined from the stream content. * @param seekable Can one seek on the stream. This parameter is ignored. */ protected void streamInit(InputStream str, boolean compressed, boolean seekable) throws FitsException { streamInit(str); } /** Initialize the input stream. Mostly this * checks to see if the stream is compressed and * wraps the stream if necessary. Even if the * stream is not compressed, it will likely be wrapped * in a PushbackInputStream. So users should probably * not supply a BufferedDataInputStream themselves, but * should allow the Fits class to do the wrapping. * @param str * @throws FitsException */ protected void streamInit(InputStream str) throws FitsException { str = FitsUtil.decompress(str); if (str instanceof ArrayDataInput) { dataStr = (ArrayDataInput) str; } else { // Use efficient blocking for input. dataStr = new BufferedDataInputStream(str); } } /** Initialize using buffered random access. * This implies that the data is uncompressed. * @param f * @throws FitsException */ protected void randomInit(File f) throws FitsException { String permissions = "r"; if (!f.exists() || !f.canRead()) { throw new FitsException("Non-existent or unreadable file"); } if (f.canWrite()) { permissions += "w"; } try { dataStr = new BufferedFile(f, permissions); ((BufferedFile) dataStr).seek(0); } catch (IOException e) { throw new FitsException("Unable to open file " + f.getPath()); } } /** Associate FITS object with a File. If the file is * compressed a stream will be used, otherwise random access * will be supported. * @param myFile The File object. */ public Fits(File myFile) throws FitsException { this(myFile, FitsUtil.isCompressed(myFile)); } /** Associate the Fits object with a File * @param myFile The File object. * @param compressed Is the data compressed? */ public Fits(File myFile, boolean compressed) throws FitsException { fileInit(myFile, compressed); } /** Get a stream from the file and then use the stream initialization. * @param myFile The File to be associated. * @param compressed Is the data compressed? */ protected void fileInit(File myFile, boolean compressed) throws FitsException { try { if (compressed) { FileInputStream str = new FileInputStream(myFile); streamInit(str); } else { randomInit(myFile); } } catch (IOException e) { throw new FitsException("Unable to create Input Stream from File: " + myFile); } } /** Associate the FITS object with a file or URL. * * The string is assumed to be a URL if it begins one of the * protocol strings. * If the string ends in .gz it is assumed that * the data is in a compressed format. * All string comparisons are case insensitive. * * @param filename The name of the file or URL to be processed. * @exception FitsException Thrown if unable to find or open * a file or URL from the string given. **/ public Fits(String filename) throws FitsException { this(filename, FitsUtil.isCompressed(filename)); } /** Associate the FITS object with a file or URL. * * The string is assumed to be a URL if it begins one of the * protocol strings. * If the string ends in .gz it is assumed that * the data is in a compressed format. * All string comparisons are case insensitive. * * @param filename The name of the file or URL to be processed. * @exception FitsException Thrown if unable to find or open * a file or URL from the string given. **/ public Fits(String filename, boolean compressed) throws FitsException { InputStream inp; if (filename == null) { throw new FitsException("Null FITS Identifier String"); } int len = filename.length(); String lc = filename.toLowerCase(); try { URL test = new URL(filename); InputStream is = FitsUtil.getURLStream(new URL(filename), 0); streamInit(is); return; } catch (Exception e) { // Just try it as a file } File fil = new File(filename); if (fil.exists()) { fileInit(fil, compressed); return; } try { InputStream str = ClassLoader.getSystemClassLoader().getResourceAsStream(filename); streamInit(str); } catch (Exception e) { // } } /** Associate the FITS object with a given uncompressed URL * @param myURL The URL to be associated with the FITS file. * @param compressed Compression flag, ignored. * @exception FitsException Thrown if unable to use the specified URL. */ public Fits(URL myURL, boolean compressed) throws FitsException { this(myURL); } /** Associate the FITS object with a given URL * @param myURL * @exception FitsException Thrown if unable to find or open * a file or URL from the string given. */ public Fits(URL myURL) throws FitsException { try { streamInit(FitsUtil.getURLStream(myURL, 0)); } catch (IOException e) { throw new FitsException("Unable to open input from URL:" + myURL); } } /** Return all HDUs for the Fits object. If the * FITS file is associated with an external stream make * sure that we have exhausted the stream. * @return an array of all HDUs in the Fits object. Returns * null if there are no HDUs associated with this object. */ public BasicHDU[] read() throws FitsException { readToEnd(); int size = getNumberOfHDUs(); if (size == 0) { return null; } BasicHDU[] hdus = new BasicHDU[size]; hduList.copyInto(hdus); return hdus; } /** Read the next HDU on the default input stream. * @return The HDU read, or null if an EOF was detected. * Note that null is only returned when the EOF is detected immediately * at the beginning of reading the HDU. */ public BasicHDU readHDU() throws FitsException, IOException { if (dataStr == null || atEOF) { return null; } if (dataStr instanceof nom.tam.util.RandomAccess && lastFileOffset > 0) { FitsUtil.reposition(dataStr, lastFileOffset); } Header hdr = Header.readHeader(dataStr); if (hdr == null) { atEOF = true; return null; } Data datum = hdr.makeData(); try { datum.read(dataStr); } catch (PaddingException e) { e.updateHeader(hdr); throw e; } lastFileOffset = FitsUtil.findOffset(dataStr); BasicHDU nextHDU = FitsFactory.HDUFactory(hdr, datum); hduList.addElement(nextHDU); return nextHDU; } /** Skip HDUs on the associate input stream. * @param n The number of HDUs to be skipped. */ public void skipHDU(int n) throws FitsException, IOException { for (int i = 0; i < n; i += 1) { skipHDU(); } } /** Skip the next HDU on the default input stream. */ public void skipHDU() throws FitsException, IOException { if (atEOF) { return; } else { Header hdr = new Header(dataStr); if (hdr == null) { atEOF = true; return; } int dataSize = (int) hdr.getDataSize(); dataStr.skip(dataSize); } } /** Return the n'th HDU. * If the HDU is already read simply return a pointer to the * cached data. Otherwise read the associated stream * until the n'th HDU is read. * @param n The index of the HDU to be read. The primary HDU is index 0. * @return The n'th HDU or null if it could not be found. */ public BasicHDU getHDU(int n) throws FitsException, IOException { int size = getNumberOfHDUs(); for (int i = size; i <= n; i += 1) { BasicHDU hdu; hdu = readHDU(); if (hdu == null) { return null; } } try { return (BasicHDU) hduList.elementAt(n); } catch (NoSuchElementException e) { throw new FitsException("Internal Error: hduList build failed"); } } /** Read to the end of the associated input stream */ private void readToEnd() throws FitsException { while (dataStr != null && !atEOF) { try { if (readHDU() == null) { break; } } catch (IOException e) { throw new FitsException("IO error: " + e); } } } /** Return the number of HDUs in the Fits object. If the * FITS file is associated with an external stream make * sure that we have exhausted the stream. * @return number of HDUs. * @deprecated The meaning of size of ambiguous. Use */ public int size() throws FitsException { readToEnd(); return getNumberOfHDUs(); } /** Add an HDU to the Fits object. Users may intermix * calls to functions which read HDUs from an associated * input stream with the addHDU and insertHDU calls, * but should be careful to understand the consequences. * * @param myHDU The HDU to be added to the end of the FITS object. */ public void addHDU(BasicHDU myHDU) throws FitsException { insertHDU(myHDU, getNumberOfHDUs()); } /** Insert a FITS object into the list of HDUs. * * @param myHDU The HDU to be inserted into the list of HDUs. * @param n The location at which the HDU is to be inserted. */ public void insertHDU(BasicHDU myHDU, int n) throws FitsException { if (myHDU == null) { return; } if (n < 0 || n > getNumberOfHDUs()) { throw new FitsException("Attempt to insert HDU at invalid location: " + n); } try { if (n == 0) { // Note that the previous initial HDU is no longer the first. // If we were to insert tables backwards from last to first, // we could get a lot of extraneous DummyHDUs but we currently // do not worry about that. if (getNumberOfHDUs() > 0) { ((BasicHDU) hduList.elementAt(0)).setPrimaryHDU(false); } if (myHDU.canBePrimary()) { myHDU.setPrimaryHDU(true); hduList.insertElementAt(myHDU, 0); } else { insertHDU(BasicHDU.getDummyHDU(), 0); myHDU.setPrimaryHDU(false); hduList.insertElementAt(myHDU, 1); } } else { myHDU.setPrimaryHDU(false); hduList.insertElementAt(myHDU, n); } } catch (NoSuchElementException e) { throw new FitsException("hduList inconsistency in insertHDU"); } } /** Delete an HDU from the HDU list. * * @param n The index of the HDU to be deleted. * If n is 0 and there is more than one HDU present, then * the next HDU will be converted from an image to * primary HDU if possible. If not a dummy header HDU * will then be inserted. */ public void deleteHDU(int n) throws FitsException { int size = getNumberOfHDUs(); if (n < 0 || n >= size) { throw new FitsException("Attempt to delete non-existent HDU:" + n); } try { hduList.removeElementAt(n); if (n == 0 && size > 1) { BasicHDU newFirst = (BasicHDU) hduList.elementAt(0); if (newFirst.canBePrimary()) { newFirst.setPrimaryHDU(true); } else { insertHDU(BasicHDU.getDummyHDU(), 0); } } } catch (NoSuchElementException e) { throw new FitsException("Internal Error: hduList Vector Inconsitency"); } } /** Write a Fits Object to an external Stream. * * @param os A DataOutput stream. */ public void write(DataOutput os) throws FitsException { ArrayDataOutput obs; boolean newOS = false; if (os instanceof ArrayDataOutput) { obs = (ArrayDataOutput) os; } else if (os instanceof DataOutputStream) { newOS = true; obs = new BufferedDataOutputStream((DataOutputStream) os); } else { throw new FitsException("Cannot create ArrayDataOutput from class " + os.getClass().getName()); } BasicHDU hh; for (int i = 0; i < getNumberOfHDUs(); i += 1) { try { hh = (BasicHDU) hduList.elementAt(i); hh.write(obs); } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); throw new FitsException("Internal Error: Vector Inconsistency" + e); } } if (newOS) { try { obs.flush(); obs.close(); } catch (IOException e) { System.err.println("Warning: error closing FITS output stream"); } } try { if (obs instanceof BufferedFile) { ((BufferedFile) obs).setLength(((BufferedFile) obs).getFilePointer()); } } catch (IOException e) { // Ignore problems... } } /** Read a FITS file from an InputStream object. * * @param is The InputStream stream whence the FITS information * is found. */ public void read(InputStream is) throws FitsException, IOException { boolean newIS = false; if (is instanceof ArrayDataInput) { dataStr = (ArrayDataInput) is; } else { dataStr = new BufferedDataInputStream(is); } read(); if (newIS) { dataStr.close(); dataStr = null; } } /** Get the current number of HDUs in the Fits object. * @return The number of HDU's in the object. * @deprecated See getNumberOfHDUs() */ public int currentSize() { return hduList.size(); } /** Get the current number of HDUs in the Fits object. * @return The number of HDU's in the object. */ public int getNumberOfHDUs() { return hduList.size(); } /** Get the data stream used for the Fits Data. * @return The associated data stream. Users may wish to * call this function after opening a Fits object when * they wish detailed control for writing some part of the FITS file. */ public ArrayDataInput getStream() { return dataStr; } /** Set the data stream to be used for future input. * * @param stream The data stream to be used. */ public void setStream(ArrayDataInput stream) { dataStr = stream; atEOF = false; lastFileOffset = -1; } /** Create an HDU from the given header. * @param h The header which describes the FITS extension */ public static BasicHDU makeHDU(Header h) throws FitsException { Data d = FitsFactory.dataFactory(h); return FitsFactory.HDUFactory(h, d); } /** Create an HDU from the given data kernel. * @param o The data to be described in this HDU. */ public static BasicHDU makeHDU(Object o) throws FitsException { return FitsFactory.HDUFactory(o); } /** Create an HDU from the given Data. * @param datum The data to be described in this HDU. */ public static BasicHDU makeHDU(Data datum) throws FitsException { Header hdr = new Header(); datum.fillHeader(hdr); return FitsFactory.HDUFactory(hdr, datum); } /** * Add or update the CHECKSUM keyword. * @param hdu the HDU to be updated. * @throws nom.tam.fits.HeaderCardException * @author R J Mathar * @since 2005-10-05 */ public static void setChecksum(BasicHDU hdu) throws nom.tam.fits.HeaderCardException, nom.tam.fits.FitsException, java.io.IOException { /* the next line with the delete is needed to avoid some unexpected * problems with non.tam.fits.Header.checkCard() which otherwise says * it expected PCOUNT and found DATE. */ Header hdr = hdu.getHeader(); hdr.deleteKey("CHECKSUM"); /* jThis would need org.nevec.utils.DateUtils compiled before org.nevec.prima.fits .... * final String doneAt = DateUtils.dateToISOstring(0) ; * We need to save the value of the comment string because this is becoming part * of the checksum calculated and needs to be re-inserted again - with the same string - * when the second/final call to addValue() is made below. */ final String doneAt = HeaderCommentsMap.getComment("fits:checksum:1"); hdr.addValue("CHECKSUM", "0000000000000000", doneAt); /* Convert the entire sequence of 2880 byte header cards into a byte array. * The main benefit compared to the C implementations is that we do not need to worry * about the particular byte order on machines (Linux/VAX/MIPS vs Hp-UX, Sparc...) supposed that * the correct implementation is in the write() interface. */ ByteArrayOutputStream hduByteImage = new ByteArrayOutputStream(); BufferedDataOutputStream bdos = new BufferedDataOutputStream(hduByteImage); // DATASUM keyword. hdu.getData().write(bdos); bdos.flush(); byte[] data = hduByteImage.toByteArray(); long checksum = checksum(data); hdu.write(new BufferedDataOutputStream(hduByteImage)); long csd = checksum(data); hdu.getHeader().addValue("DATASUM", ""+csd, "Checksum of data"); // We already have the checsum of the data. Lets compute it for // the header. hduByteImage.reset(); hdu.getHeader().write(bdos); bdos.flush(); data = hduByteImage.toByteArray(); long csh = checksum(data); long cshdu = csh + csd; // If we had a carry it should go into the // beginning. while ( (cshdu & 0xFFFFFFFF00000000L) != 0) { cshdu = (cshdu&0xFFFFFFFFL) + 1; } /* This time we do not use a deleteKey() to ensure that the keyword is replaced "in place". * Note that the value of the checksum is actually independent to a permutation of the * 80-byte records within the header. */ hdr.addValue("CHECKSUM", checksumEnc(cshdu, true), doneAt); } /** * Add or Modify the CHECKSUM keyword in all headers. * @throws nom.tam.fits.HeaderCardException * @throws nom.tam.fits.FitsException * @author R J Mathar * @since 2005-10-05 */ public void setChecksum() throws nom.tam.fits.HeaderCardException, nom.tam.fits.FitsException, java.io.IOException { for (int i = 0; i < getNumberOfHDUs(); i += 1) { setChecksum(getHDU(i)); } } /** * Calculate the Seaman-Pence 32-bit 1's complement checksum over the byte stream. The option * to start from an intermediate checksum accumulated over another previous * byte stream is not implemented. * The implementation accumulates in two 64-bit integer values the two low-order and the two * high-order bytes of adjacent 4-byte groups. A carry-over of bits is never done within the main * loop (only once at the end at reduction to a 32-bit positive integer) since an overflow * of a 64-bit value (signed, with maximum at 2^63-1) by summation of 16-bit values could only * occur after adding approximately 140G short values (=2^47) (280GBytes) or more. We assume * for now that this routine here is never called to swallow FITS files of that size or larger. * @param data the byte sequence * @return the 32bit checksum in the range from 0 to 2^32-1 * @see "http://heasarc.gsfc.nasa.gov/docs/heasarc/fits/checksum.html" * @author R J Mathar * @since 2005-10-05 */ public static long checksum(final byte[] data) { long hi = 0; long lo = 0; final int len = 2 * (data.length / 4); // System.out.println(data.length + " bytes") ; final int remain = data.length % 4; /* a write(2) on Sparc/PA-RISC would write the MSB first, on Linux the LSB; by some kind * of coincidence, we can stay with the byte order known from the original C version of * the algorithm. */ for (int i = 0; i < len; i += 2) { /* The four bytes in this block handled by a single 'i' are each signed (-128 to 127) * in Java and need to be masked indivdually to avoid sign extension /propagation. */ hi += (data[2 * i] << 8) & 0xff00L | data[2 * i + 1] & 0xffL; lo += (data[2 * i + 2] << 8) & 0xff00L | data[2 * i + 3] & 0xffL; } /* The following three cases actually cannot happen since FITS records are multiples of 2880 bytes. */ if (remain >= 1) { hi += (data[2 * len] << 8) & 0xff00L; } if (remain >= 2) { hi += data[2 * len + 1] & 0xffL; } if (remain >= 3) { lo += (data[2 * len + 2] << 8) & 0xff00L; } long hicarry = hi >>> 16; long locarry = lo >>> 16; while (hicarry != 0 || locarry != 0) { hi = (hi & 0xffffL) + locarry; lo = (lo & 0xffffL) + hicarry; hicarry = hi >>> 16; locarry = lo >>> 16; } return (hi << 16) + lo; } /** * Encode a 32bit integer according to the Seaman-Pence proposal. * @param c the checksum previously calculated * @return the encoded string of 16 bytes. * @see http://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/checksum/node14.html#SECTION00035000000000000000 * @author R J Mathar * @since 2005-10-05 */ private static String checksumEnc(final long c, final boolean compl) { byte[] asc = new byte[16]; final int[] exclude = {0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60}; final long[] mask = {0xff000000L, 0xff0000L, 0xff00L, 0xffL}; final int offset = 0x30; /* ASCII 0 (zero */ final long value = compl ? ~c : c; for (int i = 0; i < 4; i++) { final int byt = (int) ((value & mask[i]) >>> (24 - 8 * i)); // each byte becomes four final int quotient = byt / 4 + offset; final int remainder = byt % 4; int[] ch = new int[4]; for (int j = 0; j < 4; j++) { ch[j] = quotient; } ch[0] += remainder; boolean check = true; for (; check;) // avoid ASCII punctuation { check = false; for (int k = 0; k < exclude.length; k++) { for (int j = 0; j < 4; j += 2) { if (ch[j] == exclude[k] || ch[j + 1] == exclude[k]) { ch[j]++; ch[j + 1]--; check = true; } } } } for (int j = 0; j < 4; j++) // assign the bytes { asc[4 * j + i] = (byte) (ch[j]); } } // shift the bytes 1 to the right circularly. try { String resul = AsciiFuncs.asciiString(asc, 15, 1); return resul.concat(AsciiFuncs.asciiString(asc, 0, 15)); } catch (Exception e) { // Impossible I hope System.err.println("CheckSum Error finding ASCII encoding"); return null; } } } fits-1.10.0/nom/tam/fits/FitsFactory.java0000664000175000017500000001323412031033644020362 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This class contains the code which * associates particular FITS types with header * and data configurations. It comprises * a set of Factory methods which call * appropriate methods in the HDU classes. * If -- God forbid -- a new FITS HDU type were * created, then the XXHDU, XXData classes would * need to be added and this file modified but * no other changes should be needed in the FITS libraries. * */ public class FitsFactory { private static boolean useAsciiTables = true; private static boolean useHierarch = false; private static boolean checkAsciiStrings = false; private static boolean allowTerminalJunk = false; /** Indicate whether ASCII tables should be used * where feasible. */ public static void setUseAsciiTables(boolean flag) { useAsciiTables = flag; } /** Get the current status of ASCII table writing */ static boolean getUseAsciiTables() { return useAsciiTables; } /** Enable/Disable hierarchical keyword processing. */ public static void setUseHierarch(boolean flag) { useHierarch = flag; } /** Enable/Disable checking of strings values used in tables * to ensure that they are within the range specified by the * FITS standard. The standard only allows the values 0x20 - 0x7E * with null bytes allowed in one limited context. * Disabled by default. */ public static void setCheckAsciiStrings(boolean flag) { checkAsciiStrings = flag; } /** Get the current status for string checking. */ static boolean getCheckAsciiStrings() { return checkAsciiStrings; } /** Are we processing HIERARCH style keywords */ public static boolean getUseHierarch() { return useHierarch; } /** Do we allow junk after a valid FITS file? */ public static void setAllowTerminalJunk(boolean flag) { allowTerminalJunk = flag; } /** Is terminal junk (i.e., non-FITS data following a valid HDU) * allowed. */ public static boolean getAllowTerminalJunk() { return allowTerminalJunk; } /** Given a Header return an appropriate datum. */ public static Data dataFactory(Header hdr) throws FitsException { if (ImageHDU.isHeader(hdr)) { Data d = ImageHDU.manufactureData(hdr); hdr.afterExtend(); // Fix for positioning error noted by V. Forchi return d; } else if (RandomGroupsHDU.isHeader(hdr)) { return RandomGroupsHDU.manufactureData(hdr); } else if (useAsciiTables && AsciiTableHDU.isHeader(hdr)) { return AsciiTableHDU.manufactureData(hdr); } else if (BinaryTableHDU.isHeader(hdr)) { return BinaryTableHDU.manufactureData(hdr); } else if (UndefinedHDU.isHeader(hdr)) { return UndefinedHDU.manufactureData(hdr); } else { throw new FitsException("Unrecognizable header in dataFactory"); } } /** Given an object, create the appropriate * FITS header to describe it. * @param o The object to be described. */ public static BasicHDU HDUFactory(Object o) throws FitsException { Data d; Header h; if (o instanceof Header) { h = (Header) o; d = dataFactory(h); } else if (ImageHDU.isData(o)) { d = ImageHDU.encapsulate(o); h = ImageHDU.manufactureHeader(d); } else if (RandomGroupsHDU.isData(o)) { d = RandomGroupsHDU.encapsulate(o); h = RandomGroupsHDU.manufactureHeader(d); } else if (useAsciiTables && AsciiTableHDU.isData(o)) { d = AsciiTableHDU.encapsulate(o); h = AsciiTableHDU.manufactureHeader(d); } else if (BinaryTableHDU.isData(o)) { d = BinaryTableHDU.encapsulate(o); h = BinaryTableHDU.manufactureHeader(d); } else if (UndefinedHDU.isData(o)) { d = UndefinedHDU.encapsulate(o); h = UndefinedHDU.manufactureHeader(d); } else { throw new FitsException("Invalid data presented to HDUFactory"); } return HDUFactory(h, d); } /** Given Header and data objects return * the appropriate type of HDU. */ public static BasicHDU HDUFactory(Header hdr, Data d) throws FitsException { if (d instanceof ImageData) { return new ImageHDU(hdr, d); } else if (d instanceof RandomGroupsData) { return new RandomGroupsHDU(hdr, d); } else if (d instanceof AsciiTable) { return new AsciiTableHDU(hdr, d); } else if (d instanceof BinaryTable) { return new BinaryTableHDU(hdr, d); } else if (d instanceof UndefinedData) { return new UndefinedHDU(hdr, d); } return null; } } fits-1.10.0/nom/tam/fits/comments.txt0000664000175000017500000000315711566662576017703 0ustar frothmaifrothmai# This file contains comment values for standard keywords. # The first token on each non-comment line is a keyword that # identifies the comment of the form class_keyword_n # where class is the class in which the data is written # keyword is the key value for the comment being written # and n is an integer (usually 1) used to distinguish multiple # entries. header_simple_1 Null Image Header header_bitpix_1 BITPIX for null image header_naxis_1 NAXIS for null image header_extend_1 Extensions are permitted header_simple_2 Java FITS: $DATE header_xtension_1 Java FITS: $DATE header_bitpix_2 header_naxis_2 Dimensionality header_naxisN_1 header_end_1 randomgroupsdata_naxis1_1 randomgroupsdata_naxisN_1 randomgroupsdata_groups_1 randomgroupsdata_gcount_1 randomgroupsdata_pcount_1 binarytablehdu_pcount_1 binarytablehdu_theap_1 binarytablehdu_tformN_1 binarytablehdu_tdimN_1 asciitable_pcount_1 No group data asciitable_gcount_1 One group asciitable_tfields_1 Number of fields in table asciitable_tbcolN_1 Column Offset asciitable_naxis1_1 Size of row in bytes asciitablehdu_tnullN_1 asciitablehdu_tfields_1 binarytable_pcount_1 binarytable_gcount_1 binarytable_naxis1_1 Bytes per row binarytable_tfields basichdu_extend_1 Allow extensions basichdu_pcount_1 Required value basichdu_gcount_1 Required value imagedata_extend_1 Extension permitted imagedata_pcount_1 No extra parameters imagedata_gcount_1 One group tablehdu_naxis2_1 tablehdu_tfields_1 Number of table fields undefineddata_naxis1_1 Number of bytes in unknown structure undefineddata_pcount_1 undefineddata_gcount_1 undefineddata_extend_1 Extensions are permittedfits-1.10.0/nom/tam/fits/ImageHDU.java0000664000175000017500000001260212031033644017506 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import nom.tam.util.ArrayFuncs; import nom.tam.util.BufferedDataInputStream; import nom.tam.image.ImageTiler; /** FITS image header/data unit */ public class ImageHDU extends BasicHDU { /** Build an image HDU using the supplied data. * @param h the header for the image. * @param d the data used in the image. * @exception FitsException if there was a problem with the data. */ public ImageHDU(Header h, Data d) throws FitsException { myData = d; myHeader = h; } /** Indicate that Images can appear at the beginning of a FITS dataset */ protected boolean canBePrimary() { return true; } /** Change the Image from/to primary */ protected void setPrimaryHDU(boolean status) { try { super.setPrimaryHDU(status); } catch (FitsException e) { System.err.println("Impossible exception in ImageData"); } if (status) { myHeader.setSimple(true); } else { myHeader.setXtension("IMAGE"); } } /** Check that this HDU has a valid header for this type. * @return true if this HDU has a valid header. */ public static boolean isHeader(Header hdr) { boolean found = false; found = hdr.getBooleanValue("SIMPLE"); if (!found) { String s = hdr.getStringValue("XTENSION"); if (s != null) { if (s.trim().equals("IMAGE") || s.trim().equals("IUEIMAGE")) { found = true; } } } if (!found) { return false; } return !hdr.getBooleanValue("GROUPS"); } /** Check if this object can be described as a FITS image. * @param o The Object being tested. */ public static boolean isData(Object o) { String s = o.getClass().getName(); int i; for (i = 0; i < s.length(); i += 1) { if (s.charAt(i) != '[') { break; } } // Allow all non-boolean/Object arrays. // This does not check the rectangularity of the array though. if (i <= 0 || s.charAt(i) == 'L' || s.charAt(i) == 'Z') { return false; } else { return true; } } /** 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 image extension could not be created. */ public Data manufactureData() throws FitsException { return manufactureData(myHeader); } public static Data manufactureData(Header hdr) throws FitsException { return new ImageData(hdr); } /** Create a header that describes the given * image data. * @param d The image to be described. * @exception FitsException if the object does not contain * valid image data. */ public static Header manufactureHeader(Data d) throws FitsException { if (d == null) { return null; } Header h = new Header(); d.fillHeader(h); return h; } /** Encapsulate an object as an ImageHDU. */ public static Data encapsulate(Object o) throws FitsException { return new ImageData(o); } public ImageTiler getTiler() { return ((ImageData) myData).getTiler(); } /** Print out some information about this HDU. */ public void info() { if (isHeader(myHeader)) { System.out.println(" Image"); } else { System.out.println(" Image (bad header)"); } System.out.println(" Header Information:"); System.out.println(" BITPIX=" + myHeader.getIntValue("BITPIX", -1)); int naxis = myHeader.getIntValue("NAXIS", -1); System.out.println(" NAXIS=" + naxis); for (int i = 1; i <= naxis; i += 1) { System.out.println(" NAXIS" + i + "=" + myHeader.getIntValue("NAXIS" + i, -1)); } System.out.println(" Data information:"); try { if (myData.getData() == null) { System.out.println(" No Data"); } else { System.out.println(" " + ArrayFuncs.arrayDescription(myData.getData())); } } catch (Exception e) { System.out.println(" Unable to get data"); } } } fits-1.10.0/nom/tam/fits/RandomGroupsData.java0000664000175000017500000001173212031033644021340 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import nom.tam.util.*; import java.io.IOException; import java.io.EOFException; /** This class instantiates FITS Random Groups data. * Random groups are instantiated as a two-dimensional * array of objects. The first dimension of the array * is the number of groups. The second dimension is 2. * The first object in every row is a one dimensional * parameter array. The second element is the n-dimensional * data array. */ public class RandomGroupsData extends Data { private Object[][] dataArray; /** Create the equivalent of a null data element. */ public RandomGroupsData() { dataArray = new Object[0][]; } /** Create a RandomGroupsData object using the specified object to * initialize the data array. * @param x The initial data array. This should a two-d * array of objects as described above. */ public RandomGroupsData(Object[][] x) { dataArray = x; } /** Get the size of the actual data element. */ protected long getTrueSize() { if (dataArray != null && dataArray.length > 0) { return (ArrayFuncs.computeLSize(dataArray[0][0]) + ArrayFuncs.computeLSize(dataArray[0][1])) * dataArray.length; } else { return 0; } } /** Read the RandomGroupsData */ public void read(ArrayDataInput str) throws FitsException { setFileOffset(str); try { str.readLArray(dataArray); } catch (IOException e) { throw new FitsException("IO error reading Random Groups data "+e); } int pad = FitsUtil.padding(getTrueSize()); try { str.skipBytes(pad); } catch (EOFException e) { throw new PaddingException("EOF reading padding after random groups", this); } catch (IOException e) { throw new FitsException("IO error reading padding after random groups"); } } /** Write the RandomGroupsData */ public void write(ArrayDataOutput str) throws FitsException { try { str.writeArray(dataArray); FitsUtil.pad(str, getTrueSize()); } catch (IOException e) { throw new FitsException("IO error writing random groups data "+e); } } protected void fillHeader(Header h) throws FitsException { if (dataArray.length <= 0 || dataArray[0].length != 2) { throw new FitsException("Data not conformable to Random Groups"); } int gcount = dataArray.length; Object paraSamp = dataArray[0][0]; Object dataSamp = dataArray[0][1]; Class pbase = nom.tam.util.ArrayFuncs.getBaseClass(paraSamp); Class dbase = nom.tam.util.ArrayFuncs.getBaseClass(dataSamp); if (pbase != dbase) { throw new FitsException("Data and parameters do not agree in type for random group"); } int[] pdims = nom.tam.util.ArrayFuncs.getDimensions(paraSamp); int[] ddims = nom.tam.util.ArrayFuncs.getDimensions(dataSamp); if (pdims.length != 1) { throw new FitsException("Parameters are not 1 d array for random groups"); } // Got the information we need to build the header. h.setSimple(true); if (dbase == byte.class) { h.setBitpix(8); } else if (dbase == short.class) { h.setBitpix(16); } else if (dbase == int.class) { h.setBitpix(32); } else if (dbase == long.class) { // Non-standard h.setBitpix(64); } else if (dbase == float.class) { h.setBitpix(-32); } else if (dbase == double.class) { h.setBitpix(-64); } else { throw new FitsException("Data type:"+dbase+" not supported for random groups"); } h.setNaxes(ddims.length+1); h.addValue("NAXIS1", 0, "ntf::randomgroupsdata:naxis1:1"); for (int i=2; i<=ddims.length+1; i += 1) { h.addValue("NAXIS"+i, ddims[i-2], "ntf::randomgroupsdata:naxisN:1"); } h.addValue("GROUPS", true, "ntf::randomgroupsdata:groups:1"); h.addValue("GCOUNT", dataArray.length, "ntf::randomgroupsdata:gcount:1"); h.addValue("PCOUNT", pdims[0], "ntf::randomgroupsdata:pcount:1"); } public Object getData() { return dataArray; } } fits-1.10.0/nom/tam/fits/UndefinedData.java0000664000175000017500000001102512031033644020614 0ustar frothmaifrothmaipackage nom.tam.fits; import nom.tam.util.*; import java.io.*; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** This class provides a simple holder for data which is * not handled by other classes. */ public class UndefinedData extends Data { /** The size of the data */ long byteSize; byte[] data; public UndefinedData(Header h) throws FitsException { /** Just get a byte buffer to hold the data. */ // Bug fix by Vincenzo Forzi. int naxis = h.getIntValue("NAXIS"); int size = naxis > 0 ? 1 : 0; for (int i = 0; i < naxis; i += 1) { size *= h.getIntValue("NAXIS" + (i + 1)); } size += h.getIntValue("PCOUNT"); if (h.getIntValue("GCOUNT") > 1) { size *= h.getIntValue("GCOUNT"); } size *= Math.abs(h.getIntValue("BITPIX") / 8); data = new byte[size]; byteSize = size; } /** Create an UndefinedData object using the specified object. */ public UndefinedData(Object x) { byteSize = ArrayFuncs.computeLSize(x); data = new byte[(int) byteSize]; } /** Fill header with keywords that describe data. * @param head The FITS header */ protected void fillHeader(Header head) { try { head.setXtension("UNKNOWN"); head.setBitpix(8); head.setNaxes(1); head.addValue("NAXIS1", byteSize,"ntf::undefineddata:naxis1:1"); head.addValue("PCOUNT", 0, "ntf::undefineddata:pcount:1"); head.addValue("GCOUNT", 1, "ntf::undefineddata:gcount:1"); head.addValue("EXTEND", true, "ntf::undefineddata:extend:1"); // Just in case! } catch (HeaderCardException e) { System.err.println("Unable to create unknown header:" + e); } } public void read(ArrayDataInput i) throws FitsException { setFileOffset(i); if (i instanceof RandomAccess) { try { i.skipBytes(byteSize); } catch (IOException e) { throw new FitsException("Unable to skip over data:" + e); } } else { try { i.readFully(data); } catch (IOException e) { throw new FitsException("Unable to read unknown data:" + e); } } int pad = FitsUtil.padding(getTrueSize()); try { i.skipBytes(pad); } catch (EOFException e) { throw new PaddingException("EOF skipping padding in undefined data", this); } catch (IOException e) { throw new FitsException("Error skipping padding in undefined data"); } } public void write(ArrayDataOutput o) throws FitsException { if (data == null) { getData(); } if (data == null) { throw new FitsException("Null unknown data"); } try { o.write(data); } catch (IOException e) { throw new FitsException("IO Error on unknown data write" + e); } FitsUtil.pad(o, getTrueSize()); } /** Get the size in bytes of the data */ protected long getTrueSize() { return byteSize; } /** Return the actual data. * Note that this may return a null when * the data is not readable. It might be better * to throw a FitsException, but this is * a very commonly called method and we prefered * not to change how users must invoke it. */ public Object getData() { if (data == null) { try { FitsUtil.reposition(input, fileOffset); input.read(data); } catch (Exception e) { return null; } } return data; } } fits-1.10.0/nom/tam/fits/TableHDU.java0000664000175000017500000003156512031033644017524 0ustar frothmaifrothmaipackage nom.tam.fits; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ import java.util.Iterator; import nom.tam.util.Cursor; /** This class allows FITS binary and ASCII tables to * be accessed via a common interface. * * Bug Fix: 3/28/01 to findColumn. */ public abstract class TableHDU extends BasicHDU { private TableData table; private int currentColumn; /** Create the TableHDU. Note that this * will normally only be invoked by subclasses * in the FITS package. * @param td The data for the table. */ TableHDU(TableData td) { table = td; } /** Get a specific row of the table */ public Object[] getRow(int row) throws FitsException { return table.getRow(row); } /** Get a specific column of the table where * the column name is specified using the TTYPEn keywords * in the header. * @param colName The name of the column to be extracted. * @throws FitsException */ public Object getColumn(String colName) throws FitsException { return getColumn(findColumn(colName)); } /** Get a specific column from the table using 0-based column * indexing. */ public Object getColumn(int col) throws FitsException { return table.getColumn(col); } /** Get all of the columns of the table. */ public Object[] getColumns() throws FitsException { Object[] result = new Object[getNCols()]; for (int i = 0; i < result.length; i += 1) { result[i] = getColumn(i); } return result; } /** Get a specific element of the table using 0-based indices. * */ public Object getElement(int row, int col) throws FitsException { return table.getElement(row, col); } /** Update a row within a table. * */ public void setRow(int row, Object[] newRow) throws FitsException { table.setRow(row, newRow); } /** Update a column within a table. The new column should have the * same format as the column being replaced. */ public void setColumn(String colName, Object newCol) throws FitsException { setColumn(findColumn(colName), newCol); } /** Update a column within a table. The new column should have the same * format ast the column being replaced. */ public void setColumn(int col, Object newCol) throws FitsException { table.setColumn(col, newCol); } /** Update a single element within the table. */ public void setElement(int row, int col, Object element) throws FitsException { table.setElement(row, col, element); } /** Add a row to the end of the table. If this is the first row, * then this will add appropriate columns for each of the entries. */ public int addRow(Object[] newRow) throws FitsException { int row = table.addRow(newRow); myHeader.addValue("NAXIS2", row, "ntf::tablehdu:naxis2:1"); return row; } /** Find the 0-based column index corresponding to a particular * column name. */ public int findColumn(String colName) { for (int i = 0; i < getNCols(); i += 1) { String val = myHeader.getStringValue("TTYPE" + (i + 1)); if (val != null && val.trim().equals(colName)) { return i; } } return -1; } /** Add a column to the table. */ public abstract int addColumn(Object data) throws FitsException; /** Get the number of columns for this table * @return The number of columns in the table. */ public int getNCols() { return table.getNCols(); } /** Get the number of rows for this table * @return The number of rows in the table. */ public int getNRows() { return table.getNRows(); } /** Get the name of a column in the table. * @param index The 0-based column index. * @return The column name. * @exception FitsException if an invalid index was requested. */ public String getColumnName(int index) { String ttype = myHeader.getStringValue("TTYPE" + (index + 1)); if (ttype != null) { ttype = ttype.trim(); } return ttype; } public void setColumnName(int index, String name, String comment) throws FitsException { setColumnMeta(index, "TTYPE", name, comment, true); } /** Specify column metadata for a given column in a way that * allows all of the column metadata for a given column * to be organized together. * * @param index The 0-based index of the column * @param key The column key. I.e., the keyword will be key+(index+1) * @param value The value to be placed in the header. * @param comment The comment for the header * @param after Should the header card be after the current column metadata block * (true), or immediately before the TFORM card (false). * @throws FitsException */ public void setColumnMeta(int index, String key, String value, String comment, boolean after) throws FitsException { setCurrentColumn(index, after); myHeader.addValue(key + (index + 1), value, comment); } /** Convenience method for getting column data. Note that this works * only for metadata that returns a string value. This is equivalent * to getStringValue(type+index); */ public String getColumnMeta(int index, String type) { return myHeader.getStringValue(type+(index+1)); } public void setColumnMeta(int index, String key, String value, String comment) throws FitsException { setColumnMeta(index, key, value, comment, true); } public void setColumnMeta(int index, String key, long value, String comment, boolean after) throws FitsException { setCurrentColumn(index, after); myHeader.addValue(key + (index + 1), value, comment); } public void setColumnMeta(int index, String key, double value, String comment, boolean after) throws FitsException { setCurrentColumn(index, after); myHeader.addValue(key + (index + 1), value, comment); } public void setColumnMeta(int index, String key, boolean value, String comment, boolean after) throws FitsException { setCurrentColumn(index, after); myHeader.addValue(key + (index + 1), value, comment); } /** Get the FITS type of a column in the table. * @param index The 0-based index of the column. * @return The FITS type. * @exception FitsException if an invalid index was requested. */ public String getColumnFormat(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 myHeader.getStringValue("TFORM" + (index + 1)).trim(); } /** Set the cursor in the header to point after the * metadata for the specified column * @param col The 0-based index of the column */ public void setCurrentColumn(int col) { setCurrentColumn(col, true); } /** Set the cursor in the header to point either before the * TFORM value or after the column metadat * @param col The 0-based index of the column * @param after True if the cursor should be placed after the existing column * metadata or false if the cursor is to be placed before the TFORM value. * If no corresponding TFORM is found, the cursoe will be placed at the end of * current header. */ public void setCurrentColumn(int col, boolean after) { if (after) { myHeader.positionAfterIndex("TFORM", col + 1); } else { String tform = "TFORM" + (col + 1); myHeader.findCard(tform); } } /** * Remove all rows from the table starting at some specific index from the table. * Inspired by a routine by R. Mathar but re-implemented using the DataTable and * changes to AsciiTable so that it can be done easily for both Binary and ASCII tables. * @param row the (0-based) index of the first row to be deleted. * @throws FitsExcpetion if an error occurs. */ public void deleteRows(final int row) throws FitsException { deleteRows(row, getNRows() - row); } /** * Remove a number of adjacent rows from the table. This routine * was inspired by code by R.Mathar but re-implemented using changes * in the ColumnTable class abd AsciiTable so that we can do * it for all FITS tables. * @param firstRow the (0-based) index of the first row to be deleted. * This is zero-based indexing: 0<=firstrow< number of rows. * @param nRow the total number of rows to be deleted. * @throws FitsException If an error occurs in the deletion. */ public void deleteRows(final int firstRow, int nRow) throws FitsException { // Just ignore invalid requests. if (nRow <= 0 || firstRow >= getNRows() || nRow <= 0) { return; } /* correct if more rows are requested than available */ if (nRow > getNRows() - firstRow) { nRow = getNRows() - firstRow; } table.deleteRows(firstRow, nRow); myHeader.setNaxis(2, getNRows()); } /** Delete a set of columns from a table. */ public void deleteColumnsIndexOne(int column, int len) throws FitsException { deleteColumnsIndexZero(column - 1, len); } /** Delete a set of columns from a table. */ public void deleteColumnsIndexZero(int column, int len) throws FitsException { deleteColumnsIndexZero(column, len, columnKeyStems()); } /** Delete a set of columns from a table. * @param column The one-indexed start column. * @param len The number of columns to delete. * @param fields Stems for the header fields to be removed * for the table. */ public void deleteColumnsIndexOne(int column, int len, String[] fields) throws FitsException { deleteColumnsIndexZero(column - 1, len, fields); } /** Delete a set of columns from a table. * @param column The zero-indexed start column. * @param len The number of columns to delete. * @param fields Stems for the header fields to be removed * for the table. */ public void deleteColumnsIndexZero(int column, int len, String[] fields) throws FitsException { if (column < 0 || len < 0 || column + len > getNCols()) { throw new FitsException("Illegal columns deletion request- Start:" + column + " Len:" + len + " from table with " + getNCols() + " columns"); } if (len == 0) { return; } int ncol = getNCols(); table.deleteColumns(column, len); // Get rid of the keywords for the deleted columns for (int col = column; col < column + len; col += 1) { for (int fld = 0; fld < fields.length; fld += 1) { String key = fields[fld] + (col + 1); myHeader.deleteKey(key); } } // Shift the keywords for the columns after the deleted columns for (int col = column + len; col < ncol; col += 1) { for (int fld = 0; fld < fields.length; fld += 1) { String oldKey = fields[fld] + (col + 1); String newKey = fields[fld] + (col + 1 - len); if (myHeader.containsKey(oldKey)) { myHeader.replaceKey(oldKey, newKey); } } } // Update the number of fields. myHeader.addValue("TFIELDS", getNCols(), "ntf::tablehdu:tfields:1"); // Give the data sections a chance to update the header too. table.updateAfterDelete(ncol, myHeader); } /** Get the stems of the keywords that are associated * with table columns. Users can supplement this * with their own and call the appropriate deleteColumns fields. */ public abstract String[] columnKeyStems(); } fits-1.10.0/nom/tam/fits/AsciiTableHDU.java0000664000175000017500000001565312031033644020475 0ustar frothmaifrothmaipackage nom.tam.fits; import java.io.IOException; import nom.tam.util.*; import java.util.Iterator; /* * This code is part of the Java FITS library developed 1996-2012 by T.A. McGlynn (NASA/GSFC) * The code is available in the public domain and may be copied, modified and used * by anyone in any fashion for any purpose without restriction. * * No warranty regarding correctness or performance of this code is given or implied. * Users may contact the author if they have questions or concerns. * * The author would like to thank many who have contributed suggestions, * enhancements and bug fixes including: * David Glowacki, R.J. Mathar, Laurent Michel, Guillaume Belanger, * Laurent Bourges, Rose Early, Fred Romelfanger, Jorgo Baker, A. Kovacs, V. Forchi, J.C. Segovia, * Booth Hartley and Jason Weiss. * I apologize to any contributors whose names may have been inadvertently omitted. * * Tom McGlynn */ /** * FITS ASCII table header/data unit */ public class AsciiTableHDU extends TableHDU { /** Just a copy of myData with the correct type */ AsciiTable data; /** The standard column stems for an ASCII table. * Note that TBCOL is not included here -- it needs to * be handled specially since it does not simply shift. */ private String[] keyStems = {"TFORM", "TZERO", "TNULL", "TTYPE", "TUNIT"}; /** * Create an ascii table header/data unit. * @param h the template specifying the ascii table. * @param d the FITS data structure containing the table data. * @exception FitsException if there was a problem with the header. */ public AsciiTableHDU(Header h, Data d) { super((TableData) d); myHeader = h; data = (AsciiTable) d; myData = d; } /** * Check that this is a valid ascii table header. * @param header to validate. * @return true if this is an ascii table header. */ public static boolean isHeader(Header header) { return header.getStringValue("XTENSION").trim().equals("TABLE"); } /** * Check that this HDU has a valid header. * @return true if this HDU has a valid header. */ public boolean isHeader() { return isHeader(myHeader); } /** Check if this data is usable as an ASCII table. */ public static boolean isData(Object o) { if (o instanceof Object[]) { Object[] oo = (Object[]) o; for (int i = 0; i < oo.length; i += 1) { if (oo[i] instanceof String[] || oo[i] instanceof int[] || oo[i] instanceof long[] || oo[i] instanceof float[] || oo[i] instanceof double[]) { continue; } return false; } return true; } else { return false; } } /** * 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 Data object could not be created * from this HDU's Header */ public static Data manufactureData(Header hdr) throws FitsException { return new AsciiTable(hdr); } /** Create an empty data structure corresponding to the input header. */ public Data manufactureData() throws FitsException { return manufactureData(myHeader); } /** Create a header to match the input data. */ public static Header manufactureHeader(Data d) throws FitsException { Header hdr = new Header(); d.fillHeader(hdr); Iterator iter = hdr.iterator(); return hdr; } /** Create a ASCII table data structure from an array of objects * representing the columns. */ public static Data encapsulate(Object o) throws FitsException { Object[] oo = (Object[]) o; AsciiTable d = new AsciiTable(); for (int i = 0; i < oo.length; i += 1) { d.addColumn(oo[i]); } return d; } /** * Skip the ASCII table and throw an exception. * @param stream the stream from which the data is read. */ public void readData(ArrayDataInput stream) throws FitsException { myData.read(stream); } /** Mark an entry as null. */ public void setNull(int row, int col, boolean flag) { if (flag) { String nullStr = myHeader.getStringValue("TNULL" + (col + 1)); if (nullStr == null) { setNullString(col, "NULL"); } } data.setNull(row, col, flag); } /** See if an element is null */ public boolean isNull(int row, int col) { return data.isNull(row, col); } /** Set the null string for a column */ public void setNullString(int col, String newNull) { myHeader.positionAfterIndex("TBCOL", col + 1); try { myHeader.addValue("TNULL" + (col + 1), newNull, "ntf::asciitablehdu:tnullN:1"); } catch (HeaderCardException e) { System.err.println("Impossible exception in setNullString" + e); } data.setNullString(col, newNull); } /** Add a column */ public int addColumn(Object newCol) throws FitsException { data.addColumn(newCol); // Move the iterator to point after all the data describing // the previous column. Cursor iter = myHeader.positionAfterIndex("TBCOL", data.getNCols()); int rowlen = data.addColInfo(getNCols(), iter); int oldRowlen = myHeader.getIntValue("NAXIS1"); myHeader.setNaxis(1, rowlen + oldRowlen); int oldTfields = myHeader.getIntValue("TFIELDS"); try { myHeader.addValue("TFIELDS", oldTfields + 1, "ntf::asciitablehdu:tfields:1"); } catch (Exception e) { System.err.println("Impossible exception at addColumn:" + e); } return getNCols(); } /** * Print a little information about the data set. */ public void info() { System.out.println("ASCII Table:"); System.out.println(" Header:"); System.out.println(" Number of fields:" + myHeader.getIntValue("TFIELDS")); System.out.println(" Number of rows: " + myHeader.getIntValue("NAXIS2")); System.out.println(" Length of row: " + myHeader.getIntValue("NAXIS1")); System.out.println(" Data:"); Object[] data = (Object[]) getKernel(); for (int i = 0; i < getNCols(); i += 1) { System.out.println(" " + i + ":" + ArrayFuncs.arrayDescription(data[i])); } } /** Return the FITS data structure associated with this HDU. */ public Data getData() { return data; } /** Return the keyword column stems for an ASCII table. */ public String[] columnKeyStems() { return keyStems; } } fits-1.10.0/META-INF/0000775000175000017500000000000012042210262014073 5ustar frothmaifrothmaifits-1.10.0/META-INF/MANIFEST.MF0000664000175000017500000000014712042210260015525 0ustar frothmaifrothmaiManifest-Version: 1.0 Ant-Version: Apache Ant 1.8.3 Created-By: 1.7.0_07-b10 (Oracle Corporation)