fits-1.10.0/ 0000775 0001750 0001750 00000000000 12050472242 012742 5 ustar frothmai frothmai fits-1.10.0/NOTE.v110.0 0000664 0001750 0001750 00000002070 12050472223 014254 0 ustar frothmai frothmai V1.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/ 0000775 0001750 0001750 00000000000 11566662560 013552 5 ustar frothmai frothmai fits-1.10.0/nom/tam/ 0000775 0001750 0001750 00000000000 11566662570 014334 5 ustar frothmai frothmai fits-1.10.0/nom/tam/util/ 0000775 0001750 0001750 00000000000 11566662570 015311 5 ustar frothmai frothmai fits-1.10.0/nom/tam/util/TruncationException.java 0000664 0001750 0001750 00000002027 12031033644 022140 0 ustar frothmai frothmai 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 */ public class TruncationException extends Exception { public TruncationException() { super(); } public TruncationException(String msg) { super(msg); } } fits-1.10.0/nom/tam/util/RandomAccess.java 0000664 0001750 0001750 00000002522 12031033644 020475 0 ustar frothmai frothmai 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 */ /** 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.java 0000664 0001750 0001750 00000070767 12031033644 020742 0 ustar frothmai frothmai 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 */ /** 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.java 0000664 0001750 0001750 00000040111 12031033644 020207 0 ustar frothmai frothmai 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.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.java 0000664 0001750 0001750 00000126156 12031033644 020222 0 ustar frothmai frothmai // 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.java 0000664 0001750 0001750 00000004213 12031033644 020716 0 ustar frothmai frothmai 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 */ /** 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.java 0000664 0001750 0001750 00000067147 12031033644 020356 0 ustar frothmai frothmai 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.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: *
* 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.java 0000664 0001750 0001750 00000004043 12031033644 020162 0 ustar frothmai frothmai /* * 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/ 0000775 0001750 0001750 00000000000 11566662570 016270 5 ustar frothmai frothmai fits-1.10.0/nom/tam/util/test/ArrayFuncs2Test.java 0000664 0001750 0001750 00000017740 12031031530 022111 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000076552 12031031530 022631 0 ustar frothmai frothmai /* * 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.java 0000664 0001750 0001750 00000030273 12031031530 022023 0 ustar frothmai frothmai /* * 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.java 0000664 0001750 0001750 00000005354 12031031526 021256 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000025643 12031031530 023022 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000020543 12031031530 021775 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000006412 12031033644 021226 0 ustar frothmai frothmai 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.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.java 0000664 0001750 0001750 00000111643 12031033644 020462 0 ustar frothmai frothmai 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 */ /** 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.java 0000664 0001750 0001750 00000056064 12031033644 022655 0 ustar frothmai frothmai 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 */ // 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.java 0000664 0001750 0001750 00000002743 12031033644 017761 0 ustar frothmai frothmai 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 */ /** 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.java 0000664 0001750 0001750 00000003516 12031033644 017414 0 ustar frothmai frothmai 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 */ /** 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.java 0000664 0001750 0001750 00000027166 12031033644 020176 0 ustar frothmai frothmai 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 */ /** 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.java 0000664 0001750 0001750 00000002312 12031033644 017231 0 ustar frothmai frothmai 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.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.java 0000664 0001750 0001750 00000002011 12031033644 021032 0 ustar frothmai frothmai 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 */ public class TableException extends Exception { public TableException() { super(); } public TableException(String msg) { super(msg); } } fits-1.10.0/nom/tam/util/BufferedDataOutputStream.java 0000664 0001750 0001750 00000036113 12031033644 023047 0 ustar frothmai frothmai 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 */ // 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.java 0000664 0001750 0001750 00000002007 12031033644 021240 0 ustar frothmai frothmai 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
*/
public class FormatException extends java.lang.Exception {
FormatException() {
super();
}
FormatException(String msg) {
super(msg);
}
}
fits-1.10.0/nom/tam/image/ 0000775 0001750 0001750 00000000000 11566662572 015420 5 ustar frothmai frothmai fits-1.10.0/nom/tam/image/ImageTiler.java 0000664 0001750 0001750 00000026513 12031033644 020270 0 ustar frothmai frothmai package 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/ 0000775 0001750 0001750 00000000000 11566662560 015300 5 ustar frothmai frothmai fits-1.10.0/nom/tam/fits/HeaderCardException.java 0000664 0001750 0001750 00000002124 12031033644 021762 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000024251 12031033644 020663 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000157263 12031033644 020334 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000003625 12031033644 017751 0 ustar frothmai frothmai package 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/ 0000775 0001750 0001750 00000000000 11566662576 017322 5 ustar frothmai frothmai fits-1.10.0/nom/tam/fits/utilities/FitsReader.java 0000664 0001750 0001750 00000002700 12031033644 022164 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000003071 12031033644 021676 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000126050 12031033644 017316 0 ustar frothmai frothmai package 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
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 ArrayListnull
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.java 0000664 0001750 0001750 00000041373 12031033644 017675 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000014026 12031033644 017630 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000002203 12031033644 021575 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000050310 12031033644 020103 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000002202 12031033644 022526 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000035721 12031033644 017514 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000010137 12031033644 016775 0 ustar frothmai frothmai package 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.java 0000664 0001750 0001750 00000026263 12031033644 017636 0 ustar frothmai frothmai package 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/ 0000775 0001750 0001750 00000000000 11656751754 016263 5 ustar frothmai frothmai fits-1.10.0/nom/tam/fits/test/test.fits.bz2 0000664 0001750 0001750 00000000355 11566662566 020632 0 ustar frothmai frothmai BZh91AY&SYà0Oè