nom.tam.fits.FitsUtil Maven / Gradle / Ivy
package nom.tam.fits;
/*
* #%L
* nom.tam FITS library
* %%
* Copyright (C) 2004 - 2015 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 java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import nom.tam.util.ArrayDataOutput;
import nom.tam.util.AsciiFuncs;
import nom.tam.util.RandomAccess;
/**
* This class comprises static utility functions used throughout the FITS
* classes.
*/
public final class FitsUtil {
private static final int BYTE_REPRESENTING_BLANK = 32;
private static final int BYTE_REPRESENTING_MAX_ASCII_VALUE = 126;
/**
* the logger to log to.
*/
private static final Logger LOG = Logger.getLogger(FitsUtil.class.getName());
private static boolean wroteCheckingError = false;
/**
* Utility class, do not instantiate it.
*/
private FitsUtil() {
}
/**
* @return Total size of blocked FITS element, using e.v. padding to fits
* block size.
* @param size
* the current size.
*/
public static int addPadding(int size) {
return size + padding(size);
}
/**
* @return Total size of blocked FITS element, using e.v. padding to fits
* block size.
* @param size
* the current size.
*/
public static long addPadding(long size) {
return size + padding(size);
}
/**
* @return Convert an array of booleans to bytes.
* @param bool
* array of booleans
*/
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;
}
/**
* @return Convert bytes to Strings.
* @param bytes
* byte array to convert
* @param maxLen
* the max string length
*/
public static String[] byteArrayToStrings(byte[] bytes, 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[bytes.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 (bytes[start] != BYTE_REPRESENTING_BLANK) {
break; // Skip only spaces.
}
}
for (; end > start; end -= 1) {
if (bytes[end - 1] != BYTE_REPRESENTING_BLANK) {
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 (bytes[j] == 0) {
end = j;
break;
}
if (checking && (bytes[j] < BYTE_REPRESENTING_BLANK || bytes[j] > BYTE_REPRESENTING_MAX_ASCII_VALUE)) {
errFound = true;
bytes[j] = BYTE_REPRESENTING_BLANK;
}
}
res[i] = AsciiFuncs.asciiString(bytes, start, end - start);
if (errFound && !FitsUtil.wroteCheckingError) {
LOG.log(Level.SEVERE, "Warning: Invalid ASCII character[s] detected in string: " + res[i]
+ " Converted to space[s]. Any subsequent invalid characters will be converted silently");
FitsUtil.wroteCheckingError = true;
}
}
return res;
}
/**
* @return Convert an array of bytes to booleans.
* @param bytes
* the array of bytes to get the booleans from.
*/
static boolean[] byteToBoolean(byte[] bytes) {
boolean[] bool = new boolean[bytes.length];
for (int i = 0; i < bytes.length; i += 1) {
bool[i] = bytes[i] == 'T';
}
return bool;
}
/**
* @return Find out where we are in a random access file .
* @param o
* the stream to get the position
*/
public static long findOffset(Closeable o) {
if (o instanceof RandomAccess) {
return ((RandomAccess) o).getFilePointer();
} else {
return -1;
}
}
/**
* @return 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.
* @param url
* the url to get the stream from
* @param level
* max levels of redirection
* @throws IOException
* if the operation failed
*/
public static InputStream getURLStream(URL url, int level) throws IOException {
URLConnection conn = null;
int code = -1;
try {
conn = url.openConnection();
if (conn instanceof HttpURLConnection) {
code = ((HttpURLConnection) conn).getResponseCode();
}
return conn.getInputStream();
} catch (ProtocolException e) {
LOG.log(Level.WARNING, "could not connect to " + url + (code >= 0 ? " got responce-code" + code : ""), e);
throw e;
}
}
/**
* @return Get the maximum length of a String in a String array.
* @param strings
* array of strings to check
* @throws FitsException
* if the operation failed
*/
public static int maxLength(String[] strings) throws FitsException {
int max = 0;
for (String element : strings) {
if (element != null && element.length() > max) {
max = element.length();
}
}
return max;
}
/**
* Add padding to an output stream.
*
* @param stream
* stream to pad
* @param size
* the current size
* @throws FitsException
* if the operation failed
*/
public static void pad(ArrayDataOutput stream, long size) throws FitsException {
pad(stream, size, (byte) 0);
}
/**
* Add padding to an output stream.
*
* @param stream
* stream to pad
* @param size
* the current size
* @param fill
* the fill byte to use
* @throws FitsException
* if the operation failed
*/
public static void pad(ArrayDataOutput stream, long size, byte fill) throws FitsException {
int len = padding(size);
if (len > 0) {
byte[] buf = new byte[len];
Arrays.fill(buf, fill);
try {
stream.write(buf);
stream.flush();
} catch (Exception e) {
throw new FitsException("Unable to write padding", e);
}
}
}
/**
* @return How many bytes are needed to fill a 2880 block?
* @param size
* the size without padding
*/
public static int padding(int size) {
return padding((long) size);
}
public static int padding(long size) {
int mod = (int) (size % FitsFactory.FITS_BLOCK_SIZE);
if (mod > 0) {
mod = FitsFactory.FITS_BLOCK_SIZE - mod;
}
return mod;
}
/**
* Reposition a random access stream to a requested offset.
*
* @param o
* the closable to reposition
* @param offset
* the offset to position it to.
* @throws FitsException
* if the operation was failed or not possible
*/
public static void reposition(Closeable o, long offset) throws FitsException {
if (o == null) {
throw new FitsException("Attempt to reposition null stream");
} else 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.getMessage(), e);
}
}
/**
* Convert an array of Strings to bytes.
*
* @return the resulting bytes
* @param stringArray
* the array with Strings
* @param maxLen
* the max length (in bytes) of every String
*/
public static byte[] stringsToByteArray(String[] stringArray, int maxLen) {
byte[] res = new byte[stringArray.length * maxLen];
for (int i = 0; i < stringArray.length; i += 1) {
byte[] bstr;
if (stringArray[i] == null) {
bstr = new byte[0];
} else {
bstr = AsciiFuncs.getBytes(stringArray[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;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy