/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.fudgemsg;
import java.lang.reflect.Array;
import java.util.Iterator;
import org.fudgemsg.FudgeField;
import org.fudgemsg.FudgeFieldType;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.MutableFudgeMsg;
import org.fudgemsg.mapping.FudgeBuilderFor;
import org.fudgemsg.mapping.FudgeDeserializer;
import org.fudgemsg.mapping.FudgeSerializer;
import org.fudgemsg.types.SecondaryFieldType;
import org.fudgemsg.wire.types.FudgeWireType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.opengamma.financial.analytics.DoubleLabelledMatrix3D;
import com.opengamma.financial.analytics.LabelledMatrix3D;
import com.opengamma.util.ClassUtils;
/**
* Base class for builders of {@link LabelledMatrix3D} subclasses. Basic message structure is:
* <dl>
* <dd>[X|Y|Z]_KEYS</dd>
* <dt>The values of the keys. The default encoding is a sub-message containing the keys as anonymous fields, but if the keys are primitive types it may use one of the built-in Fudge array types.</dt>
* <dd>[X|Y|Z]_LABELS</dd>
* <dt>The values of the labels as a sub-message containing an anonymous field for each value.</dt>
* <dd>[X|Y|Z]_TYPES</dd>
* <dt>Additional type information for the labels as a sub-message containing an anonymous field for each value in <code>?_LABELS</code>. If a numeric value, this is the Fudge type identifier of the
* original value. If a string value, this is a class name of the original type. This is an optional field. If omitted, default types will be inferred from <code>?_LABELS</code>. It will only be
* generated if the inferred types differ from the actual types.</dt>
* <dd>VALUES</dd>
* <dt>A flattened array of matrix data, e.g. <code>{ { { 1, 2 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } }</code> becomes <code>{ 1, 2, 3, 4, 5, 6, 7, 8 }</code>.</dt>
*
* @param <KX> X key type
* @param <KY> Y key type
* @param <KZ> Z key type
* @param <T> sub-type of matrix to build
*/
public abstract class LabelledMatrix3DFudgeBuilder<KX, KY, KZ, T extends LabelledMatrix3D<KX, KY, KZ, ?, ?, ?, T>> extends AbstractFudgeBuilder<T> {
private static final Logger s_logger = LoggerFactory.getLogger(LabelledMatrix3DFudgeBuilder.class);
/** Field name. */
public static final String X_KEYS_KEY = "X_KEYS";
/** Field name. */
public static final String Y_KEYS_KEY = "Y_KEYS";
/** Field name. */
public static final String Z_KEYS_KEY = "Z_KEYS";
/** Field name. */
public static final String X_LABELS_KEY = "X_LABELS";
/** Field name. */
public static final String X_LABEL_TYPES_KEY = "X_LABEL_TYPES";
/** Field name. */
public static final String Y_LABELS_KEY = "Y_LABELS";
/** Field name. */
public static final String Y_LABEL_TYPES_KEY = "Y_LABEL_TYPES";
/** Field name. */
public static final String Z_LABELS_KEY = "Z_LABELS";
/** Field name. */
public static final String Z_LABEL_TYPES_KEY = "Z_LABEL_TYPES";
/** Field name. */
public static final String VALUES_KEY = "VALUES";
private final Class<KX> _xKeyClass;
private final Class<KY> _yKeyClass;
private final Class<KZ> _zKeyClass;
protected LabelledMatrix3DFudgeBuilder(final Class<KX> xKey, final Class<KY> yKey, final Class<KZ> zKey) {
_xKeyClass = xKey;
_yKeyClass = yKey;
_zKeyClass = zKey;
}
protected Class<KX> getXKeyClass() {
return _xKeyClass;
}
protected Class<KY> getYKeyClass() {
return _yKeyClass;
}
protected Class<KZ> getZKeyClass() {
return _zKeyClass;
}
@Override
protected final void buildMessage(final FudgeSerializer serializer, final MutableFudgeMsg message, final T object) {
s_logger.debug("Building message from {}", object);
writeLabels(serializer, message, object.getXLabels(), X_LABELS_KEY, X_LABEL_TYPES_KEY);
writeXKeys(serializer, message, object.getXKeys());
writeLabels(serializer, message, object.getYLabels(), Y_LABELS_KEY, Y_LABEL_TYPES_KEY);
writeYKeys(serializer, message, object.getYKeys());
writeLabels(serializer, message, object.getZLabels(), Z_LABELS_KEY, Z_LABEL_TYPES_KEY);
writeZKeys(serializer, message, object.getZKeys());
writeValues(message, object.getValues());
s_logger.debug("Built {}", message);
}
/**
* General purpose label writer.
*
* @param serializer serialization context
* @param message message to add the key to
* @param labels labels to add
* @param valueKey name of the field to hold label value information
* @param typeKey name of the optional field to hold additional type information
*/
protected void writeLabels(final FudgeSerializer serializer, final MutableFudgeMsg message, final Object[] labels, final String valueKey, final String typeKey) {
final MutableFudgeMsg valueMsg = serializer.newMessage();
final MutableFudgeMsg typeMsg = serializer.newMessage();
boolean needsTypeInfo = false;
for (Object label : labels) {
final FudgeFieldType type = serializer.getFudgeContext().getTypeDictionary().getByJavaType(label.getClass());
if (type == null) {
serializer.addToMessage(valueMsg, null, null, label);
valueMsg.add(null, null, FudgeWireType.SUB_MESSAGE, serializer.objectToFudgeMsg(label));
typeMsg.add(null, null, label.getClass().getName());
needsTypeInfo = true;
} else {
valueMsg.add(null, null, type, label);
if (type instanceof SecondaryFieldType<?, ?>) {
typeMsg.add(null, null, type.getJavaType().getName());
needsTypeInfo = true;
} else {
typeMsg.add(null, null, type.getTypeId());
}
}
}
if (!needsTypeInfo) {
// Message contains only primitive types; only put the type info in if there have been type reductions
final Iterator<FudgeField> itrValueMsg = valueMsg.iterator();
final Iterator<FudgeField> itrTypeMsg = typeMsg.iterator();
while (itrValueMsg.hasNext()) {
if (itrValueMsg.next().getType().getTypeId() != typeMsg.getFieldValue(Integer.class, itrTypeMsg.next())) {
needsTypeInfo = true;
break;
}
}
}
message.add(valueKey, valueMsg);
if (needsTypeInfo) {
message.add(typeKey, typeMsg);
}
}
/**
* General purpose key writer; defaults to {@link #writeKeys} - override in a subclass for more efficient handling.
*
* @param serializer serialization context
* @param message message to add the key to
* @param keys keys to add
*/
protected void writeXKeys(final FudgeSerializer serializer, final MutableFudgeMsg message, final KX[] keys) {
writeKeys(serializer, message, keys, getXKeyClass(), X_KEYS_KEY);
}
/**
* General purpose key writer; defaults to {@link #writeKeys} - override in a subclass for more efficient handling.
*
* @param serializer serialization context
* @param message message to add the key to
* @param keys keys to add
*/
protected void writeYKeys(final FudgeSerializer serializer, final MutableFudgeMsg message, final KY[] keys) {
writeKeys(serializer, message, keys, getYKeyClass(), Y_KEYS_KEY);
}
/**
* General purpose key writer; defaults to {@link #writeKeys} - override in a subclass for more efficient handling.
*
* @param serializer serialization context
* @param message message to add the key to
* @param keys keys to add
*/
protected void writeZKeys(final FudgeSerializer serializer, final MutableFudgeMsg message, final KZ[] keys) {
writeKeys(serializer, message, keys, getZKeyClass(), Z_KEYS_KEY);
}
/**
* General purposes key writer; add each key as a field to a sub-message.
*
* @param <K> type of key
* @param serializer serialization context
* @param message message to add the keys to
* @param keys keys to add
* @param keyClass common base type of the keys (as known to the receiver)
* @param keysKey name of the field to add the keys as
*/
protected <K> void writeKeys(final FudgeSerializer serializer, final MutableFudgeMsg message, final K[] keys, final Class<K> keyClass, final String keysKey) {
final MutableFudgeMsg submsg = serializer.newMessage();
for (K key : keys) {
serializer.addToMessageWithClassHeaders(submsg, null, null, key, keyClass);
}
message.add(keysKey, submsg);
}
protected void writeDoubleKeys(final MutableFudgeMsg message, final Double[] keys, final String keysKey) {
final double[] arr = new double[keys.length];
for (int i = 0; i < keys.length; i++) {
arr[i] = keys[i];
}
message.add(keysKey, arr);
}
/**
* General purpose value writer.
*
* @param message message to add the values to
* @param values values to add
*/
protected void writeValues(final MutableFudgeMsg message, final double[][][] values) {
final int z = values.length;
final int y = values[0].length;
final int x = values[0][0].length;
final double[] flat = new double[z * y * x];
int i = 0;
for (double[][] slice : values) {
for (double[] row : slice) {
System.arraycopy(row, 0, flat, i, row.length);
i += row.length;
}
}
message.add(VALUES_KEY, flat);
}
@Override
public final T buildObject(final FudgeDeserializer deserializer, final FudgeMsg message) {
s_logger.debug("Building object from {}", message);
final KX[] xKeys = readXKeys(deserializer, message);
final Object[] xLabels = readLabels(deserializer, message, X_LABELS_KEY, X_LABEL_TYPES_KEY);
final KY[] yKeys = readYKeys(deserializer, message);
final Object[] yLabels = readLabels(deserializer, message, Y_LABELS_KEY, Y_LABEL_TYPES_KEY);
final KZ[] zKeys = readZKeys(deserializer, message);
final Object[] zLabels = readLabels(deserializer, message, Z_LABELS_KEY, Z_LABEL_TYPES_KEY);
final double[][][] values = readValues(message, xKeys.length, yKeys.length, zKeys.length);
final T result = createMatrix(xKeys, xLabels, yKeys, yLabels, zKeys, zLabels, values);
s_logger.debug("Built object {}", result);
return result;
}
/**
* Inverse of {@link #writeLabels}.
*
* @param deserializer deserialization context
* @param message message to read from
* @param labelsKey name of the field containing label values
* @param labelTypesKey name of the optional field containing additional type information
* @return new labels array
*/
protected Object[] readLabels(final FudgeDeserializer deserializer, final FudgeMsg message, final String labelsKey, final String labelTypesKey) {
final FudgeMsg valueMsg = message.getMessage(labelsKey);
if (valueMsg == null) {
s_logger.warn("Message field {} not found in {}", labelsKey, message);
throw new IllegalArgumentException("Message is not a LabelledMatrix3D - does not contain a " + labelsKey + " field");
}
final Object[] labels = new Object[valueMsg.getNumFields()];
final FudgeMsg typeMsg = message.getMessage(labelTypesKey);
final Iterator<FudgeField> itrValue = valueMsg.iterator();
final Iterator<FudgeField> itrType = (typeMsg != null) ? typeMsg.iterator() : null;
int i = 0;
while (itrValue.hasNext()) {
Class<?> type = null;
if (itrType != null) {
final FudgeField typeField = itrType.next();
final Object val = typeField.getValue();
if (val instanceof String) {
try {
type = ClassUtils.loadClass((String) val);
} catch (ClassNotFoundException e) {
s_logger.warn("Message field {} requires unknown class {}", i, val);
type = Object.class;
}
} else {
type = deserializer.getFudgeContext().getTypeDictionary().getByTypeId(((Number) val).intValue()).getJavaType();
}
} else {
type = Object.class;
}
labels[i++] = deserializer.fieldValueToObject(type, itrValue.next());
}
return labels;
}
/**
* Inverse of {@link #readXKeys}.
*
* @param deserializer deserialization context
* @param message message to read from
* @return the keys
*/
protected KX[] readXKeys(final FudgeDeserializer deserializer, final FudgeMsg message) {
return readKeys(deserializer, message, getXKeyClass(), X_KEYS_KEY);
}
/**
* Inverse of {@link #readXKeys}.
*
* @param deserializer deserialization context
* @param message message to read from
* @return the keys
*/
protected KY[] readYKeys(final FudgeDeserializer deserializer, final FudgeMsg message) {
return readKeys(deserializer, message, getYKeyClass(), Y_KEYS_KEY);
}
/**
* Inverse of {@link #readXKeys}.
*
* @param deserializer deserialization context
* @param message message to read from
* @return the keys
*/
protected KZ[] readZKeys(final FudgeDeserializer deserializer, final FudgeMsg message) {
return readKeys(deserializer, message, getZKeyClass(), Z_KEYS_KEY);
}
/**
* Inverse of {@link #writeKeys}.
*
* @param <K> key type
* @param deserializer deserialization context
* @param message message to read from
* @param keyClass common base type of the keys (as understood by the sender)
* @param keysKey name of the field containing the keys
* @return the keys
*/
@SuppressWarnings("unchecked")
protected <K> K[] readKeys(final FudgeDeserializer deserializer, final FudgeMsg message, final Class<K> keyClass, final String keysKey) {
final FudgeMsg submsg = message.getMessage(keysKey);
if (submsg == null) {
s_logger.warn("Message field {} not found in {}", keysKey, message);
throw new IllegalArgumentException("Message is not a LabelledMatrix3D - does not contain a " + keysKey + " field");
}
final K[] keys = (K[]) Array.newInstance(keyClass, submsg.getNumFields());
int i = 0;
for (FudgeField key : submsg) {
keys[i++] = deserializer.fieldValueToObject(keyClass, key);
}
return keys;
}
protected Double[] readDoubleKeys(final FudgeMsg message, final String keysKey) {
final FudgeField field = message.getByName(keysKey);
if (field == null) {
s_logger.warn("Message field {} not found in {}", keysKey, message);
throw new IllegalArgumentException("Message is not a LabelledMatrix3D - does not contain a " + keysKey + " field");
}
final double[] keys = message.getFieldValue(double[].class, field);
final Double[] arr = new Double[keys.length];
for (int i = 0; i < keys.length; i++) {
arr[i] = keys[i];
}
return arr;
}
/**
* Inverse of {@link #writeValues}.
*
* @param message message to read from
* @param x number of values in the X dimension
* @param y number of values in the Y dimension
* @param z number of values in the Z dimension
* @return the values
*/
protected double[][][] readValues(final FudgeMsg message, final int x, final int y, final int z) {
final FudgeField field = message.getByName(VALUES_KEY);
if (field == null) {
s_logger.warn("Message field {} not found in {}", VALUES_KEY, message);
throw new IllegalArgumentException("Message is not a LabelledMatrix3D - does not contain a " + VALUES_KEY + " field");
}
final double[] flat = message.getFieldValue(double[].class, field);
if (flat.length != x * y * z) {
s_logger.warn("Invalid values length in {}", message);
throw new IllegalArgumentException("Expected " + (x * y * z) + " matrix elements, got " + flat.length);
}
final double[][][] values = new double[z][y][x];
int i = 0;
for (double[][] slice : values) {
for (double[] row : slice) {
System.arraycopy(flat, i, row, 0, x);
i += x;
}
}
return values;
}
protected abstract T createMatrix(final KX[] xKeys, final Object[] xLabels, final KY[] yKeys, final Object[] yLabels, final KZ[] zKeys, final Object[] zLabels, final double[][][] values);
/**
* Builder for {@link DoubleLabelledMatrix3D}.
*/
@FudgeBuilderFor(DoubleLabelledMatrix3D.class)
public static final class DoubleBuilder extends LabelledMatrix3DFudgeBuilder<Double, Double, Double, DoubleLabelledMatrix3D> {
public DoubleBuilder() {
super(Double.class, Double.class, Double.class);
}
@Override
protected void writeXKeys(final FudgeSerializer serializer, final MutableFudgeMsg message, final Double[] keys) {
writeDoubleKeys(message, keys, X_KEYS_KEY);
}
@Override
protected void writeYKeys(final FudgeSerializer serializer, final MutableFudgeMsg message, final Double[] keys) {
writeDoubleKeys(message, keys, Y_KEYS_KEY);
}
@Override
protected void writeZKeys(final FudgeSerializer serializer, final MutableFudgeMsg message, final Double[] keys) {
writeDoubleKeys(message, keys, Z_KEYS_KEY);
}
@Override
protected Double[] readXKeys(final FudgeDeserializer deserializer, final FudgeMsg message) {
return readDoubleKeys(message, X_KEYS_KEY);
}
@Override
protected Double[] readYKeys(final FudgeDeserializer deserializer, final FudgeMsg message) {
return readDoubleKeys(message, Y_KEYS_KEY);
}
@Override
protected Double[] readZKeys(final FudgeDeserializer deserializer, final FudgeMsg message) {
return readDoubleKeys(message, Z_KEYS_KEY);
}
@Override
protected DoubleLabelledMatrix3D createMatrix(final Double[] xKeys, final Object[] xLabels, final Double[] yKeys, final Object[] yLabels, final Double[] zKeys, final Object[] zLabels,
final double[][][] values) {
return new DoubleLabelledMatrix3D(xKeys, xLabels, yKeys, yLabels, zKeys, zLabels, values);
}
}
}