/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.timeseries;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import org.fudgemsg.FudgeField;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.MutableFudgeMsg;
import org.fudgemsg.mapping.FudgeDeserializer;
import org.fudgemsg.mapping.FudgeSerializer;
import com.google.common.collect.Iterators;
import com.opengamma.core.historicaltimeseries.HistoricalTimeSeries;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.function.Function;
import com.opengamma.util.tuple.Triple;
/**
* A collection of historical time-series objects. The time-series are keyed by the originally requested data field
* name and the external ids associated with them. The original field is not generally necessary but is to allow
* override operations to identify the nature of the data points in the time series they are being applied to. The
* time-series are maintained in the order in which they were added, for example tenor order, and are iterable in this
* order.
*/
public final class HistoricalTimeSeriesBundle {
private static final class Entry implements Iterable<HistoricalTimeSeries> {
// Maintain insertion order, so that the bundle can be ordered e.g. by tenor
private final Map<ExternalIdBundle, HistoricalTimeSeries> _timeSeries = new LinkedHashMap<ExternalIdBundle, HistoricalTimeSeries>();
private Map<ExternalId, HistoricalTimeSeries> _lookup;
private HistoricalTimeSeries getImpl(final ExternalId id) {
return _lookup.get(id);
}
private void addImpl(final ExternalId id, final HistoricalTimeSeries hts) {
_lookup.put(id, hts);
}
private void addImpl(final ExternalIdBundle ids, final HistoricalTimeSeries hts) {
for (ExternalId id : ids) {
addImpl(id, hts);
}
}
/*
* Added synchronized here because the hashmap was getting corrupted by concurrent access.
*/
private synchronized void lookup() {
if (_lookup != null) {
return;
}
_lookup = new HashMap<ExternalId, HistoricalTimeSeries>();
for (Map.Entry<ExternalIdBundle, HistoricalTimeSeries> e : _timeSeries.entrySet()) {
addImpl(e.getKey(), e.getValue());
}
}
public HistoricalTimeSeries get(final ExternalIdBundle bundle) {
lookup();
for (ExternalId id : bundle) {
final HistoricalTimeSeries hts = getImpl(id);
if (hts != null) {
return hts;
}
}
return null;
}
public HistoricalTimeSeries get(final ExternalId id) {
lookup();
return getImpl(id);
}
/*
* Added synchronized here because the hashmap was getting corrupted by concurrent access.
*/
public synchronized void add(final ExternalIdBundle bundle, final HistoricalTimeSeries timeSeries) {
_timeSeries.put(bundle, timeSeries);
if (_lookup != null) {
addImpl(bundle, timeSeries);
}
}
public int size() {
return _timeSeries.size();
}
@Override
public Iterator<HistoricalTimeSeries> iterator() {
return _timeSeries.values().iterator();
}
public MutableFudgeMsg toFudgeMsg(final FudgeSerializer context) {
final MutableFudgeMsg msg = context.newMessage();
for (Map.Entry<ExternalIdBundle, HistoricalTimeSeries> data : _timeSeries.entrySet()) {
context.addToMessageWithClassHeaders(msg, null, 1, data.getKey(), ExternalIdBundle.class);
context.addToMessageWithClassHeaders(msg, null, 2, data.getValue(), HistoricalTimeSeries.class);
}
return msg;
}
public static Entry fromFudgeMsg(final FudgeDeserializer context, final FudgeMsg msg) {
final Entry e = new Entry();
final Iterator<FudgeField> keys = msg.getAllByOrdinal(1).iterator();
final Iterator<FudgeField> values = msg.getAllByOrdinal(2).iterator();
while (keys.hasNext() && values.hasNext()) {
final FudgeField key = keys.next();
final FudgeField value = values.next();
e.add(context.fieldValueToObject(ExternalIdBundle.class, key), context.fieldValueToObject(HistoricalTimeSeries.class, value));
}
return e;
}
@Override
public String toString() {
return _timeSeries.keySet().toString();
}
}
private final Map<String, Entry> _data = new HashMap<String, Entry>();
public void add(final String field, final ExternalIdBundle idBundle, final HistoricalTimeSeries timeSeries) {
ArgumentChecker.notNull(field, "field");
ArgumentChecker.notNull(idBundle, "idBundle");
ArgumentChecker.notNull(timeSeries, "timeSeries");
Entry e = _data.get(field);
if (e == null) {
e = new Entry();
_data.put(field, e);
}
e.add(idBundle, timeSeries);
}
public HistoricalTimeSeries get(final String field, final ExternalId id) {
final Entry e = _data.get(field);
if (e == null) {
return null;
}
return e.get(id);
}
public HistoricalTimeSeries get(final String field, final ExternalIdBundle ids) {
final Entry e = _data.get(field);
if (e == null) {
return null;
}
return e.get(ids);
}
public int size(final String field) {
ArgumentChecker.notNull(field, "field");
final Entry e = _data.get(field);
if (e == null) {
return 0;
}
return e.size();
}
/**
* Gets an iterator for the time-series stored under the given field name. This iterates over the time-series in the
* same order as they were added.
*
* @param field the data field, not null
* @return an iterator, not null
*/
public Iterator<HistoricalTimeSeries> iterator(final String field) {
ArgumentChecker.notNull(field, "field");
final Entry e = _data.get(field);
if (e == null) {
return Iterators.emptyIterator();
}
return e.iterator();
}
protected HistoricalTimeSeriesBundle apply(final Function<Triple<String, ExternalIdBundle, HistoricalTimeSeries>, HistoricalTimeSeries> function) {
final HistoricalTimeSeriesBundle result = new HistoricalTimeSeriesBundle();
for (Map.Entry<String, Entry> fieldTimeSeries : _data.entrySet()) {
final Entry newEntry = new Entry();
for (Map.Entry<ExternalIdBundle, HistoricalTimeSeries> timeSeries : fieldTimeSeries.getValue()._timeSeries.entrySet()) {
newEntry._timeSeries.put(timeSeries.getKey(), function.apply(
Triple.of(fieldTimeSeries.getKey(), timeSeries.getKey(), timeSeries.getValue())));
}
result._data.put(fieldTimeSeries.getKey(), newEntry);
}
return result;
}
public MutableFudgeMsg toFudgeMsg(final FudgeSerializer context) {
final MutableFudgeMsg msg = context.newMessage();
for (Map.Entry<String, Entry> entry : _data.entrySet()) {
msg.add(entry.getKey(), entry.getValue().toFudgeMsg(context));
}
return msg;
}
public static HistoricalTimeSeriesBundle fromFudgeMsg(final FudgeDeserializer context, final FudgeMsg msg) {
final HistoricalTimeSeriesBundle bundle = new HistoricalTimeSeriesBundle();
for (FudgeField field : msg) {
if (field.getValue() instanceof FudgeMsg) {
bundle._data.put(field.getName(), Entry.fromFudgeMsg(context, (FudgeMsg) field.getValue()));
}
}
return bundle;
}
@Override
public String toString() {
return "HistoricalTimeSeriesBundle" + _data;
}
}