/*
* Copyright (c) 2012 European Synchrotron Radiation Facility,
* Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package fable.python;
import java.io.File;
import java.io.InvalidObjectException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import jep.JepException;
import org.dawb.fabio.FableJep;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fable.framework.internal.IPropertyVarKeys;
import fable.framework.logging.FableLogger;
import fable.framework.toolbox.ColumnFileId;
import fable.framework.toolbox.FableUtils;
/**
* This is a python module interface in java for columnfile.py. Use it to parse
* a file with columns
* <p>
* columnfile represents an ascii file with titles begining "#" and multiple
* lines of data. An equals sign "=" on a "#" line implies a parameter = value
* pair
*
* @author SUCHET
*
*/
public class ColumnFile {
private FableJep fableJep;
protected HashMap<String, double[]> columns = new HashMap<String, double[]>();
private ArrayList<IPropertyChangeListener> listeners = new ArrayList<IPropertyChangeListener>();
private Logger logger;
private String fullFileName;
private String fileName;
int nCols = 0;
int nRows = 0;
float[] unitCell;
public String[] titles;
private float[] table_data;
private HashMap<String, Integer> column_index;
/** To sort tableViewer for editor, please set selected columnindex */
private int sortedColumnIndex;
/** To know if we should sort in ascendant or descendant oder. */
private int sortedDirection;
/** Sorted index */
private float[] bigArraySorted;
/**
* Class constructor
*
* @throws JepException
*
*/
public ColumnFile() throws Throwable {
fableJep = FableJep.getFableJep();
importModules();
logger = LoggerFactory.getLogger(ColumnFile.class);
}
/**
* Class constructor column_object already exists in jep_as_parameter
*
* @param jep_as_parameter
* @throws JepException
*/
public ColumnFile(FableJep jep_as_parameter) throws JepException {
fableJep = jep_as_parameter;
importModules();
logger = LoggerFactory.getLogger(ColumnFile.class);
// name = (String)fableJep.getValue("column_object.filename");
fireAddColumnFile();
}
/**
* Import the python modules needed to read columnfiles in jep.
*/
private void importModules() {
try {
FableJep.getFableJep().jepImportModules("numpy"); //$NON-NLS-1$
FableJep.getFableJep().jepImportSpecificDefinition("ImageD11", "columnfile"); //$NON-NLS-1$ //$NON-NLS-2$
} catch (Throwable ex) {
FableUtils.excNoTraceMsg(this, "Error importing modules", ex);
}
}
/**
* Class constructor specifying file to parse.
* <p>
* Creates a new column_object python object. Use it if you don' t have a
* columnfile object in your jep
*
* @param FileName
* @throws JepException
*/
public ColumnFile(String _fileName) throws Throwable {
this();
loadColumnFile(_fileName);
}
/**
* open a new column file in the current jep Init sort index, based on the
* file id, or on the first column if it is not defined.
*
* @param _fileName
* - name of column file to open
* @throws JepException
*/
public void loadColumnFile(String _fileName) {
/*
* final ImageDescriptor pluginImage = AbstractUIPlugin.
* imageDescriptorFromPlugin(Activator.PLUGIN_ID, "fable.gif");
*/
try {
fableJep.set("name", _fileName); //$NON-NLS-1$
setFileName(_fileName);
fableJep.eval("column_object=columnfile.columnfile(name)"); //$NON-NLS-1$
fableJep.eval("column_object.readfile(name)"); //$NON-NLS-1$
loadRows();
sortedColumnIndex = getColumnIDIndex();
setSortedIndex(sortedColumnIndex, SWT.UP);
if (sortedColumnIndex < 0) {
sortedColumnIndex = 0;
}
fireAddColumnFile();
} catch (JepException e) {
// TODO Auto-generated catch block
logger.debug(e.getMessage());
// e.printStackTrace();
}
}
/***
* For example, in transformer, colmnfile object exists in python object
* transformer. So we don't create a new columnfile like it is created in
* contructor. And filename haven' t been set.
*
* @param name
*/
public void setFileName(String name) {
fullFileName = name;
if (fullFileName != null && fullFileName.length() > 1) {
fileName = fullFileName.substring(fullFileName
.lastIndexOf(File.separatorChar) + 1);
} else {
fileName = ""; //$NON-NLS-1$
}
}
/**
* Return the full file name.
*
* @return
*/
public String getFullFileName() {
return fullFileName;
}
/**
* Return the short file name.
*
* @return
*/
public String getFileName() {
return fileName;
}
public void setPythonObject(FableJep jep_as_parameter) throws JepException {
fableJep = jep_as_parameter;
logger = LoggerFactory.getLogger(ColumnFile.class);
fullFileName = (String) fableJep.getValue("column_object.filename"); //$NON-NLS-1$
}
/**
* This method load a big array of float from python object.
*
*/
public void loadRows() {
try {
float f = 0f;
Object objRows = fableJep.getValue("column_object.nrows");
if (objRows instanceof Integer) {
FableLogger.getLogger().debug("Integer");
nRows = (Integer) fableJep.getValue("column_object.nrows"); //$NON-NLS-1$
} else if (objRows instanceof Float) {
FableLogger.getLogger().debug("Float");
f = (Float) fableJep.getValue("column_object.nrows");//$NON-NLS-1$
nRows = (int) f;
}
f = 0f;
Object objCols = fableJep.getValue("column_object.ncols");
if (objCols instanceof Integer) {
FableLogger.getLogger().debug("Integer");
nCols = (Integer) fableJep.getValue("column_object.ncols"); //$NON-NLS-1$
} else if (objCols instanceof Float) {
FableLogger.getLogger().debug("Float");
f = (Float) fableJep.getValue("column_object.ncols");//$NON-NLS-1$
nCols = (int) f;
}
titles = new String[nCols];
column_index = new HashMap<String, Integer>();
for (int i = 0; i < nCols; i++) {
fableJep.set("i", i); //$NON-NLS-1$
titles[i] = (String) fableJep
.getValue("column_object.titles[i]"); //$NON-NLS-1$
column_index.put(titles[i], i);
}
table_data = (float[]) fableJep
.getValue_floatarray("column_object.bigarray.astype(numpy.float32).tostring()"); //$NON-NLS-1$
} catch (JepException e) {
logger.error("can not create loadRows : " + e.getMessage()); //$NON-NLS-1$
}
}
/**
* This method returns the name of the id for a columnfile..... To be
* improved, no ?
*
* @return columnfile id (spot3d_id if file type is flt.)
*/
public String getColumnfileId() {
String id = ""; //$NON-NLS-1$
if (this.fileName != null
&& this.fileName.toLowerCase().endsWith(
ColumnFileId.getString("ColumnFile.flt"))) { //$NON-NLS-1$
id = ColumnFileId.getString("ColumnFile.idflt"); //$NON-NLS-1$
}
return id;
}
/**
* This method is used to get the index of the column that have columnFile
* if. For example, for a flt file, id=spot3d_id and index is the last
* column.
*
* @return the index of the column in titles[] where key = file id. If index
* is not found, return value is -1.
*/
public int getColumnIDIndex() {
int i = 0;
boolean found = false;
for (i = 0; !found && i < titles.length; i++) {
if (titles[i].equals(getColumnfileId())) {
found = true;
}
}
if (!found) {
i = -1;
}
return i - 1;
}
/**
* This methods have been created (thanks to Jon for its help in python), to
* sort columns in table viewer, as it is now virtual. In fact, the content
* provider <code>ILazyContentProvider</code> is not compatible with a sort
* set on the table. <br>
* <b>Numpy module for python side of this method must have been imported
* previously.
*
* @param the
* index of the column to sort
* @return a table with sorted index
*/
public float[] getSortedIndex(int columnIndex) {
try {
fableJep.set("i", columnIndex); //$NON-NLS-1$
// fableJep.set("sortedTable", columnIndex);
// numpy sort returns a table of sorted values
/*
* fableJep.eval("sortedTable =" +
* "numpy.argsort(column_object.bigarray[:i])");
*/
if (sortedDirection == SWT.UP) {
return (float[]) fableJep
.getValue_floatarray("numpy.argsort(column_object.bigarray[i,:]).astype(numpy.float32)." //$NON-NLS-1$
+ "tostring()"); //$NON-NLS-1$
} else {
return (float[]) fableJep
.getValue_floatarray("numpy.argsort(column_object.bigarray[i,:])[::-1]." + //$NON-NLS-1$
"astype(numpy.float32)." //$NON-NLS-1$
+ "tostring()"); //$NON-NLS-1$
}
} catch (JepException e) {
logger.debug(e.getMessage());
}
return null;
}
/**
* This function is used to save a column file object, for example if spots
* have been removed for the original file.
*
* @param filename
* the name of the file to save this column file object.
* @return true if no error occured while saving this file.
*/
public boolean saveColumnFile(String filename) {
boolean bok = true;
try {
fableJep.set("filename", filename); //$NON-NLS-1$
fableJep.eval("column_object.writefile(filename)"); //$NON-NLS-1$
setFileName(filename);
fireSaveDone();
} catch (JepException e) {
bok = false;
}
return bok;
}
/**
* @return A HashMap<String, double[]> with keys for column label and
* double[] for values to plot.
*/
public HashMap<String, double[]> getColumnstoPlot() {
// Get column file
Display.getDefault().syncExec(new Runnable() {
// @Override
public void run() {
try {
columns.clear();
if (fableJep == null) {
throw new InvalidObjectException("FableJep is null");
}
Object val = fableJep.getValue("column_object.ncols"); //$NON-NLS-1$
if (val != null) {
nCols = (Integer) val;
titles = new String[nCols];
for (int i = 0; i < nCols; i++) {
fableJep.set("i", i); //$NON-NLS-1$
String name = (String) fableJep
.getValue("column_object.titles[i]"); //$NON-NLS-1$
fableJep
.eval("filteredValues=column_object.getcolumn(" + //$NON-NLS-1$
"column_object.titles[i])"); //$NON-NLS-1$
float[] myData = (float[]) fableJep
.getValue_floatarray("filteredValues.astype(numpy.float32).tostring()"); //$NON-NLS-1$
double[] myDoubleToplot = new double[myData.length];
nRows = myData.length;
// Converts float to double for the chart
for (int j = 0; j < myData.length; j++) {
myDoubleToplot[j] = (double) myData[j];
}
titles[i] = name;
columns.put(name, myDoubleToplot);
}
}
} catch (JepException ex) {
FableUtils.excNoTraceMsg(this,
"Cannot create columns to plot", ex);
} catch (Exception ex) {
FableUtils.excTraceMsg(this,
"Unexpected error creating columns to plot", ex);
}
}
});
return columns;
}
/***
*
* @param index
* the number of the row.
* @param colName
* the name of the column to get index.
* @return the value of the cell at for this index row and for this column.
*
*/
public float getColumnFileCell(int index, String colName) {
float returnValue = 0;
Integer indexColumn = column_index.get(colName);
if (indexColumn != null) {
index = index + (indexColumn * nRows);
if (index < table_data.length) {
returnValue = table_data[index];
}
}
return returnValue;
}
/*
*
* /
*/
public void update() {
fireColumnHasBeenUpdated();
}
/**
*
*
*/
public void AddUnitCell(float[] values) {
unitCell = values;
fireAddUnitCell();
}
public float[] getUnitCell() {
return unitCell;
}
public int getNRows() {
return nRows;
}
public int getNCols() {
return nCols;
}
// String[] array = (String[])set.toArray(new String[set.size()]);
public String[] getTitles() {
return titles;
// return (String[])columns.keySet().toArray(new
// String[columns.keySet().
// size()]);
}
/**
* Add a listener to this columnFile.
*
* @param listener
*/
public void addPropertyChangeListener(IPropertyChangeListener listener) {
listeners.add(listener);
}
/**
* remove a listener from this columnFile.
*
* @param listener
*/
public void removePropertyrChangeListener(IPropertyChangeListener listener) {
listeners.remove(listener);
}
/**
* Sends a <code>PropertyChangeEvent</code> to all ColumnFile listeners when
* a new column is added in python object.
*/
public void fireAddColumnFile() {
for (Iterator<IPropertyChangeListener> it = listeners.iterator(); it
.hasNext();) {
IPropertyChangeListener element = (IPropertyChangeListener) it
.next();
if (element != null) {
element.propertyChange(new PropertyChangeEvent(this,
IPropertyVarKeys.ADDCOLUMN, null, this));
}
}
}
/**
* Sends a <code>PropertyChangeEvent</code> to all ColumnFile listeners when
* a column have been updated in python object.
*/
public void fireColumnHasBeenUpdated() {
for (Iterator<IPropertyChangeListener> it = listeners.iterator(); it
.hasNext();) {
IPropertyChangeListener element = (IPropertyChangeListener) it
.next();
if (element != null) {
element.propertyChange(new PropertyChangeEvent(this,
IPropertyVarKeys.UPDATECOLUMN, null, this));
}
}
}
private void fireSaveDone() {
for (Iterator<IPropertyChangeListener> it = listeners.iterator(); it
.hasNext();) {
IPropertyChangeListener element = (IPropertyChangeListener) it
.next();
if (element != null) {
element.propertyChange(new PropertyChangeEvent(this,
IPropertyVarKeys.PROPDIRTY, true, false));
}
}
}
/**
* Sends a <code>PropertyChangeEvent</code> to ColumnFile listeners when
* unit cells have been added in python object. <BR>
* This method is very specific to filtered peaks file and for their
* calibration.
*/
public void fireAddUnitCell() {
for (Iterator<IPropertyChangeListener> it = listeners.iterator(); it
.hasNext();) {
IPropertyChangeListener element = (IPropertyChangeListener) it
.next();
if (element != null) {
element.propertyChange(new PropertyChangeEvent(this,
IPropertyVarKeys.ADDUNITCELL, null, this));
}
}
}
/**
* remove all dots between these values
*
* @param key1min
* min value for key1
* @param key1max
* max value for key1
* @param key2min
* min value for key2
* @param key2max
* max value for key2
* @throws JepException
*/
public void removeDots(String key1, double key1min, double key1max,
String key2, double key2min, double key2max) throws JepException {
if (fableJep != null) {
fableJep.set("key1min", key1min); //$NON-NLS-1$
fableJep.set("key1max", key1max); //$NON-NLS-1$
fableJep.set("key2min", key2min); //$NON-NLS-1$
fableJep.set("key2max", key2max); //$NON-NLS-1$
fableJep.eval("column_object." + key1); //$NON-NLS-1$
fableJep.eval("mask = (column_object." + key1 + " >= key1min) & " //$NON-NLS-1$ //$NON-NLS-2$
+ "(column_object." + key1 + "<= key1max) & " //$NON-NLS-1$ //$NON-NLS-2$
+ "(column_object." + key2 + " >= key2min) & " //$NON-NLS-1$ //$NON-NLS-2$
+ "(column_object." + key2 + "<= key2max)"); //$NON-NLS-1$ //$NON-NLS-2$
fableJep.eval("column_object.filter(~mask)"); //$NON-NLS-1$
loadRows();
initBigArraySorted();
update();
}
}
/**
* This methods remove selected rows from columnfile.
*
* @param idlist
* a table of id selected in a tableViewer.
* @param id
* columnFile id.
*/
public void removeRow(Object[] idlist, String id) {
if (fableJep != null) {
makeMaskForSelectedRows(idlist, id);
try {
fableJep.eval("column_object.filter(~mask)"); //$NON-NLS-1$
loadRows();
initBigArraySorted();
} catch (JepException e) {
logger.debug("filter on selected rows canno be apply."); //$NON-NLS-1$
logger.debug(e.getMessage());
}
update();
}
}
/**
* This methods remove selected rows from columnfile.
*
* @param rows
* a table of id selected in a tableViewer.
* @param id
* columnFile id.
*/
public void keepRow(Object[] rows, String id) {
if (fableJep != null) {
makeMaskForSelectedRows(rows, id);
try {
fableJep.eval("column_object.filter(mask)"); //$NON-NLS-1$
loadRows();
// Update bigArraySorted
initBigArraySorted();
} catch (JepException e) {
logger.debug("filter on selected rows cannot be apply."); //$NON-NLS-1$
logger.debug(e.getMessage());
}
update();
}
}
/**
* This method inits or reallocate this array containing a list of index.
*/
private void initBigArraySorted() {
bigArraySorted = new float[nRows];
bigArraySorted = getSortedIndex(sortedColumnIndex);
}
/**
* This function instantiates mask on selected id. For example, in a Table,
* user wants to remove selected rows for a columnfile. He wants to remove
* spots with these id : rows=[0, 1, 2, 9, 10, 11] and id name is spot3d_id.
* Thanks jon for your python code!
*
* @param rows
* a table of id.
* @param id
* ColumnFile id name (spot3d_id for example)
*/
private void makeMaskForSelectedRows(Object[] rows, String id) {
try {
// Converts id into integer
fableJep.eval("myid = column_object." + id //$NON-NLS-1$
+ ".copy().astype(numpy.int32)"); //$NON-NLS-1$
// init mask with zero values
fableJep.eval("mask = numpy.zeros(myid.shape, numpy.int32)"); //$NON-NLS-1$
// logger.debug(fableJep.getValue("print type(mask), \"mask.dtype\""));
// logger.debug(fableJep.getValue("print type(myid), \"myid.dtype \""));
} catch (JepException e1) {
logger.debug("mask can not be instantiate."); //$NON-NLS-1$
logger.debug(e1.getMessage());
}
for (int i = 0; i < rows.length; i++) {
try {
fableJep.eval("numpy.add(mask, myid == " + rows[i] + ", mask)"); //$NON-NLS-1$ //$NON-NLS-2$
} catch (JepException e) {
logger.debug("mask can not be build."); //$NON-NLS-1$
logger.debug(e.getMessage());
}
}
// convert mask to a logical type
try {
fableJep.eval("mask = (mask != 0)"); //$NON-NLS-1$
} catch (JepException e) {
logger.debug("mask can not be converted into a logical type."); //$NON-NLS-1$
logger.debug(e.getMessage());
}
}
/**
* keep all dots selected in the zone and remove others
*
* @param key1min
* min value for key1
* @param key1max
* max value for key1
* @param key2min
* min value for key2
* @param key2max
* max value for key2
* @throws JepException
*/
public void keepDots(String key1, double key1min, double key1max,
String key2, double key2min, double key2max) throws JepException {
if (fableJep != null) {
fableJep.set("key1min", key1min); //$NON-NLS-1$
fableJep.set("key1max", key1max); //$NON-NLS-1$
fableJep.set("key2min", key2min); //$NON-NLS-1$
fableJep.set("key2max", key2max); //$NON-NLS-1$
fableJep.eval("mask = (column_object." + key1 + " >= key1min) & " //$NON-NLS-1$ //$NON-NLS-2$
+ "(column_object." + key1 + "<= key1max) & " //$NON-NLS-1$ //$NON-NLS-2$
+ "(column_object." + key2 + " >= key2min) & " //$NON-NLS-1$ //$NON-NLS-2$
+ "(column_object." + key2 + "<= key2max)"); //$NON-NLS-1$ //$NON-NLS-2$
fableJep.eval("column_object.filter(mask)"); //$NON-NLS-1$
loadRows();
initBigArraySorted();
update();
}
}
/**
* This method is called from transformer to display last computation i.e.
* tth and eta
*
* @param xyLabel
*/
public void displayComputedData(String[] xyLabel) {
firePlotData(xyLabel);
}
/**
*
* @param xyLabel
* a table with two String : {"xLabel", and "yLabel"} which are
* data to plot.
*/
private void firePlotData(String[] xyLabel) {
for (Iterator<IPropertyChangeListener> it = listeners.iterator(); it
.hasNext();) {
IPropertyChangeListener element = (IPropertyChangeListener) it
.next();
if (element != null) {
element.propertyChange(new PropertyChangeEvent(this,
"PlotData", null, xyLabel)); //$NON-NLS-1$
}
}
}
/**
* This methods sends an event to all ColumnFile listeners, and espacially
* views, to remove all dots in plot.
*/
public void fireRemoveAll() {
for (Iterator<IPropertyChangeListener> it = listeners.iterator(); it
.hasNext();) {
IPropertyChangeListener element = (IPropertyChangeListener) it
.next();
if (element != null) {
element.propertyChange(new PropertyChangeEvent(this,
"removeAll", null, null)); //$NON-NLS-1$
}
}
}
public float[] getData() {
return table_data;
}
/*
* public Row[] getRows() {
*
* return rows; }
*/
public void setSortedIndex(int index, int dir) {
sortedColumnIndex = index;
sortedDirection = dir;
initBigArraySorted();
}
/**
* This method return a row at index
*
* @param index
* @return
*/
public float[] getRowAt(int index) {
float[] f = null;
try {
fableJep.set("col", bigArraySorted[index]); //$NON-NLS-1$
} catch (JepException e) {
logger.debug("getRowAt/bigArraySorted" + e.getMessage());
}
try {
f = (float[]) fableJep.getValue_floatarray("column_object." + //$NON-NLS-1$
"bigarray[:,col].astype(numpy.float32).tostring()"); //$NON-NLS-1$
} catch (JepException e) {
logger.debug("getRowAt/getValue_floatarray" + e.getMessage());
}
return f;
}
}
/**/