/*
This file is part of the software framework JStatCom,
a library for building applications for data based analysis.
Copyright (C) 2000-2005 Markus Kraetzig
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
*/
package com.jstatcom.io;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.StringTokenizer;
import com.jstatcom.component.TopFrameReference;
import com.jstatcom.model.JSCConstants;
import com.jstatcom.model.JSCData;
import com.jstatcom.model.JSCDate;
import com.jstatcom.model.JSCNArray;
import com.jstatcom.model.JSCSArray;
import com.jstatcom.model.JSCTypeDef;
import com.jstatcom.model.JSCTypes;
import com.jstatcom.model.SymbolTable;
import com.jstatcom.ts.TS;
import com.jstatcom.ts.TSDate;
import com.jstatcom.ts.TSDateRange;
/**
* Class for importing files from Gauss .fmt files. There are 4
* supported formats, see the Gauss reference manual for a specification of the
* binary formats:
*
* - Small Matrix v89
*
- Extended Matrix v89
*
- Matrix v92
*
- Universal Matrix v96
*
*
* Version v96 is also supported for exporting data via a static method.
*
* @author Markus Kraetzig
*/
public final class TSGaussFMTHandler implements DataHandler {
private boolean noDialog = false;
private static final TSGaussFMTHandler handler = new TSGaussFMTHandler();
private File selectedFile = null;
private TSDataDialog dialog = null;
private boolean cancelled = false;
private final SymbolTable symbolTable = new SymbolTable(
"TSGaussFMTHandler", false);
/**
* Gets the singleton instance of this handler.
*
* @return instance
*/
public static TSGaussFMTHandler getInstance() {
return handler;
}
/**
* Creates a TSGaussFMTHandler.
*/
private TSGaussFMTHandler() {
super();
}
/**
* Helper method to get an array of valid variable names.
*
* @param fName
* filename of the file to be read in
* @param cols
* number of columns with numerical data
* @param namesFromDialog
* string with space separated names, typically from a dialog,
* for example "a b c d"
* @return a cols x 1 string array with valid names that are either
* assembled from fName and an index or from
* namesFromDialog
*/
public static String[] getNamesArray(File fName, int cols,
String namesFromDialog) {
String fileName = fName.getName();
String[] names = new String[cols];
if (fileName.indexOf('.') > -1)
fileName = fileName.substring(0, fileName.lastIndexOf(".")) + "_";
if (JSCConstants.isValidName(fileName) != null)
fileName = "var_";
for (int i = 0; i < names.length; i++)
names[i] = fileName + (i + 1);
// overwrite the default names with the ones from the dialog
StringTokenizer st = new StringTokenizer(namesFromDialog);
String[] stringArray = new String[st.countTokens()];
for (int i = 0; i < stringArray.length; i++)
stringArray[i] = st.nextToken();
int length = stringArray.length;
if (names.length < stringArray.length)
length = names.length;
for (int i = 0; i < length; i++) {
if (JSCConstants.isValidName(stringArray[i]) == null)
names[i] = stringArray[i];
}
return names;
}
/**
* Initiates parsing of .fmt Gauss matrix file.
*
* @param file
* the file to read in
* @return true of import was ok, false if
* cancelled or erroneous
* @throws IllegalArgumentException
* if (file == null || !file.isFile())
* @throws RuntimeException
* when file reading fails
*/
public boolean importData(File file) {
if (file == null)
throw new IllegalArgumentException("Argument was null.");
if (!file.isFile())
throw new IllegalArgumentException("Invalid data file " + file
+ ".");
selectedFile = file;
cancelled = false;
try {
DataInputStream in = new DataInputStream(new FileInputStream(file));
// decide which parsing should be used
int size = in.available();
byte[] header = new byte[size];
in.read(header);
in.close();
// to reset
in = new DataInputStream(new FileInputStream(file));
// small v89
if (header[0] == (byte) 0xDD && header[1] == (byte) 0xDD) {
importSmallMatrixv89(in, header);
in.close();
return !cancelled;
}
// extended v89
if ((header[0] == (byte) 0xDD && header[1] == (byte) 0xEE)
|| header[0] == (byte) 0xEE && header[1] == (byte) 0xDD) {
importExtendedMatrixv89(in, header);
in.close();
return !cancelled;
}
// v92
if (header[0] == (byte) 0x00 && header[1] == (byte) 0x00) {
importMatrixv92(in, header);
in.close();
return !cancelled;
}
// v96
if (header[0] == (byte) 0xFF && header[1] == (byte) 0xFF) {
importMatrixv96(in, header);
in.close();
return !cancelled;
}
throw new RuntimeException(
"Unrecognized matrix file version (neither v89, v92, v96).");
} catch (Throwable t) {
throw new RuntimeException(t.getMessage());
}
}
/**
* Reads the small Matrix v89 .fmt format.
*
* @param in
* stream to read from
* @param header
* 128 byte array with header
*/
private void importSmallMatrixv89(DataInputStream in, byte[] header)
throws IOException {
boolean littleEndian = (header[8] == (byte) 0x86);
in.skipBytes(2);
// rows
short rows = in.readShort();
if (littleEndian)
rows = Short.reverseBytes(rows);
// cols
short cols = in.readShort();
if (littleEndian)
cols = Short.reverseBytes(cols);
in.skipBytes(10);
double[][] val = new double[rows][cols];
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
if (littleEndian)
val[i][j] = Double.longBitsToDouble(Long.reverseBytes(in
.readLong()));
else
val[i][j] = in.readDouble();
setDataToSymbolTable(val);
}
/**
* Reads the extended Matrix v89 .fmt format.
*
* @param in
* stream to read from
* @param header
* 128 byte array with header
*/
private void importExtendedMatrixv89(DataInputStream in, byte[] header)
throws IOException {
boolean littleEndian = (header[0] == (byte) 0xDD);
in.skipBytes(4);
// rows
int rows = in.readInt();
if (littleEndian)
rows = Integer.reverseBytes(rows);
// cols
int cols = in.readInt();
if (littleEndian)
cols = Integer.reverseBytes(cols);
in.skipBytes(4);
double[][] val = new double[rows][cols];
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
if (littleEndian)
val[i][j] = Double.longBitsToDouble(Long.reverseBytes(in
.readLong()));
else
val[i][j] = in.readDouble();
setDataToSymbolTable(val);
}
/**
* Reads the Matrix v92 .fmt format.
*
* @param in
* stream to read from
* @param header
* 128 byte array with header
*/
private void importMatrixv92(DataInputStream in, byte[] header)
throws IOException {
boolean isLittleEndian = (header[4] == (byte) 0xCD);
in.skipBytes(24);
int type = in.readInt();
if (isLittleEndian)
type = Integer.reverseBytes(type);
in.skipBytes(100);
double[][] val = null;
if (type == 0)// scalar
{
val = new double[1][1];
if (isLittleEndian)
val[0][0] = Double.longBitsToDouble(Long.reverseBytes(in
.readLong()));
else
val[0][0] = in.readDouble();
} else if (type == 1)// row vector
{
int rows = in.readInt();
if (isLittleEndian)
rows = Integer.reverseBytes(rows);
in.skipBytes(4); // 4 padding bytes
val = new double[1][rows];
for (int i = 0; i < rows; i++)
if (isLittleEndian)
val[0][i] = Double.longBitsToDouble(Long.reverseBytes(in
.readLong()));
else
val[0][i] = in.readDouble();
} else if (type == 2)// matrix
{
int rows = in.readInt();
if (isLittleEndian)
rows = Integer.reverseBytes(rows);
int cols = in.readInt();
if (isLittleEndian)
cols = Integer.reverseBytes(cols);
val = new double[rows][cols];
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
if (isLittleEndian)
val[i][j] = Double.longBitsToDouble(Long
.reverseBytes(in.readLong()));
else
val[i][j] = in.readDouble();
}
setDataToSymbolTable(val);
}
/**
* Reads the Matrix v96 .fmt format.
*
* @param in
* stream to read from
* @param header
* 128 byte array with header
*/
private void importMatrixv96(DataInputStream in, byte[] header)
throws IOException {
boolean isLittleEndian = (header[20] == (byte) 0x00);
in.skipBytes(60);
int type = in.readInt();
if (isLittleEndian)
type = Integer.reverseBytes(type);
int major = in.readInt(); // rowMajor == 1, colMajor == 2
if (isLittleEndian)
major = Integer.reverseBytes(major);
in.skipBytes(60);
double[][] val = null;
if (type == 0)// scalar
{
val = new double[1][1];
if (isLittleEndian)
val[0][0] = Double.longBitsToDouble(Long.reverseBytes(in
.readLong()));
else
val[0][0] = in.readDouble();
} else if (type == 1)// row vector
{
int rows = in.readInt();
if (isLittleEndian)
rows = Integer.reverseBytes(rows);
in.skipBytes(4); // 4 padding bytes
val = new double[1][rows];
for (int i = 0; i < rows; i++)
if (isLittleEndian)
val[0][i] = Double.longBitsToDouble(Long.reverseBytes(in
.readLong()));
else
val[0][i] = in.readDouble();
} else if (type == 2)// matrix
{
int rows = in.readInt();
if (isLittleEndian)
rows = Integer.reverseBytes(rows);
int cols = in.readInt();
if (isLittleEndian)
cols = Integer.reverseBytes(cols);
val = new double[rows][cols];
if (major == 1) // rows major
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
if (isLittleEndian)
val[i][j] = Double.longBitsToDouble(Long
.reverseBytes(in.readLong()));
else
val[i][j] = in.readDouble();
else
// column major
for (int i = 0; i < cols; i++)
for (int j = 0; j < rows; j++)
if (isLittleEndian)
val[j][i] = Double.longBitsToDouble(Long
.reverseBytes(in.readLong()));
else
val[j][i] = in.readDouble();
}
setDataToSymbolTable(val);
}
private void setDataToSymbolTable(double[][] values) {
if (!noDialog) {
getDataDialog().setVisible(true);
if (getDataDialog().isCanceled()) {
cancelled = true;
return;
}
}
symbolTable.set(new JSCNArray(TSImportTypes.DATA.name(), values));
symbolTable.set(new JSCDate(TSImportTypes.STARTDATE.name(),
getDataDialog().getTSDate()));
String[] names = new String[0];
if (values.length > 0)
names = getNamesArray(selectedFile, values[0].length,
getDataDialog().getVariableNames());
symbolTable.set(new JSCSArray(TSImportTypes.VARNAMES.name(), names));
}
/**
* Gets the imported data according to an instance of
* TSImportTypes.
*
* @param type
* the elementtype of a time series dataset
* @return the data stored as JSCData
* @throws IllegalArgumentException
* if (type == null)
* @throws IllegalStateException
* if nothing was imported before
*/
public JSCData getData(ImportTypes type) {
if (symbolTable == null)
throw new IllegalStateException("There was nothing imported yet.");
if (type == null)
throw new IllegalArgumentException("Argument was null.");
return symbolTable.getJSCData(new JSCTypeDef(type.name(), type.type()));
}
/**
* Gets the dialog used for raw ASCII files.
*/
private TSDataDialog getDataDialog() {
if (dialog == null)
dialog = new TSDataDialog(TopFrameReference.getTopFrameRef(), true);
dialog.setLocationRelativeTo(TopFrameReference.getTopFrameRef());
return dialog;
}
public void exportTS(File file, TS[] ts, String description) {
if (file == null)
throw new IllegalArgumentException("File argument was null.");
if (ts == null)
throw new IllegalArgumentException("Time series argument was null.");
if (ts.length == 0)
return;
TSDateRange mergedRange = TSDateRange.getMergedRange(ts);
// data
double[][] values = new double[mergedRange.numOfObs()][ts.length];
for (int i = 0; i < mergedRange.numOfObs(); i++) {
TSDate currentDate = mergedRange.dateForIndex(i);
for (int j = 0; j < ts.length; j++)
values[i][j] = ts[j].valueAt(currentDate);
}
export(values, file);
}
public void exportData(File file, JSCData data, String description) {
if (file == null)
throw new IllegalArgumentException("File argument was null.");
if (data == null)
throw new IllegalArgumentException("Data argument was null.");
if (data.type() != JSCTypes.NARRAY)
throw new IllegalArgumentException("Data of type \"" + data.type()
+ "\" cannot be stored in Excel format.");
if (data.isEmpty())
return;
export(((JSCNArray) data).doubleArray(), file);
}
private void export(double[][] values, File file) {
try {
DataOutputStream out = new DataOutputStream(new FileOutputStream(
file));
// write header 128 byte
byte[] toWrite = new byte[40];
for (int i = 0; i < 4; i++)
toWrite[i] = (byte) 0xFF;
for (int i = 4; i < 8; i++)
toWrite[i] = (byte) 0x00;
for (int i = 8; i < 12; i++)
toWrite[i] = (byte) 0xFF;
for (int i = 12; i < 16; i++)
toWrite[i] = (byte) 0x00;
for (int i = 16; i < 20; i++)
toWrite[i] = (byte) 0xFF;
for (int i = 20; i < 24; i++)
toWrite[i] = (byte) 0xFF;
for (int i = 24; i < 28; i++)
toWrite[i] = (byte) 0x00;
toWrite[28] = (byte) 0xAB;
toWrite[29] = (byte) 0xCD;
toWrite[30] = (byte) 0xEF;
toWrite[31] = (byte) 0x01;
toWrite[32] = (byte) 0x00;
toWrite[33] = (byte) 0x00;
toWrite[34] = (byte) 0x00;
toWrite[35] = (byte) 0x01;
out.write(toWrite);
out.writeInt(1);
out.writeInt(1008);
out.writeInt(8);
out.writeInt(0);
out.writeInt(1);
// scalar, row vector, matrix
int dim = 0;
if (values.length == 1 && values[0].length == 1)
dim = 0;
if (values.length > 1 && values[0].length == 1)
dim = 1;
if (values.length > 1 && values[0].length > 1)
dim = 2;
out.writeInt(dim);
out.writeInt(1);
out.writeInt(0);
int headerSize = 128 + 4 * dim;
if (dim == 1) // padding
headerSize += 4;
out.writeInt(headerSize);
toWrite = new byte[52];
out.write(toWrite);
if (dim == 0)
out.writeDouble(values[0][0]);
if (dim == 1) {
out.writeInt(values.length);
toWrite = new byte[4];
out.write(toWrite);
}
if (dim == 2) {
out.writeInt(values.length);
out.writeInt(values[0].length);
}
for (int i = 0; i < values.length; i++)
for (int j = 0; j < values[0].length; j++)
out.writeDouble(values[i][j]);
out.close();
} catch (Throwable ex) {
throw new RuntimeException(ex.getMessage());
}
}
/**
* Sets this handler in test mode. No user dialog is shown, default values
* are used instead. This is helpful for running automated unit tests.
*
* @param noDialog
*/
public void setTestMode(boolean noDialog) {
this.noDialog = noDialog;
}
}
Ads help maintain this website.