/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.integration.tool.portfolio.xml;
import java.util.Set;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.opengamma.util.ArgumentChecker;
/**
* Manages the relationship between trades and positions for a position load process.
* The resolver is initially populated with a set of trade ids. Position trade
* relationships can then be added and when complete the {@link #resolve} method called.
* This freezes the state of the resolver (no more relationships can be added), and
* the following are calculated:
* <ul>
* <li>positions with their associated trades</li>
* <li>trades from the trade population with no associated position</li>
* <li>trades in positions which were not in the original trade set</li>
* <li>trades that appear in multiple positions</li>
* </ul>
* These can be examined by the client and errors raised as appropriate.
*/
public class TradePositionResolver {
/**
* The set of trade ids which are expected to be handled. Attempts to create
* a position where the trade is unknown will be flagged as errors.
*/
private final Set<String> _tradeIds;
/**
* The builder used to construct the {@link #_positions} multimap once the
* {@link #resolve()} method has been called. The builder is used in
* preference to a mutable map due to the convenience of using the
* {@link ImmutableMultimap#inverse()} method when constructing the
* {@link #_invertedPositions}.
*
*/
private final ImmutableMultimap.Builder<String, String> _positionBuilder = ImmutableMultimap.builder();
/**
* Multimap of positions -> trades. It will be quite usual to have a position
* containing multiple trades. This map is not populated until the
* {@link #resolve()} method has been called.
*/
private ImmutableMultimap<String, String> _positions;
/**
* The inverse of the position map, so containing trades -> positions. Generally we
* are not expecting a trade to be in more than one position but it may be possible
* in the future. This map is not populated until the {@link #resolve()} method has
* been called.
*/
private ImmutableMultimap<String, String> _invertedPositions;
/**
* The set of trades which were in the initial set of trade ids but were not
* referenced in any of the positions. This Iterable is not populated until the
* {@link #resolve()} method has been called.
*/
private Iterable<String> _orphanTrades;
/**
* Multimap of trades -> positions indicating trades which appear in more than
* one position (along with the positions they appear in). It is for the client to
* determine whether this is acceptable or not, and how to resolve. This map is not
* populated until the {@link #resolve()} method has been called.
*/
private ImmutableMultimap<String, String> _duplicateTrades;
/**
* The set of trades which were referenced by positions but were not in the initial
* set of trade ids. It is for the client to determine if this is an error and how
* to resolve. This Iterable is not populated until the {@link #resolve()} method has
* been called.
*/
private Iterable<String> _unknownTrades;
private ImmutableMultimap<String, String> _portfolioPositions;
private ImmutableMultimap<String, String> _portfolioTrades;
private ImmutableMultimap<String, String> _portfolioPortfolios;
/**
* Maintains the state of the resolver. Positions can only be added whilst we are
* in the unresolved state, accessor methods can only be called when we are in the
* resolved state.
*/
private boolean _isResolved;
/**
* Constructor, taken in the set of known trade ids. Trade ids which are referenced
* by position but are not in the initial set can be determined from the
* {@link #getUnknownTrades()} method.
*
* @param tradeIds the known set of trade ids
*/
public TradePositionResolver(Set<String> tradeIds) {
ArgumentChecker.notNull(tradeIds, "_tradeIds");
_tradeIds = tradeIds;
}
/**
* Returns a Multimap of positions -> trades built from the positions added via the
* {@link #addToPosition(String, String)} method. It will be quite usual to have a position
* containing multiple trades. Note that this map is not populated until the
* {@link #resolve()} method has been called. Any attempt to access before that point will
* result in an IllegalStateException being thrown.
*
* @return Multimap of position -> trades
* @throws IllegalStateException if this method is called before the {@link #resolve()} method
*/
public Multimap<String, String> getPositions() {
checkResolved();
return _positions;
}
/**
* Returns the set of trades which were in the initial set of trade ids but which have not
* been referenced by any of the positions. Note that this Iterable is not populated until
* the {@link #resolve()} method has been called. Any attempt to access before that point will
* result in an IllegalStateException being thrown.
*
* @return the set of orphan trades
* @throws IllegalStateException if this method is called before the {@link #resolve()} method
*/
public Iterable<String> getOrphans() {
checkResolved();
return _orphanTrades;
}
/**
* Returns a Multimap of trades -> positions indicating trades which appear in more than
* one position (along with the positions they appear in). It is for the client to
* determine whether this is acceptable or not, and how to resolve. Note that this map
* is not populated until the {@link #resolve()} method has been called.
*
* @return Multimap of trade -> positions
* @throws IllegalStateException if this method is called before the {@link #resolve()} method
*/
public Multimap<String, String> getDuplicateTrades() {
checkResolved();
return _duplicateTrades;
}
/**
* Returns the set of trades which were referenced by positions but were not in the initial
* set of trade ids. It is for the client to determine if this is an error and how to resolve.
* Note that this Iterable is not populated until the {@link #resolve()} method has been called.
*
* @return the set of unknown trades
* @throws IllegalStateException if this method is called before the {@link #resolve()} method
*/
public Iterable<String> getUnknownTrades() {
checkResolved();
return _unknownTrades;
}
/**
* Record an association between the trade and position. Note that duplicate
* position / trade combinations are accepted and therefore the client should
* ensure uniqueness before calling, if that is what is required. Note that this
* method cannot be called after the {@link #resolve()} method has been called.
*
* @param positionId the position to be added to
* @param tradeId the trade to be added to the position
* @throws IllegalStateException if this method is called after the {@link #resolve()} method
*/
public void addToPosition(String positionId, String tradeId) {
if (_isResolved) {
throw new IllegalStateException("Cannot add position data as resolve() method has been called.");
}
_positionBuilder.put(positionId, tradeId);
}
public void addPositionToPortfolio(String portfolioId, String positionId) {
}
public void addTradeToPortfolio(String portfolioId, String tradeId) {
}
/**
* Mark the resolved as resolved and perform the calculations required. After calling
* this method, calls to the accessor methods are enabled and calls to
* {@link #addToPosition(String, String)} will be disallowed.
*/
public void resolve() {
if (!_isResolved) {
_isResolved = true;
_positions = _positionBuilder.build();
_invertedPositions = _positions.inverse();
_orphanTrades = determineOrphanTrades();
_duplicateTrades = ImmutableMultimap.copyOf(determineDuplicatedTrades());
_unknownTrades = determineUnknownTrades();
}
}
private void checkResolved() {
if (!_isResolved) {
throw new IllegalStateException("Cannot access resolved data until resolve() method is called.");
}
}
private Iterable<String> determineOrphanTrades() {
return ImmutableSet.copyOf(Iterables.filter(_tradeIds,
new Predicate<String>() {
@Override
public boolean apply(String tradeId) {
return !_invertedPositions.containsKey(tradeId);
}
}
));
}
private Multimap<String, String> determineDuplicatedTrades() {
return Multimaps.filterKeys(_invertedPositions,
new Predicate<String>() {
@Override
public boolean apply(String s) {
return _invertedPositions.get(s).size() > 1;
}
}
);
}
private Iterable<String> determineUnknownTrades() {
return Iterables.filter(_invertedPositions.keySet(),
new Predicate<String>() {
@Override
public boolean apply(String s) {
return !_tradeIds.contains(s);
}
}
);
}
}