/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.timeseries;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
/**
* A simple time-series implementation based on a map.
* <p>
* This class is mostly useful to understand the basic time-series API.
*
* @param <T> the date-time type, such as {@code Instant} or {@code LocalDate}
* @param <V> the value being viewed over time, such as {@code Double}
*/
public class SimpleMapTimeSeries<T, V> implements TimeSeries<T, V>, Serializable {
/** Serialization version. */
private static final long serialVersionUID = 1L;
/**
* The map of data points.
*/
private final NavigableMap<T, V> _map;
/**
* The indexed data points.
*/
private final T[] _times;
/**
* The indexed data points.
*/
private final V[] _values;
/**
* Creates an empty instance.
*
* @param dateTimeType the date-time type, not null
* @param valueType the value type, not null
*/
@SuppressWarnings("unchecked")
public SimpleMapTimeSeries(Class<T> dateTimeType, Class<V> valueType) {
TimeSeriesUtils.notNull(dateTimeType, "dateTimeType");
TimeSeriesUtils.notNull(valueType, "valueType");
_map = new TreeMap<T, V>();
_times = (T[]) Array.newInstance(dateTimeType, 0);
_values = (V[]) Array.newInstance(valueType, 0);
}
/**
* Creates an instance.
*
* @param dateTimes the date-times, not null
* @param values the values, not null
*/
public SimpleMapTimeSeries(T[] dateTimes, V[] values) {
TimeSeriesUtils.notNull(dateTimes, "dateTimes");
TimeSeriesUtils.notNull(values, "values");
TimeSeriesUtils.isTrue(dateTimes.length == values.length, "Arrays must be same length");
NavigableMap<T, V> newMap = new TreeMap<T, V>();
for (int i = 0; i < dateTimes.length; i++) {
newMap.put(dateTimes[i], values[i]);
}
_map = newMap;
_times = dateTimes.clone();
_values = values.clone();
}
/**
* Creates an instance (for private use only).
*
* @param map the map to assign, not null
* @param oldDateTimes the old date-time array, not null
* @param oldValues the old value array, not null
*/
@SuppressWarnings("unchecked")
private SimpleMapTimeSeries(NavigableMap<T, V> map, T[] oldDateTimes, V[] oldValues) {
TimeSeriesUtils.notNull(map, "map");
TimeSeriesUtils.notNull(oldDateTimes, "oldDateTimes");
TimeSeriesUtils.notNull(oldValues, "oldValues");
_map = map;
_times = (T[]) Array.newInstance(oldDateTimes.getClass().getComponentType(), map.size());
_values = (V[]) Array.newInstance(oldValues.getClass().getComponentType(), map.size());
int i = 0;
for (T oldDateTime : oldDateTimes) {
if (map.containsKey(oldDateTime)) {
_times[i] = oldDateTime;
_values[i++] = map.get(oldDateTime);
}
}
}
//-------------------------------------------------------------------------
@Override
public int size() {
return _map.size();
}
@Override
public boolean isEmpty() {
return _map.isEmpty();
}
@Override
public boolean containsTime(T dateTime) {
return _map.containsKey(dateTime);
}
@Override
public V getValue(T dateTime) {
return _map.get(dateTime);
}
@Override
public T getTimeAtIndex(int index) {
return _times[index];
}
@Override
public V getValueAtIndex(int index) {
return _values[index];
}
@Override
public T getLatestTime() {
return _map.lastKey();
}
@Override
public V getLatestValue() {
return _map.get(_map.lastKey());
}
@Override
public T getEarliestTime() {
return _map.firstKey();
}
@Override
public V getEarliestValue() {
return _map.get(_map.firstKey());
}
@Override
public Iterator<Entry<T, V>> iterator() {
return _map.entrySet().iterator();
}
@Override
public Iterator<T> timesIterator() {
return _map.keySet().iterator();
}
@Override
public Iterator<V> valuesIterator() {
return _map.values().iterator();
}
@Override
public TimeSeries<T, V> subSeries(T startTime, boolean includeStart, T endTime, boolean includeEnd) {
return new SimpleMapTimeSeries<T, V>(_map.subMap(startTime, includeStart, endTime, includeEnd), _times, _values);
}
@Override
public TimeSeries<T, V> subSeries(T startTimeInclusive, T endTimeExclusive) {
return new SimpleMapTimeSeries<T, V>((NavigableMap<T, V>) _map.subMap(startTimeInclusive, endTimeExclusive), _times, _values);
}
@Override
public TimeSeries<T, V> head(int numItems) {
T element = getTimeAtIndex(numItems);
return new SimpleMapTimeSeries<T, V>(_map.headMap(element, true), _times, _values);
}
@Override
public TimeSeries<T, V> tail(int numItems) {
T element = getTimeAtIndex(size() - numItems);
return new SimpleMapTimeSeries<T, V>(_map.tailMap(element, true), _times, _values);
}
@Override
public TimeSeries<T, V> lag(int lagCount) {
if (lagCount == 0) {
return this;
} else {
NavigableMap<T, V> newMap = new TreeMap<T, V>();
Iterator<T> times = timesIterator();
Iterator<V> values = valuesIterator();
if (lagCount > 0) {
if (lagCount < _times.length) {
for (int i = 0; i < lagCount; i++) {
times.next();
}
while (times.hasNext()) {
newMap.put(times.next(), values.next());
}
}
} else {
if (-lagCount < _times.length) {
for (int i = lagCount; i < 0; i++) {
values.next();
}
while (values.hasNext()) {
newMap.put(times.next(), values.next());
}
}
}
return new SimpleMapTimeSeries<T, V>(newMap, _times, _values);
}
}
@Override
public List<T> times() {
return new ArrayList<T>(Arrays.asList(_times));
}
@Override
public T[] timesArray() {
return _times.clone();
}
@Override
public List<V> values() {
return new ArrayList<V>(Arrays.asList(_values));
}
@Override
public V[] valuesArray() {
return _values.clone();
}
@Override
public TimeSeries<T, V> newInstance(T[] dateTimes, V[] values) {
return new SimpleMapTimeSeries<T, V>(dateTimes, values);
}
//-------------------------------------------------------------------------
@Override
public String toString() {
return TimeSeriesUtils.toString(this);
}
}