/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.integration.viewer.status.impl;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.joda.beans.BeanBuilder;
import org.joda.beans.MetaProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.opengamma.integration.viewer.status.AggregateType;
import com.opengamma.integration.viewer.status.ViewColumnType;
import com.opengamma.integration.viewer.status.ViewStatus;
import com.opengamma.integration.viewer.status.ViewStatusKey;
import com.opengamma.integration.viewer.status.ViewStatusModel;
import com.opengamma.integration.viewer.status.ViewStatusResultAggregator;
import com.opengamma.integration.viewer.status.impl.ViewStatusKeyBean.Meta;
import com.opengamma.util.ArgumentChecker;
/**
* Implementation of {@link ViewStatusResultAggregator}
*/
public class ViewStatusResultAggregatorImpl implements ViewStatusResultAggregator {
private static final Logger s_logger = LoggerFactory.getLogger(ViewStatusResultAggregatorImpl.class);
/**
* Header for Security Type
*/
public static final String SECURITY_HEADER = "SecurityType";
/**
* Header for Value Requirement Name
*/
public static final String VALUE_REQUIREMENT_NAME_HEADER = "ValueRequirementName";
/**
* Header for Currency
*/
public static final String CURRENCY_HEADER = "Currency";
/**
* Header for Target type
*/
public static final String TARGET_TYPE_HEADER = "Target Type";
/**
* Header for status
*/
public static final String STATUS = "Status";
private Map<ViewStatusKey, ViewStatus> _viewStatusResult = Maps.newConcurrentMap();
private static final Map<MetaProperty<?>, String> HEADERS = Maps.newHashMap();
static {
Meta statusKeyMeta = ViewStatusKeyBean.meta();
HEADERS.put(statusKeyMeta.securityType(), SECURITY_HEADER);
HEADERS.put(statusKeyMeta.valueRequirementName(), VALUE_REQUIREMENT_NAME_HEADER);
HEADERS.put(statusKeyMeta.currency(), CURRENCY_HEADER);
HEADERS.put(statusKeyMeta.targetType(), TARGET_TYPE_HEADER);
}
private static final String[] DEFAULT_HEADERS = {TARGET_TYPE_HEADER, SECURITY_HEADER, VALUE_REQUIREMENT_NAME_HEADER, CURRENCY_HEADER, STATUS};
private static final String EMPTY_STR = StringUtils.EMPTY;
@Override
public ViewStatusModel aggregate(AggregateType aggregateType) {
ArgumentChecker.notNull(aggregateType, "aggregateType");
if (aggregateType == AggregateType.NO_AGGREGATION) {
return defaultModel();
}
return aggregate(aggregateType.getColumnTypes());
}
private ViewStatusModel aggregate(final List<ViewColumnType> columnTypes) {
ArgumentChecker.notNull(columnTypes, "aggregations");
if (columnTypes.isEmpty()) {
return defaultModel();
}
Iterable<ViewColumnType> fixedColumnTypes = Iterables.limit(columnTypes, columnTypes.size() - 1);
final Map<List<String>, Set<String>> fixedRow2Columns = Maps.newHashMap();
for (ViewStatusKey viewStatusKey : _viewStatusResult.keySet()) {
ViewStatusKeyBean viewStatusKeyBean = new ViewStatusKeyBean(viewStatusKey.getSecurityType(), viewStatusKey.getValueRequirementName(),
viewStatusKey.getCurrency(), viewStatusKey.getTargetType());
List<String> key = viewStatusKey(viewStatusKeyBean, fixedColumnTypes);
Set<String> columnValues = fixedRow2Columns.get(key);
if (columnValues == null) {
columnValues = Sets.newHashSet();
fixedRow2Columns.put(key, columnValues);
}
columnValues.addAll(viewStatusKey(viewStatusKeyBean, Collections.singletonList(Iterables.getLast(columnTypes))));
}
Set<String> extraColumns = getExtraColumns(fixedRow2Columns);
List<List<String>> columnHeaders = makeHeaders(columnTypes, extraColumns);
List<List<Object>> rowData = createRowData(fixedRow2Columns, extraColumns, columnTypes);
return new SimpleViewStatusModel(columnHeaders, rowData, _viewStatusResult);
}
private List<List<Object>> createRowData(final Map<List<String>, Set<String>> fixedRow2Columns, final Set<String> extraColumns, List<ViewColumnType> columnTypes) {
List<List<String>> rows = Lists.newArrayList(fixedRow2Columns.keySet());
Comparator<List<String>> rowComparator = new Comparator<List<String>>() {
@Override
public int compare(List<String> left, List<String> right) {
int compare = 0;
for (int i = 0; i < left.size(); i++) {
compare = left.get(i).compareTo(right.get(i));
if (compare != 0) {
return compare;
}
}
return compare;
}
};
Collections.sort(rows, rowComparator);
List<List<Object>> processedRows = Lists.newArrayListWithCapacity(rows.size());
String[] currentRow = new String[Iterables.getFirst(rows, Lists.newArrayList()).size()];
for (List<String> row : rows) {
List<Object> processedRow = Lists.newArrayList();
Iterable<String> columns = Iterables.limit(row, row.size() - 1);
int count = 0;
for (String col : columns) {
if (currentRow[count] == null || !col.equals(currentRow[count])) {
currentRow[count] = col;
processedRow.add(col);
} else {
processedRow.add(EMPTY_STR);
}
count++;
}
processedRow.add(Iterables.getLast(row));
for (String col : extraColumns) {
List<String> keyMemebers = Lists.newArrayList(row);
keyMemebers.add(col);
ViewStatus status = getStatus(keyFromRowValues(keyMemebers, columnTypes));
if (status == null) {
processedRow.add(EMPTY_STR);
} else {
processedRow.add(status);
}
}
processedRows.add(processedRow);
}
return processedRows;
}
private ViewStatusKey keyFromRowValues(List<String> keyValues, List<ViewColumnType> columnTypes) {
Meta keyMeta = ViewStatusKeyBean.meta();
BeanBuilder<? extends ViewStatusKeyBean> beanBuilder = keyMeta.builder();
Iterator<String> keyItr = keyValues.iterator();
Iterator<ViewColumnType> colTypeItr = columnTypes.iterator();
while (keyItr.hasNext() && colTypeItr.hasNext()) {
beanBuilder.set(colTypeItr.next().getMetaProperty(), keyItr.next());
}
ViewStatusKeyBean result = beanBuilder.build();
s_logger.debug("{} built from properties: {} and types: {}", result, keyValues, columnTypes);
return result;
}
private Set<String> getExtraColumns(Map<List<String>, Set<String>> fixedRow2Columns) {
Set<String> extraColumns = Sets.newTreeSet();
Iterables.addAll(extraColumns, Iterables.concat(fixedRow2Columns.values()));
return extraColumns;
}
private List<List<String>> makeHeaders(List<ViewColumnType> columnTypes, Set<String> extraColumns) {
List<List<String>> headers = Lists.newArrayListWithCapacity(2);
int colSize = columnTypes.size() + extraColumns.size() - 1;
headers.add(topColumnHeaders(columnTypes, colSize));
headers.add(subColumnHeaders(extraColumns, colSize));
return headers;
}
private List<String> viewStatusKey(ViewStatusKeyBean viewStatusKeyBean, Iterable<ViewColumnType> fixedColumnTypes) {
List<String> result = Lists.newArrayList();
for (ViewColumnType keyType : fixedColumnTypes) {
MetaProperty<String> metaProperty = keyType.getMetaProperty();
if (ViewStatusKeyBean.meta().currency().equals(metaProperty)) {
result.add(viewStatusKeyBean.getCurrency());
} else if (ViewStatusKeyBean.meta().securityType().equals(metaProperty)) {
result.add(viewStatusKeyBean.getSecurityType());
} else if (ViewStatusKeyBean.meta().targetType().equals(metaProperty)) {
result.add(viewStatusKeyBean.getTargetType());
} else if (ViewStatusKeyBean.meta().valueRequirementName().equals(metaProperty)) {
result.add(viewStatusKeyBean.getValueRequirementName());
}
}
return ImmutableList.copyOf(result);
}
private List<String> subColumnHeaders(Set<String> extraColumnHeaders, int colsize) {
List<String> subHeader = Lists.newArrayListWithCapacity(colsize);
int emptySize = colsize - extraColumnHeaders.size();
for (int i = 0; i < emptySize; i++) {
subHeader.add(StringUtils.EMPTY);
}
Iterables.addAll(subHeader, extraColumnHeaders);
return subHeader;
}
private List<String> topColumnHeaders(List<ViewColumnType> columnTypes, int colsize) {
List<String> topHeader = Lists.newArrayListWithCapacity(colsize);
for (ViewColumnType columnType : columnTypes) {
topHeader.add(HEADERS.get(columnType.getMetaProperty()));
}
int emptySize = colsize - columnTypes.size();
for (int i = 0; i < emptySize; i++) {
topHeader.add(StringUtils.EMPTY);
}
return topHeader;
}
@Override
public void putStatus(ViewStatusKey key, ViewStatus status) {
ArgumentChecker.notNull(key, "key");
ArgumentChecker.notNull(status, "status");
_viewStatusResult.put(ImmutableViewStatusKey.of(key), status);
}
@Override
public ViewStatus getStatus(ViewStatusKey key) {
if (key == null) {
return null;
} else {
return _viewStatusResult.get(ImmutableViewStatusKey.of(key));
}
}
@Override
public Set<ViewStatusKey> keySet() {
return Sets.newHashSet(_viewStatusResult.keySet());
}
private ViewStatusModel defaultModel() {
List<List<String>> columnHeaders = Lists.newArrayListWithCapacity(1);
columnHeaders.add(Arrays.asList(DEFAULT_HEADERS));
List<List<Object>> rowData = Lists.newArrayListWithCapacity(_viewStatusResult.size());
for (ViewStatusKey key : _viewStatusResult.keySet()) {
List<Object> row = Lists.newArrayList();
ViewStatus status = _viewStatusResult.get(key);
row.add(key.getTargetType());
row.add(key.getSecurityType());
row.add(key.getValueRequirementName());
row.add(key.getCurrency());
row.add(status.getValue());
rowData.add(row);
}
return new SimpleViewStatusModel(columnHeaders, rowData, _viewStatusResult);
}
/**
* Immutable key into view status result map
*/
static class ImmutableViewStatusKey implements ViewStatusKey {
private final String _securityType;
private final String _valueName;
private final String _currency;
private final String _targetType;
public ImmutableViewStatusKey(String securityType, String valueName, String currency, String targetType) {
ArgumentChecker.notNull(securityType, "securityType");
ArgumentChecker.notNull(valueName, "valueName");
ArgumentChecker.notNull(currency, "currency");
ArgumentChecker.notNull(targetType, "targetType");
_securityType = securityType;
_valueName = valueName;
_currency = currency;
_targetType = targetType;
}
@Override
public String getSecurityType() {
return _securityType;
}
@Override
public String getValueRequirementName() {
return _valueName;
}
@Override
public String getCurrency() {
return _currency;
}
@Override
public String getTargetType() {
return _targetType;
}
public static ImmutableViewStatusKey of(final ViewStatusKey key) {
ArgumentChecker.notNull(key, "key");
return new ImmutableViewStatusKey(key.getSecurityType(), key.getValueRequirementName(), key.getCurrency(), key.getTargetType());
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
}