/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This program 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 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Nicolas Brodu
*
* $Id: ExpressionDataSource.java,v 1.9 2007/06/29 16:36:16 ogor Exp $
*
* Changes
* -------
* 14-Jan-2004 : Creation date (NB);
*
*/
package jsynoptic.data;
import java.util.Iterator;
import java.util.Set;
import jsynoptic.parser.ExpressionNode;
import simtools.data.DataException;
import simtools.data.DataInfo;
import simtools.data.DataSource;
import simtools.data.DataSourceListener;
import simtools.data.NoSuchIndex;
import simtools.data.UnsupportedOperation;
/**
* A data source that evaluate a mathematical expression on other sources to compute
* its values
*/
public class ExpressionDataSource extends DataSource implements DataSourceListener {
protected ExpressionNode rootNode;
protected Set variables;
protected DataInfo info;
// Intersection of all the variable data sources definition range
protected long minIndex = 0;
protected long maxIndex = 0;
/** min max values if computed */
protected double min;
protected double max;
protected boolean minmaxOk;
protected static String EXPRESSION_DATA_SOURCE_MARKER = "ExpressionDataSource:";
/**
* @return Returns the variables.
*/
public Set getVariables() {
return variables;
}
/* (non-Javadoc)
* @see simtools.data.DataSource#getInformation()
*/
public DataInfo getInformation() {
return info;
}
public ExpressionDataSource(DataInfo di, ExpressionNode rootNode) {
info = di;
info.id = EXPRESSION_DATA_SOURCE_MARKER + info.id;
this.rootNode = rootNode;
variables = rootNode.getVariables();
// Remove unknown variable types.
// Use the occasion to compute our index range (intersection of all ranges)
// If no data source is used, the index range is irrelevant
minIndex = 0;
maxIndex = Long.MAX_VALUE;
for (Iterator it = variables.iterator(); it.hasNext();) {
Object o = it.next();
if (!(o instanceof VariableAssociation)) {
it.remove();
continue;
}
DataSource ds = ((VariableAssociation)o).ds;
try {
minIndex = Math.max(minIndex, ds.getStartIndex());
maxIndex = Math.min(maxIndex, ds.getLastIndex());
} catch (UnsupportedOperation e) {
}
ds.addListener(this);
}
if (maxIndex == Long.MAX_VALUE) maxIndex = 0;
minmaxOk=false;
}
/* (non-Javadoc)
* @see simtools.data.DataSource#computeMin()
*/
public Object computeMin() throws UnsupportedOperation {
if (!minmaxOk) {
computeMinMax();
}
return new Double(min);
}
/* (non-Javadoc)
* @see simtools.data.DataSource#computeMax()
*/
public Object computeMax() throws UnsupportedOperation {
if (!minmaxOk) {
computeMinMax();
}
return new Double(max);
}
/* (non-Javadoc)
* @see simtools.data.DataSource#getMin()
*/
public Object getMin() throws UnsupportedOperation {
if (!minmaxOk) {
computeMinMax();
}
return new Double(min);
}
/* (non-Javadoc)
* @see simtools.data.DataSource#getMax()
*/
public Object getMax() throws UnsupportedOperation {
if (!minmaxOk) {
computeMinMax();
}
return new Double(max);
}
/**
* @throws UnsupportedOperation
*
*/
protected void computeMinMax() throws UnsupportedOperation {
Object v0;
try {
v0 = getValue(minIndex);
} catch (DataException e) {
throw new UnsupportedOperation();
}
Number n0;
if(v0 instanceof Number){
n0=(Number)v0;
}
else{
throw new UnsupportedOperation();
}
min=n0.doubleValue();
max=n0.doubleValue();
for(long i=minIndex+1;i<=maxIndex;i++){
double v;
try {
v = ((Number)getValue(i)).doubleValue();
} catch (DataException e1) {
break;
}
if(v<min){
min=v;
}
if(v>max){
max=v;
}
}
minmaxOk=true;
}
/**
* @param exp
* @param node
*/
public synchronized void changeExpression(String exp, ExpressionNode node) {
cleanup();
rootNode = node;
variables = rootNode.getVariables();
info.comment = exp;
// Remove unknown variable types.
// Use the occasion to compute our index range (intersection of all ranges)
// If no data source is used, the index range is irrelevant
minIndex = Long.MIN_VALUE;
maxIndex = Long.MAX_VALUE;
for (Iterator it = variables.iterator(); it.hasNext();) {
Object o = it.next();
if (!(o instanceof VariableAssociation)) {
it.remove();
continue;
}
DataSource ds = ((VariableAssociation)o).ds;
try {
minIndex = Math.max(minIndex, ds.getStartIndex());
maxIndex = Math.min(maxIndex, ds.getLastIndex());
} catch (UnsupportedOperation e) {
}
ds.addListener(this);
if (minIndex == Long.MIN_VALUE) minIndex = 0;
}
computeSortedOrder();
minmaxOk=false;
notifyListenersForValueChange(minIndex,maxIndex);
notifyEndNotificationListeners();
}
/** Remove the listeners */
public synchronized void cleanup() {
for (Iterator it = variables.iterator(); it.hasNext();) {
DataSource ds = ((VariableAssociation)it.next()).ds;
ds.removeListener(this);
}
}
/* (non-Javadoc)
* @see simtools.data.ValueProvider#getValue(long)
*/
public Object getValue(long index) throws DataException {
if(index<0)
throw new NoSuchIndex(index);
synchronized (this) {
// Lock the variables index
for (Iterator it = variables.iterator(); it.hasNext();) {
((VariableAssociation)it.next()).lockIndex(index);
}
Object ret = null;
try {
// Compute the value at this index
ret = rootNode.evaluate();
}
finally {
// Release the variables index
for (Iterator it = variables.iterator(); it.hasNext();) {
((VariableAssociation)it.next()).releaseIndex();
}
}
return ret;
}
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceValueChanged(simtools.data.DataSource, long, long)
*/
public void DataSourceValueChanged(DataSource ds, long minIndex, long maxIndex) {
// Only care if it happens in our range
if ((minIndex>=this.minIndex) && (maxIndex<=this.maxIndex)){
minmaxOk=false;
notifyListenersForValueChange(minIndex, maxIndex);
notifyEndNotificationListeners();
}
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceIndexRangeChanged(simtools.data.DataSource, long, long)
*/
public void DataSourceIndexRangeChanged(DataSource ds, long startIndex, long lastIndex) {
// One of the sources changed its definition interval => recompute the intersection
long min = startIndex;
long max = lastIndex;
for (Iterator it = variables.iterator(); it.hasNext();) {
DataSource dsv = ((VariableAssociation)it.next()).ds;
if (dsv==ds) continue; // Same source, and do not need to use equals() here
try {
min = Math.max(min, dsv.getStartIndex());
max = Math.min(max, dsv.getLastIndex());
} catch (UnsupportedOperation e) {
}
}
// Update our range
minIndex = min; maxIndex = max;
minmaxOk=false;
notifyListenersForIndexRangeChange(minIndex,maxIndex);
notifyEndNotificationListeners();
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceInfoChanged(simtools.data.DataSource, simtools.data.DataInfo)
*/
public void DataSourceInfoChanged(DataSource ds, DataInfo newInfo) {
// Don't care
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceValueRangeChanged(simtools.data.DataSource)
*/
public void DataSourceValueRangeChanged(DataSource ds) {
// Don't care
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceOrderChanged(simtools.data.DataSource, int)
*/
public void DataSourceOrderChanged(DataSource ds, int newOrder) {
// Don't care
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceReplaced(simtools.data.DataSource, simtools.data.DataSource)
*/
public void DataSourceReplaced(DataSource oldData, DataSource newData) {
for (Iterator it = variables.iterator(); it.hasNext();) {
Object o = it.next();
if (!(o instanceof VariableAssociation)) {
it.remove();
continue;
}
DataSource ds = ((VariableAssociation)o).ds;
if(ds==newData){
((VariableAssociation)o).ds=newData;
try {
minIndex = Math.max(minIndex, newData.getStartIndex());
maxIndex = Math.min(maxIndex, newData.getLastIndex());
} catch (UnsupportedOperation e) {
}
newData.addListener(this);
}
}
}
/* (non-Javadoc)
* @see simtools.data.DataSource#computeLastIndex()
*/
public long computeLastIndex() throws UnsupportedOperation {
// If no data source is used, the index range is irrelevant
long max = Long.MAX_VALUE;
for (Iterator it = variables.iterator(); it.hasNext();) {
DataSource ds = ((VariableAssociation)it.next()).ds;
max = Math.min(max, ds.computeLastIndex());
}
maxIndex = max;
if (maxIndex == Long.MAX_VALUE) maxIndex = 0;
return maxIndex;
}
/* (non-Javadoc)
* @see simtools.data.DataSource#computeStartIndex()
*/
public long computeStartIndex() throws UnsupportedOperation {
// If no data source is used, the index range is irrelevant
long min = Long.MIN_VALUE;
for (Iterator it = variables.iterator(); it.hasNext();) {
DataSource ds = ((VariableAssociation)it.next()).ds;
min = Math.max(min, ds.computeStartIndex());
}
minIndex = min;
if (minIndex == Long.MIN_VALUE) minIndex = 0;
return minIndex;
}
/* (non-Javadoc)
* @see simtools.data.DataSource#getLastIndex()
*/
public long getLastIndex() throws UnsupportedOperation {
return maxIndex;
}
/* (non-Javadoc)
* @see simtools.data.DataSource#getStartIndex()
*/
public long getStartIndex() throws UnsupportedOperation {
return minIndex;
}
/* (non-Javadoc)
* @see simtools.data.DataSource#isComparable()
*/
public boolean isComparable() {
return true;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString() {
return info.label + " = " + info.comment;
}
/* (non-Javadoc)
* @see simtools.data.DataSource#getSourceDependencies()
*/
public DataSource[] getSourceDependencies() {
// Make it so that the variables will be serialized before this data source
// And also that they will exist for the parsing at reload (see ExpressionDataSourceProvider)
int len = variables.size();
if (len<=0) return null;
DataSource[] ret = new DataSource[len];
int i=0;
for (Iterator it = variables.iterator(); it.hasNext();) {
ret[i++] = ((VariableAssociation)it.next()).ds;
}
return ret;
}
/* (non-Javadoc)
* @see simtools.data.DataSource#getDataSourceInformationClass()
*/
public String getDataSourceInformationClass(){
return "simtools.ui.DynamicDataSourceInformation";
}
}