Bitpix.java
package nom.tam.fits.header;
import java.util.logging.Logger;
/*
* #%L
* nom.tam FITS library
* %%
* Copyright (C) 1996 - 2021 nom-tam-fits
* %%
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
* #L%
*/
import nom.tam.fits.FitsException;
import nom.tam.fits.FitsFactory;
import nom.tam.fits.Header;
import nom.tam.fits.HeaderCard;
import nom.tam.util.type.ElementType;
/**
* Standard BITPIX values and associated functions. Since the FITS BITPIX keyword has
* only a handful of legal values, an <code>enum</code> provides ideal type-safe
* representation. It also allows to interface the value for the type of data
* it represents in a natural way.
*
*
* @author Attila Kovacs
*
* @since 1.16
*/
public enum Bitpix {
BYTE(Byte.TYPE, ElementType.BYTE, "bytes"),
SHORT(Short.TYPE, ElementType.SHORT, "16-bit integers"),
INTEGER(Integer.TYPE, ElementType.INT, "32-bit integers"),
LONG(Long.TYPE, ElementType.LONG, "64-bit integers"),
FLOAT(Float.TYPE, ElementType.FLOAT, "32-bit floating point"),
DOUBLE(Double.TYPE, ElementType.DOUBLE, "64-bit floating point");
private static final Logger LOG = Logger.getLogger("nom.tam.fits.HeaderCardParser");
private static final int BITS_TO_BYTES_SHIFT = 3;
/** BITPIX value for <code>byte</code> type data */
public static final int VALUE_FOR_BYTE = 8;
/** BITPIX value for <code>short</code> type data */
public static final int VALUE_FOR_SHORT = 16;
/** BITPIX value for <code>int</code> type data */
public static final int VALUE_FOR_INT = 32;
/** BITPIX value for <code>long</code> type data */
public static final int VALUE_FOR_LONG = 64;
/** BITPIX value for <code>float</code> type data */
public static final int VALUE_FOR_FLOAT = -32;
/** BITPIX value for <code>double</code> type data */
public static final int VALUE_FOR_DOUBLE = -64;
/** the number subclass represented this BITPIX instance */
private Class<? extends Number> numberType;
/** the library's element type */
private ElementType<?> elementType;
/** a concise description of the data type represented */
private String description;
/**
* Constructor for a standard BITPIX instance.
*
* @param numberType the Number subclass
* @param elementType the class of data element
* @param desc a concise description of the data type
*/
Bitpix(Class<? extends Number> numberType, ElementType<?> elementType, String desc) {
this.numberType = numberType;
this.elementType = elementType;
this.description = desc;
}
public final ElementType<?> getElementType() {
return elementType;
}
/**
* Returns the sublass of {@link Number} corresponding for this BITPIX value.
*
* @return the number class for this BITPIX instance.
*
* @see #getPrimitiveType()
* @see Bitpix#forNumberType(Class)
*/
public final Class<? extends Number> getNumberType() {
return numberType;
}
/**
* Returns the primitive built-in Java number type corresponding for this BITPIX value.
*
* @return the primitive class for this BITPIX instance, such as <code>int.class</code>,
* or <code>double.class</code>.
*
* @see #getNumberType()
* @see Bitpix#forPrimitiveType(Class)
*/
public final Class<?> getPrimitiveType() {
return elementType.primitiveClass();
}
/**
* Returns the FITS standard BITPIX header value for this instance.
*
* @return the standard FITS BITPIX value, such as 8, 16, 32, 64, -32, or -64.
*
* @see Bitpix#forValue(int)
* @see #getHeaderCard()
*/
public final int getHeaderValue() {
return elementType.bitPix();
}
/**
* Returns the Java letter ID for this BITPIX instance, such as the letter ID
* used in the Java array representation of that class. For example, an <code>int[]</code>
* array has class <code>I[</code>, so the letter ID is <code>I</code>.
*
* @return The Java letter ID for arrays corresponding to this BITPIX instance.
*
* @see Bitpix#forArrayID(char)
*/
public final char getArrayID() {
return elementType.type();
}
/**
* Returns a concise description of the data type represented by this BITPIX instance.
*
* @return a brief description of the corresponding data type.
*/
public final String getDescription() {
return description;
}
/**
* Returns the size of a data element, in bytes, for this BITPIX instance
*
* @return the size of a data element in bytes.
*/
public final int byteSize() {
return Math.abs(getHeaderValue()) >>> BITS_TO_BYTES_SHIFT;
}
/**
* Returns the standard FITS header card for this BITPIX instance.
*
* @return the standard FITS header card with the BITPIX keyword and the corresponding value
* for this instance.
*
* @see #getHeaderValue()
*/
public final HeaderCard getHeaderCard() {
return HeaderCard.create(Standard.BITPIX, getHeaderValue());
}
/**
* Returns the standard BITPIX object for a primitive type.
*
* @param dataType the primitive class, such as <code>int.class</code>.
* @return the standard BITPIX associated to the number type
* @throws FitsException if the class is not a primitive class, or if its not
* one that has a corresponding BITPIX value (e.g. <code>
* boolean.class</code>).
*
* @see Bitpix#forNumberType(Class)
* @see #getPrimitiveType()
*/
public static Bitpix forPrimitiveType(Class<?> dataType) throws FitsException {
if (dataType == byte.class) {
return BYTE;
}
if (dataType == short.class) {
return SHORT;
}
if (dataType == int.class) {
return INTEGER;
}
if (dataType == long.class) {
return LONG;
}
if (dataType == float.class) {
return FLOAT;
}
if (dataType == double.class) {
return DOUBLE;
}
if (Object.class.isAssignableFrom(dataType)) {
throw new FitsException("No BITPIX for type: " + dataType + " (expected primitive type)");
}
throw new FitsException("No BITPIX for primitive type: " + dataType);
}
/**
* Returns the standard BITPIX object for a number type.
*
* @param dataType the class of number, such as {@link Integer#TYPE}.
* @return the standard BITPIX associated to the number type
* @throws FitsException if there is no standard BITPIX value corresponding to the number type
* (e.g. {@link java.math.BigDecimal}).
*
* @see Bitpix#forPrimitiveType(Class)
* @see #getNumberType()
*/
public static Bitpix forNumberType(Class<? extends Number> dataType) throws FitsException {
if (Byte.class.isAssignableFrom(dataType)) {
return BYTE;
}
if (Short.class.isAssignableFrom(dataType)) {
return SHORT;
}
if (Integer.class.isAssignableFrom(dataType)) {
return INTEGER;
}
if (Long.class.isAssignableFrom(dataType)) {
return LONG;
}
if (Float.class.isAssignableFrom(dataType)) {
return FLOAT;
}
if (Double.class.isAssignableFrom(dataType)) {
return DOUBLE;
}
throw new FitsException("No BITPIX for Number type " + dataType);
}
/**
* Returns the standard BITPIX object based on the value assigned to the BITPIX keyword in the header
*
* @param h the FITS header
* @return the standard BITPIX enum that matches the header description, or is
* inferred from an invalid header description (provided
* {@link FitsFactory#setAllowHeaderRepairs(boolean)} is enabled).
* @throws FitsException if the header does not contain a BITPIX value or it is invalid
* and cannot or will not be repaired.
*
* @see Bitpix#fromHeader(Header, boolean)
* @see Bitpix#forValue(int)
* @see FitsFactory#setAllowHeaderRepairs(boolean)
*/
public static Bitpix fromHeader(Header h) throws FitsException {
return forValue(h.getIntValue(Standard.BITPIX, 0));
}
/**
* Returns the standard BITPIX object based on the value assigned to the BITPIX keyword in the header
*
* @param h the FITS header
* @param allowRepair if we can try repair non-standard (invalid) BITPIX values.
* @return the standard BITPIX enum that matches the header description, or is
* inferred from an invalid header description.
* @throws FitsException if the header does not contain a BITPIX value or it is invalid
* and cannot or will not be repaired.
*
* @see Bitpix#fromHeader(Header)
* @see Bitpix#forValue(int, boolean)
*/
public static Bitpix fromHeader(Header h, boolean allowRepair) throws FitsException {
return forValue(h.getIntValue(Standard.BITPIX, 0), allowRepair);
}
/**
* Returns the standard BITPIX enum value for a given integer value, such as 8, 16, 32, 64, -32, or -64.
* If the value is not one of the standard values, then depending on whether header repairs are enabled
* either an exception is thrown, or else the value the value is 'repaired' and a loh entry is made
* to the logger of {@link Header}.
*
* @param ival The integer value of BITPIX in the FITS header.
* @return The standard value as a Java object.
* @throws FitsException if the value was invalid or irreparable.
*
* @see Bitpix#forValue(int, boolean)
* @see FitsFactory#setAllowHeaderRepairs(boolean)
* @see #getHeaderValue()
*/
public static Bitpix forValue(int ival) throws FitsException {
try {
return forValue(ival, FitsFactory.isAllowHeaderRepairs());
} catch (FitsException e) {
throw new FitsException(e.getMessage() + "\n\n"
+ " --> Try FitsFactory.setAllowHeaderRepairs(true).\n");
}
}
/**
* Returns the standard BITPIX enum value for a given integer value, such as 8, 16, 32, 64, -32, or -64.
* If the value is not one of the standard values, then depending on whether repairs are enabled
* either an exception is thrown, or else the value the value is 'repaired' and a loh entry is made
* to the logger of {@link Header}.
*
* @param ival The integer value of BITPIX in the FITS header.
* @param allowRepair Whether we can fix up invalid values to make them valid.
* @return The standard value as a Java object.
* @throws FitsException if the value was invalid or irreparable.
*
* @see Bitpix#forValue(int)
* @see #getHeaderValue()
*/
public static Bitpix forValue(int ival, boolean allowRepair) throws FitsException {
if (ival == 0) {
throw new FitsException("Invalid BITPIX value:" + ival);
}
// Normally BITPIX must be one one of the supported values. Unfortunately, some
// commercial cameras fill illegal values, such as 20.
// We can 'repair' them by rounding up to the next valid value, so 20 repairs to 32, and
// maxing at +/- 64, so for example -80 repairs to -64.
if (allowRepair) {
int fixed = 0;
if (ival < 0) {
fixed = ival < VALUE_FOR_FLOAT ? VALUE_FOR_DOUBLE : VALUE_FOR_FLOAT;
} else if (ival < VALUE_FOR_BYTE) {
fixed = VALUE_FOR_BYTE;
} else if (ival > VALUE_FOR_LONG) {
fixed = VALUE_FOR_LONG;
} else if (ival > Integer.highestOneBit(ival)) {
fixed = (Integer.highestOneBit(ival) << 1);
}
if (fixed != 0) {
LOG.warning("Repaired invalid BITPIX value:" + ival + " --> " + fixed);
ival = fixed;
}
}
switch (ival) {
case VALUE_FOR_BYTE:
return BYTE;
case VALUE_FOR_SHORT:
return SHORT;
case VALUE_FOR_INT:
return INTEGER;
case VALUE_FOR_LONG:
return LONG;
case VALUE_FOR_FLOAT:
return FLOAT;
case VALUE_FOR_DOUBLE:
return DOUBLE;
default:
throw new FitsException("Invalid BITPIX value:" + ival);
}
}
/**
* Returns the standard BITPIX object for the given Java array ID. The array ID is the same
* letter code as Java uses for identifying ptrimitive array types. For example a Java
* array of <code>long[][]</code> has a class name of <code>J[[</code>, so so the array ID for
* <code>long</code> arrays is <code>J</code>.
*
* @param id The Java letter ID for arrays of the underlying primitive type. E.g. <code>J</code>
* for <code>long</code>.
* @return The standard BITPIX enum corresponding to the data type.
* @throws FitsException if the data type is unknown or does not have a BITPIX ewquivalent.
*
*/
public static Bitpix forArrayID(char id) throws FitsException {
switch (id) {
case 'B':
return BYTE;
case 'S':
return SHORT;
case 'I':
return INTEGER;
case 'J':
return LONG;
case 'F':
return FLOAT;
case 'D':
return DOUBLE;
default:
throw new FitsException("Invalid BITPIX data ID: '" + id + "'");
}
}
}