/**
* 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.Set;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValuePropertyNames;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.web.analytics.formatting.TypeFormatter;
/**
* Specifies the header label of a column and the type of data it displays.
*/
/* package */ class GridColumn {
/** The column header. */
private final String _header;
/** Description of the column. */
private final String _description;
/** Type of data displayed in the column, null if unknown or if different rows contain different types. */
private final Class<?> _type;
/** Converts cell data to strings or objects for display in the client. */
private final CellRenderer _renderer;
/** Specifies the analytics data displayed in the column, null if the column data doesn't come from the engine. */
private final ColumnSpecification _columnSpec;
/** Null if this column doesn't display exploded data, otherwise the key into the exploded data */
private final Object _inlineKey;
/** Null if this column doesn't display exploded data, otherwise the index in the set of exploded data columns */
private final Integer _inlineIndex;
/**
* If a column's data can be displayed inline across multiple columns the type of data displayed
* in the cells will not be the same as the column's underlying data type. e.g. if a column contains a vector
* of double values the cells will contain doubles when but the underlying type of the first column will be a
* vector.
*/
private final Class<?> _underlyingType;
/* package */ GridColumn(String header, String description, Class<?> type, CellRenderer renderer) {
this(header, description, type, null, renderer, null, null, null);
}
/* package */ GridColumn(String header,
String description,
Class<?> type,
CellRenderer renderer,
ColumnSpecification columnSpec) {
this(header, description, type, null, renderer, columnSpec, null, null);
}
/* package */ GridColumn(String header,
String description,
Class<?> type,
Class<?> underlyingType,
CellRenderer renderer,
ColumnSpecification columnSpec,
Object inlineKey,
Integer inlineIndex) {
ArgumentChecker.notNull(header, "header");
ArgumentChecker.notNull(renderer, "renderer");
_inlineIndex = inlineIndex;
_columnSpec = columnSpec;
_header = header;
_renderer = renderer;
if (description != null) {
_description = description;
} else {
_description = header;
}
_type = type;
_underlyingType = underlyingType;
_inlineKey = inlineKey;
}
/**
* Factory method that creates a column for a column specification, calculation configutation and data type.
* @param columnSpec The column specification
* @param columnType Type of data displayed in the column
* @return A column for displaying data calculated for the requirement and calculation configuration
*/
/* package */ static GridColumn forSpec(ColumnSpecification columnSpec,
Class<?> columnType,
TargetLookup targetLookup) {
return forSpec(columnSpec.getHeader(), columnSpec, columnType, null, targetLookup, null, null);
}
/**
* Factory method to create a column for inlined values. These are single values (e.g. vectors) displayed over
* multiple columns.
* @param header The column header
* @param columnSpec Specification of the column's value
* @param columnType The type displayed in the column
* @param underlyingType The type of the column's underlying data, can be different from the type displayed in the
* column for types that are displayed inline, e.g. vectors of doubles where the double values are displayed but
* the underlying type is a vector
* @param targetLookup For looking up values to populate the column
* @param inlineIndex The index of the individual data item in this column. This is used to extract each cell's data
* from the value.
* @return The column
*/
/* package */ static GridColumn forSpec(String header,
ColumnSpecification columnSpec,
Class<?> columnType,
Class<?> underlyingType,
TargetLookup targetLookup,
Object inlineKey,
Integer inlineIndex) {
CellRenderer renderer = new AnalyticsRenderer(columnSpec, targetLookup);
return new GridColumn(header,
createDescription(columnSpec.getValueProperties()),
columnType,
underlyingType,
renderer,
columnSpec,
inlineKey,
inlineIndex);
}
/**
* @return Label text for the column header
*/
/* package */ String getHeader() {
return _header;
}
/**
* @return Description of the column's data
*/
/* package */ String getDescription() {
return _description;
}
/**
* @return Type of data displayed in the column, can be null if the type is unknown or can change
*/
/* package */ Class<?> getType() {
return _type;
}
/**
* @return The specification of this column's analytics data or null if it displays static data.
*/
/* package */ ColumnSpecification getSpecification() {
return _columnSpec;
}
/* package */ Integer getInlineIndex() {
return _inlineIndex;
}
/**
* @return If this column's data can be displayed inline (e.g. a vector) this returns the underlying type which
* won't be the type displayed in the cells. e.g. for a vector of doubles the underlying type is vector but the
* type of values displayed in the cells is double. If the column's data can't be displayed inline this method
* returns the same value as {@link #getType()}.
*/
/* package */ Class<?> getUnderlyingType() {
if (_underlyingType != null) {
return _underlyingType;
} else {
return _type;
}
}
/* package */ ResultsCell buildResults(int rowIndex, TypeFormatter.Format format, ResultsCache cache) {
return _renderer.getResults(rowIndex, format, cache, _type, _inlineKey);
}
private static String createDescription(ValueProperties constraints) {
if (constraints.isEmpty()) {
return "No constraints";
}
StringBuilder sb = new StringBuilder();
boolean firstProperty = true;
for (String propertyName : constraints.getProperties()) {
if (ValuePropertyNames.FUNCTION.equals(propertyName)) {
continue;
}
if (firstProperty) {
firstProperty = false;
} else {
sb.append("; \n");
}
sb.append(propertyName).append("=");
Set<String> propertyValues = constraints.getValues(propertyName);
boolean isOptional = constraints.isOptional(propertyName);
if (propertyValues.size() == 0) {
sb.append("[empty]");
} else if (propertyValues.size() == 1 && !isOptional) {
sb.append(propertyValues.iterator().next());
} else {
sb.append("(");
boolean firstValue = true;
for (String propertyValue : propertyValues) {
if (firstValue) {
firstValue = false;
} else {
sb.append(", ");
}
sb.append(propertyValue);
}
sb.append(")");
}
if (isOptional) {
sb.append("?");
}
}
return sb.toString();
}
@Override
public String toString() {
return "AnalyticsColumn [" +
"_header='" + _header + '\'' +
", _type=" + _type +
", _description='" + _description + '\'' +
"]";
}
// TODO merge this into the AnalyticsColumn and create subclasses for each of the renderer classes
/* package */ interface CellRenderer {
ResultsCell getResults(int rowIndex,
TypeFormatter.Format format,
ResultsCache cache,
Class<?> columnType,
Object inlineKey);
}
}