/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.web.analytics;
import java.util.Collections;
import java.util.List;
import com.google.common.collect.Lists;
import com.opengamma.core.position.Portfolio;
import com.opengamma.core.position.PortfolioNode;
import com.opengamma.core.position.Position;
import com.opengamma.core.security.Security;
import com.opengamma.financial.security.FinancialSecurity;
import com.opengamma.util.ArgumentChecker;
/**
* Minimal representation of a node in the structure of a portfolio. For use in an analytics grid. Contains a
* list of sub-nodes and indices of the rows at which a node starts and ends.
*/
/* package */ class AnalyticsNode {
/** Index of the row containing this node. */
private final int _startRow;
/** Index of the row containing this node's last child. */
private final int _endRow;
/** Immediate child nodes. */
private final List<AnalyticsNode> _children;
/** Whether this node represents a position in a fungible security, i.e. it has child nodes which are trades. */
private final boolean _collapsed;
/* package */ AnalyticsNode(int startRow, int endRow, List<AnalyticsNode> children, boolean collapsed) {
ArgumentChecker.notNull(children, "children");
_collapsed = collapsed;
_startRow = startRow;
_endRow = endRow;
_children = children;
}
/**
* Factory method that creates a node structure from a portfolio and returns the root node.
* @param portfolio The portfolio
* @return The root node of the portfolio's node structure, null if the portfolio is null
*/
/* package */ static AnalyticsNode portfolioRoot(Portfolio portfolio) {
if (portfolio == null) {
return null;
}
PortfolioNode root = portfolio.getRootNode();
return new PortfolioNodeBuilder(root).getRoot();
}
/**
* @return The row index (zero-based and inclusive) at which the node starts.
*/
/* package */ int getStartRow() {
return _startRow;
}
/**
* @return The row index (zero-based and inclusive) at which the node ends. This includes all nested child nodes.
* i.e. the end row of a node is the same as the end row of its most deeply nested child.
*/
/* package */ int getEndRow() {
return _endRow;
}
/**
* @return The direct children of this node.
*/
/* package */ List<AnalyticsNode> getChildren() {
return _children;
}
/**
* @return Whether this node is collapsed
*/
public boolean isCollapsed() {
return _collapsed;
}
@Override
public String toString() {
return "AnalyticsNode [_startRow=" + _startRow + ", _endRow=" + _endRow + ", _children=" + _children + "]";
}
/**
* Mutable builder that creates the node structure for a portfolio and returns the root node. Package-scoped for
* testing.
*/
/* package */ static final class PortfolioNodeBuilder {
/** The root node of the portfolio. */
private final AnalyticsNode _root;
/** Index of last row, updated as the structure is built. */
private int _lastRow;
/* package */ PortfolioNodeBuilder(PortfolioNode root) {
_root = createPortfolioNode(root, 0);
_lastRow = 0;
}
private AnalyticsNode createPortfolioNode(PortfolioNode node, int level) {
int nodeStart = _lastRow;
List<AnalyticsNode> nodes = Lists.newArrayList();
for (Position position : node.getPositions()) {
++_lastRow;
if (position.getTrades().size() > 0 && isFungible(position.getSecurity())) {
nodes.add(createFungiblePositionNode(position));
}
}
for (PortfolioNode child : node.getChildNodes()) {
++_lastRow;
nodes.add(createPortfolioNode(child, level + 1));
}
// leave root and first-level children expanded
boolean collapse = level >= 2;
return new AnalyticsNode(nodeStart, _lastRow, Collections.unmodifiableList(nodes), collapse);
}
private AnalyticsNode createFungiblePositionNode(Position position) {
int nodeStart = _lastRow;
_lastRow += position.getTrades().size();
return new AnalyticsNode(nodeStart, _lastRow, Collections.<AnalyticsNode>emptyList(), true);
}
/**
* @param security A security
* @return true if the security is fungible, false if OTC
*/
private static boolean isFungible(Security security) {
if (security instanceof FinancialSecurity) {
Boolean isOTC = ((FinancialSecurity) security).accept(new OtcSecurityVisitor());
if (isOTC == null) {
return false;
}
return !isOTC;
} else {
return false;
}
}
/* package */ AnalyticsNode getRoot() {
return _root;
}
}
}