/*
 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:
 * 
 * 

* 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.