/* ==============================================
* Simtools : The tools library used in JSynoptic
* ==============================================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* 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.
*
* (C) Copyright 2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Nicolas Brodu
*
* $Id: DataSourceAnimator.java,v 1.4 2006/02/15 18:29:07 cazenave Exp $
*
* Changes
* -------
* 21-Jan-2004: made initial version from collection animator (NB)
*
*/
package simtools.data;
import java.util.Timer;
import java.util.TimerTask;
/**
* This class adds dynamical features to a DataSource. This is useful to replay time series,
* for example.
* It will use the underlying source to provide the values, and has its own API to control
* how to advance the index : step by step, or with a timer.
*
* Note: All APIs from superclasses are redirected. So, this wrapper should really be transparent.
*
* @author Nicolas Brodu
*/
public class DataSourceAnimator extends DynamicDataSource implements DataSourceListener {
static public final String MARKER = "AnimatedSource:";
protected DataSource target;
protected long period = 1000;
protected Timer timer;
protected boolean finished;
protected boolean autoStop;
/**
* @return true iff the underlying data source has no more values
*/
public boolean isFinished() {
return finished;
}
public DataSourceAnimator(DataSource ds) {
this(ds,0);
}
public DataSourceAnimator(DataSource ds, int delay) {
this(ds,delay,1000);
}
public DataSourceAnimator(DataSource ds, int delay, long period) {
super(ds.getInformation(), ds.getKind(), delay);
this.period = period;
if (ds instanceof DataSourceAnimator) target = ((DataSourceAnimator)ds).target;
target = ds;
info.id = MARKER + info.id;
this.autoRegister = false; // let's control things more finely
target.addListener(this);
}
/** Increase current index */
public void step() throws DataException {
try {
switch (kind) {
/// case ValueProvider.TypeProvider: super.setTypeValue(target.getTypeValue(lastIndex+1));
// ------ START of perl-generated corresponding code --------
case ValueProvider.ByteProvider: super.setByteValue(target.getByteValue(lastIndex+1));
case ValueProvider.ShortProvider: super.setShortValue(target.getShortValue(lastIndex+1));
case ValueProvider.IntegerProvider: super.setIntegerValue(target.getIntegerValue(lastIndex+1));
case ValueProvider.LongProvider: super.setLongValue(target.getLongValue(lastIndex+1));
case ValueProvider.FloatProvider: super.setFloatValue(target.getFloatValue(lastIndex+1));
case ValueProvider.DoubleProvider: super.setDoubleValue(target.getDoubleValue(lastIndex+1));
// -------- END of perl-generated corresponding code --------
case ValueProvider.ObjectProvider: super.setObjectValue(target.getValue(lastIndex+1));
}
finished = false;
} catch (DataException de) {
if (autoStop && (de instanceof NoSuchIndex)) {
finished=true;
}
throw de;
}
super.registerNewValue();
}
/**
* @return The current period, in milliseconds
*/
public long getPeriod() {
return period;
}
/** Sets a period for cyclic execution
* @param period, in milliseconds
*/
public void setPeriod(long period) {
if (isRunning()) {
stop();
this.period = period;
start();
}
else this.period = period;
}
/**
* @return true is the values are beeing updated cyclically.
*/
public boolean isRunning() {
return timer != null;
}
/**
* Starts to update the values with the given period.
*/
public void start(long period) {
if (isRunning()) return;
this.period=period;
start();
}
/**
* Starts to update the values with the previously set period. Default is 1 second.
*/
public void start() {
if (isRunning()) return;
timer = new Timer(true); // do not block the application on exit
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
try {
step();
} catch (DataException e) {
if (autoStop && (e instanceof NoSuchIndex)) {
stop(); // Exits gracefully when there is no more data
}
} catch(Exception e){
// need some feed back in case of error
// TimerTask/finally hide exceptions
e.printStackTrace();
stop();
}
}
}
,0,period);
}
/**
* Stop to update the values. This method is automatically called when there is no more
* data in the underlying data source, if this option is chosen.
*/
public void stop() {
if (!isRunning()) return;
timer.cancel();
timer = null;
}
/**
* Resets the source. Next call to step will show the first source element.
*/
public void reset() {
synchronized (this) {
if (buffer!=null) buffer.clear();
this.lastIndex = -1;
finished = false;
}
notifyListenersForIndexRangeChange(getStartIndex(), getLastIndex());
}
/**
* @return Returns the autoStop.
*/
public boolean isAutoStop() {
return autoStop;
}
/**
* @param autoStop The autoStop to set.
*/
public void setAutoStop(boolean autoStop) {
this.autoStop = autoStop;
}
// We listen to our target
// And propagate the changes together with our own listeners
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceValueChanged(simtools.data.DataSource, long, long)
*/
public void DataSourceValueChanged(DataSource ds, long minIndex, long maxIndex) {
if (buffer==null) {
if ((lastIndex>=minIndex) && (lastIndex <= maxIndex))
this.notifyListenersForValueChange(lastIndex, lastIndex);
} else {
long idxmin = Math.max(minIndex, buffer.getStartIndex());
long idxmax = Math.min(maxIndex, buffer.getEndIndex());
if (idxmin<=idxmax) // intersection not empty
this.notifyListenersForValueChange(idxmin, idxmax);
}
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceIndexRangeChanged(simtools.data.DataSource, long, long)
*/
public void DataSourceIndexRangeChanged(DataSource ds, long startIndex, long lastIndex) {
if (buffer==null) {
if ((this.lastIndex>=startIndex) && (this.lastIndex <= lastIndex))
this.notifyListenersForIndexRangeChange(this.lastIndex, this.lastIndex);
} else {
long idxmin = Math.max(startIndex, buffer.getStartIndex());
long idxmax = Math.min(lastIndex, buffer.getEndIndex());
if (idxmin<=idxmax) // intersection not empty
this.notifyListenersForIndexRangeChange(idxmin, idxmax);
}
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceInfoChanged(simtools.data.DataSource, simtools.data.DataInfo)
*/
public void DataSourceInfoChanged(DataSource ds, DataInfo newInfo) {
info = newInfo;
notifyListenersForInfoChange(newInfo);
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceValueRangeChanged(simtools.data.DataSource)
*/
public void DataSourceValueRangeChanged(DataSource ds) {
this.notifyListenersForValueRangeChange();
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceOrderChanged(simtools.data.DataSource, int)
*/
public void DataSourceOrderChanged(DataSource ds, int newOrder) {
this.notifyListenersForOrderChange(newOrder);
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceReplaced(simtools.data.DataSource, simtools.data.DataSource)
*/
public void DataSourceReplaced(DataSource oldData, DataSource newData) {
this.notifyListenersForDataReplaced(oldData,newData);
}
// Delegate methods
/* (non-Javadoc)
* @see simtools.data.DataSource#sortedOrder()
*/
public int sortedOrder() {
return target.sortedOrder();
}
/* (non-Javadoc)
* @see simtools.data.DynamicDataSource#valueClass()
*/
public Class valueClass() {
return target.valueClass();
}
}