/*
* Copyright (C) 2015 by Array Systems Computing Inc. http://www.array.ca
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see http://www.gnu.org/licenses/
*/
package org.esa.s1tbx.io.binary;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
/**
* A reader for reading binary files
*/
public final class BinaryFileReader {
private static final String EM_EXPECTED_X_FOUND_Y_BYTES = "Expected bytes to read %d, but only found %d";
private static final String EM_READING_X_TYPE = "Reading '%s'-Type";
private static final String EM_NOT_PARSABLE_X_STRING = "Not able to parse %s string";
private final ImageInputStream stream;
public BinaryFileReader(final ImageInputStream stream) {
this.stream = stream;
}
public void close() throws IOException {
stream.close();
}
public void setByteOrder(ByteOrder order) {
stream.setByteOrder(order);
}
public void seek(final long pos) throws IOException {
stream.seek(pos);
}
public void skipBytes(final long numBytes) throws IOException {
stream.skipBytes(numBytes);
}
public int readB1() throws IOException, IllegalBinaryFormatException {
final long streamPosition = stream.getStreamPosition();
try {
return stream.readByte() & 0xFF;
} catch (IOException e) {
final String message = String.format(EM_READING_X_TYPE, new Object[]{"B1"});
throw new IllegalBinaryFormatException(message, streamPosition, e);
}
}
public int readUB1() throws IOException, IllegalBinaryFormatException {
final long streamPosition = stream.getStreamPosition();
try {
return stream.readUnsignedByte();
} catch (IOException e) {
final String message = String.format(EM_READING_X_TYPE, new Object[]{"B2"});
throw new IllegalBinaryFormatException(message, streamPosition, e);
}
}
public short readB2() throws IOException, IllegalBinaryFormatException {
final long streamPosition = stream.getStreamPosition();
try {
return stream.readShort();
} catch (IOException e) {
final String message = String.format(EM_READING_X_TYPE, new Object[]{"B2"});
throw new IllegalBinaryFormatException(message, streamPosition, e);
}
}
public int readUB2() throws IOException, IllegalBinaryFormatException {
final long streamPosition = stream.getStreamPosition();
try {
return stream.readUnsignedShort();
} catch (IOException e) {
final String message = String.format(EM_READING_X_TYPE, new Object[]{"B2"});
throw new IllegalBinaryFormatException(message, streamPosition, e);
}
}
public int readB4() throws IOException, IllegalBinaryFormatException {
final long streamPosition = stream.getStreamPosition();
try {
return stream.readInt();
} catch (IOException e) {
final String message = String.format(EM_READING_X_TYPE, new Object[]{"B4"});
throw new IllegalBinaryFormatException(message, streamPosition, e);
}
}
public long readB8() throws IOException, IllegalBinaryFormatException {
final long streamPosition = stream.getStreamPosition();
try {
return stream.readLong();
} catch (IOException e) {
final String message = String.format(EM_READING_X_TYPE, new Object[]{"B8"});
throw new IllegalBinaryFormatException(message, streamPosition, e);
}
}
public void read(final byte[] array) throws IOException {
stream.readFully(array, 0, array.length);
}
public void read(final char[] array) throws IOException {
stream.readFully(array, 0, array.length);
}
public void read(final short[] array) throws IOException {
stream.readFully(array, 0, array.length);
}
public void read(final int[] array) throws IOException {
stream.readFully(array, 0, array.length);
}
public void read(final long[] array) throws IOException {
stream.readFully(array, 0, array.length);
}
public void read(final float[] array) throws IOException {
stream.readFully(array, 0, array.length);
}
public void read(final double[] array) throws IOException {
stream.readFully(array, 0, array.length);
}
public long readIn(final int n) throws IOException, IllegalBinaryFormatException {
final long streamPosition = stream.getStreamPosition();
final String longStr = readAn(n).trim();
if (longStr.isEmpty()) return 0;
return parseLong(longStr, streamPosition);
}
private static long parseLong(String integerStr, long streamPosition) throws IllegalBinaryFormatException {
long number;
try {
number = Long.parseLong(integerStr);
} catch (NumberFormatException e) {
final String newStr = createIntegerString(integerStr,
new char[]{'.', '-'}, ' ').trim();
try {
if (newStr.isEmpty() || newStr.equals(".") || newStr.equals("-")) return 0;
number = Long.parseLong(newStr);
} catch (NumberFormatException e2) {
final String message = String.format(EM_NOT_PARSABLE_X_STRING + " \"" + integerStr + '"',
new Object[]{"integer"});
throw new IllegalBinaryFormatException(message, streamPosition, e);
}
}
return number;
}
public double readFn(final int n) throws IOException, IllegalBinaryFormatException {
final long streamPosition = stream.getStreamPosition();
String doubleString = readAn(n).trim();
if (doubleString.isEmpty()) return 0;
doubleString = doubleString.replaceAll("D", "E");
try {
return Double.parseDouble(doubleString);
} catch (NumberFormatException e) {
final String message = String.format(EM_NOT_PARSABLE_X_STRING, new Object[]{"double"});
throw new IllegalBinaryFormatException(message, streamPosition, e);
}
}
public void readFn(final int n, final double[] numbers) throws IOException, IllegalBinaryFormatException {
final long streamPosition = stream.getStreamPosition();
for (int i = 0; i < numbers.length; i++) {
try {
numbers[i] = Double.parseDouble(readAn(n).trim());
} catch (IllegalBinaryFormatException e) {
final String message = String.format(EM_READING_X_TYPE, new Object[]{"Gn[]"});
throw new IllegalBinaryFormatException(message, streamPosition, e);
}
}
}
public double readEn(final int n) throws IOException {
final byte[] b = new byte[n];
int bytesRead = stream.read(b);
String str = new String(b).trim();
if (str.isEmpty()) return 0;
ByteBuffer bBuffer = ByteBuffer.wrap(b);
double d = bBuffer.getDouble();
return d;
}
public String readAn(final int n) throws IOException, IllegalBinaryFormatException {
final long streamPosition = stream.getStreamPosition();
final byte[] bytes = new byte[n];
final int bytesRead;
try {
bytesRead = stream.read(bytes);
} catch (IOException e) {
final String message = String.format(EM_READING_X_TYPE, new Object[]{"An"});
throw new IllegalBinaryFormatException(message, streamPosition, e);
}
if (bytesRead != n) {
final String message = String.format(EM_EXPECTED_X_FOUND_Y_BYTES, n, bytesRead);
throw new IllegalBinaryFormatException(message, streamPosition);
}
final String str = new String(bytes);
if (str.contains("\0"))
return str.replace("\0", " ");
return str;
}
public int[] readInArray(final int arraySize, final int intValLength)
throws IOException, IllegalBinaryFormatException {
final long streamPosition = stream.getStreamPosition();
final int[] ints = new int[arraySize];
for (int i = 0; i < ints.length; i++) {
final String integerString = readAn(intValLength).trim();
if (integerString.length() > 0) {
ints[i] = (int) parseLong(integerString, streamPosition + i * intValLength);
}
}
return ints;
}
public long getCurrentPos() throws IOException {
return stream.getStreamPosition();
}
public long getLength() throws IOException {
return stream.length();
}
private static String createIntegerString(String name, char[] validChars, char replaceChar) {
char[] sortedValidChars = null;
if (validChars == null) {
sortedValidChars = new char[0];
} else {
sortedValidChars = validChars.clone();
}
Arrays.sort(sortedValidChars);
StringBuilder validName = new StringBuilder(name.length());
boolean pad = false;
for (int i = 0; i < name.length(); i++) {
final char ch = name.charAt(i);
if (!pad && Character.isDigit(ch)) {
validName.append(ch);
} else if (!pad && Arrays.binarySearch(sortedValidChars, ch) >= 0) {
validName.append(ch);
} else {
pad = true;
validName.append(replaceChar);
}
}
return validName.toString();
}
}