/*******************************************************************************
* Copyright (c) 2013 Luigi Sgro. 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
*
* Contributors:
* Luigi Sgro - initial API and implementation
******************************************************************************/
package com.quantcomponents.chart.series;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import com.quantcomponents.core.calendar.CalendarTradingSchedule;
import com.quantcomponents.core.calendar.ITradingCalendar;
import com.quantcomponents.core.calendar.ITradingSchedule;
import com.quantcomponents.core.model.ISeries;
import com.quantcomponents.core.model.ISeriesListener;
import com.quantcomponents.core.model.ISeriesOperator;
import com.quantcomponents.core.model.ISeriesPoint;
import com.quantcomponents.marketdata.TimeSeriesBetween;
import com.quantcomponents.marketdata.TimeSeriesSince;
import com.quantcomponents.marketdata.TimeSeriesTail;
/**
* Implementation of {@link ITimeSeriesChartModel} for double charts indexed by {@link java.util.Date}
*
* @param <P> the type of the data points
*/
public class TimeSeriesChartModel<P extends ISeriesPoint<Date, Double>> implements ITimeSeriesChartModel<P>, ISeriesListener<Date, Double> {
private static final int DEFAULT_NUM_POINTS = 200;
private final ISeries<Date, Double, P> timeSeries;
private final Set<ITimeSeriesChartModelListener<P>> listeners = new CopyOnWriteArraySet<ITimeSeriesChartModelListener<P>>();
private ITradingCalendar tradingCalendar;
private ITradingSchedule tradingSchedule;
private ISeriesOperator<Date, Double, P> currentOperator;
private ISeries<Date, Double, P> data;
private boolean movingWindow;
private volatile boolean suspendUpdates;
public TimeSeriesChartModel(ISeries<Date, Double, P> timeSeries, ITradingCalendar tradingCalendar) {
setTradingCalendar(tradingCalendar);
this.timeSeries = timeSeries;
timeSeries.addSeriesListener(this);
currentOperator = new TimeSeriesTail<P>(tradingSchedule, DEFAULT_NUM_POINTS);
}
@Override
public void onItemUpdated(ISeriesPoint<Date, Double> existingItem, ISeriesPoint<Date, Double> updatedItem) {
if (!suspendUpdates) {
recalculateAndNotify();
}
}
@Override
public void onItemAdded(ISeriesPoint<Date, Double> newItem) {
if (!suspendUpdates) {
recalculateAndNotify();
}
}
public void dispose() {
timeSeries.removeSeriesListener(this);
}
@Override
public synchronized void setFixedWindow(Date startDate, Date endDate, ITradingCalendar tradingCalendar) {
setTradingCalendar(tradingCalendar);
currentOperator = new TimeSeriesBetween<P>(tradingSchedule, startDate, endDate);
movingWindow = false;
data = null;
}
@Override
public synchronized void setFixedStartWindow(Date startDate, ITradingCalendar tradingCalendar) {
setTradingCalendar(tradingCalendar);
currentOperator = new TimeSeriesSince<P>(tradingSchedule, startDate);
movingWindow = true;
data = null;
}
@Override
public synchronized void setFixedDurationWindow(Date startDate, ITradingCalendar tradingCalendar) {
setTradingCalendar(tradingCalendar);
TimeSeriesSince<P> tmpOperator = new TimeSeriesSince<P>(tradingSchedule, startDate);
ISeries<Date, Double, P> tmpWindow = tmpOperator.transform(timeSeries);
currentOperator = new TimeSeriesTail<P>(tradingSchedule, tmpWindow.size());
movingWindow = true;
data = null;
}
@Override
public synchronized void setFixedDurationWindow(int points, ITradingCalendar tradingCalendar) {
setTradingCalendar(tradingCalendar);
currentOperator = new TimeSeriesTail<P>(tradingSchedule, points);
movingWindow = true;
data = null;
}
public void setSuspendUpdates(boolean suspendUpdates) {
this.suspendUpdates = suspendUpdates;
}
@Override
public synchronized ISeries<Date, Double, P> data() {
if (data == null) {
recalculate();
}
return data;
}
@Override
public void addListener(ITimeSeriesChartModelListener<P> listener) {
listeners.add(listener);
}
@Override
public void removeListener(ITimeSeriesChartModelListener<P> listener) {
listeners.remove(listener);
}
private synchronized ISeries<Date, Double, P> recalculate() {
int oldSize = 0;
data = currentOperator.transform(timeSeries);
if (data.size() != oldSize) {
oldSize = data.size();
}
return data;
}
private void recalculateAndNotify() {
ISeries<Date, Double, P> snapshot = recalculate();
for (ITimeSeriesChartModelListener<P> listener : listeners) {
listener.onModelUpdated(snapshot);
}
}
@Override
public synchronized Date getStartDate() {
if (data != null && !data.isEmpty()) {
return data.getFirst().getIndex();
} else {
return null;
}
}
@Override
public synchronized Date getEndDate() {
if (data != null && !data.isEmpty()) {
return data.getLast().getIndex();
} else {
return null;
}
}
@Override
public synchronized boolean isMovingWindow() {
return movingWindow;
}
@Override
public ITradingCalendar getTradingCalendar() {
return tradingCalendar;
}
private void setTradingCalendar(ITradingCalendar tradingCalendar) {
this.tradingCalendar = tradingCalendar;
tradingSchedule = new CalendarTradingSchedule(tradingCalendar);
}
}