/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.web.analytics.blotter;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.joda.beans.Bean;
import org.joda.beans.BeanBuilder;
import org.joda.beans.JodaBeanUtils;
import org.joda.beans.MetaBean;
import org.joda.beans.MetaProperty;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.id.ExternalId;
import com.opengamma.util.ArgumentChecker;
/**
* Builds a bean instance by traversing the {@link MetaBean} and populating the {@link MetaBean#builder() builder}
* with data from a {@link BeanDataSource}. The {@link BeanBuilder builder} is returned so calling code can update
* it before building the instance, e.g. to set invariant values that aren't in the data source but are necessary
* to build a valid object.
* @param <T> The type of bean to build
*/
/* package */ class BeanBuildingVisitor<T extends Bean> implements BeanVisitor<BeanBuilder<T>> {
/** The source of data for building the bean */
private final BeanDataSource _data;
/** For looking up {@link MetaBean}s for building the bean and any sub-beans */
private final MetaBeanFactory _metaBeanFactory;
/** For converting the data to property values */
private final Converters _converters;
/** The builder for the instance being built */
private BeanBuilder<T> _builder;
/* package */ BeanBuildingVisitor(BeanDataSource data, MetaBeanFactory metaBeanFactory, Converters converters) {
ArgumentChecker.notNull(data, "data");
ArgumentChecker.notNull(metaBeanFactory, "metaBeanFactory");
ArgumentChecker.notNull(converters, "converters");
_converters = converters;
_metaBeanFactory = metaBeanFactory;
_data = data;
}
@SuppressWarnings("unchecked")
@Override
public void visitMetaBean(MetaBean metaBean) {
_builder = (BeanBuilder<T>) metaBean.builder();
}
@Override
public void visitBeanProperty(MetaProperty<?> property, BeanTraverser traverser) {
visitProperty(property, traverser);
}
@Override
public void visitSetProperty(MetaProperty<?> property, BeanTraverser traverser) {
List<?> dataValues = _data.getCollectionValues(property.name());
if (dataValues == null) {
return;
}
Set<Object> values = Sets.newHashSetWithExpectedSize(dataValues.size());
Class<?> collectionType = JodaBeanUtils.collectionType(property, property.declaringType());
for (Object dataValue : dataValues) {
values.add(convert(dataValue, property, collectionType, traverser));
}
_builder.set(property, values);
}
@Override
public void visitListProperty(MetaProperty<?> property, BeanTraverser traverser) {
List<?> dataValues = _data.getCollectionValues(property.name());
if (dataValues == null) {
return;
}
List<Object> values = Lists.newArrayList();
Class<?> collectionType = JodaBeanUtils.collectionType(property, property.declaringType());
for (Object dataValue : dataValues) {
values.add(convert(dataValue, property, collectionType, traverser));
}
_builder.set(property, values);
}
@Override
public void visitCollectionProperty(MetaProperty<?> property, BeanTraverser traverser) {
visitListProperty(property, traverser);
}
@Override
public void visitMapProperty(MetaProperty<?> property, BeanTraverser traverser) {
_builder.set(property, buildMap(property, traverser));
}
@Override
public void visitProperty(MetaProperty<?> property, BeanTraverser traverser) {
_builder.set(property, convert(_data.getValue(property.name()), property, property.propertyType(), traverser));
}
@Override
public BeanBuilder<T> finish() {
return _builder;
}
private Object convert(Object value, MetaProperty<?> property, Class<?> expectedType, BeanTraverser traverser) {
Object convertedValue = _converters.convert(value, property, expectedType);
if (convertedValue != Converters.CONVERSION_FAILED) {
return convertedValue;
}
if (value instanceof BeanDataSource) {
BeanDataSource beanData = (BeanDataSource) value;
BeanBuildingVisitor<?> visitor = new BeanBuildingVisitor<>(beanData, _metaBeanFactory, _converters);
MetaBean metaBean = _metaBeanFactory.beanFor(beanData);
return ((BeanBuilder<?>) traverser.traverse(metaBean, visitor)).build();
}
throw new IllegalArgumentException("Unable to convert " + value + " to " + expectedType.getName());
}
private Map<?, ?> buildMap(MetaProperty<?> property, BeanTraverser traverser) {
Map<?, ?> sourceData = _data.getMapValues(property.name());
Class<? extends Bean> beanType = property.metaBean().beanType();
Class<?> keyType = JodaBeanUtils.mapKeyType(property, beanType);
Class<?> valueType = JodaBeanUtils.mapValueType(property, beanType);
Map<Object, Object> map = Maps.newHashMapWithExpectedSize(sourceData.size());
for (Map.Entry<?, ?> entry : sourceData.entrySet()) {
Object key = convert(entry.getKey(), property, keyType, traverser);
Object value = convert(entry.getValue(), property, valueType, traverser);
map.put(key, value);
}
return map;
}
}
/**
* Converter that ignores its input and always returns {@code FINANCIAL_REGION~GB}. This is for building FX securities
* which always have the same region.
*/
/* package */ class FXRegionConverter implements Converter<Object, ExternalId> {
/** Great Britain region */
private static final ExternalId GB_REGION = ExternalId.of(ExternalSchemes.FINANCIAL, "GB");
/**
* Ignores its input, always returns {@code FINANCIAL_REGION~GB}.
* @param notUsed Not used
* @return {@code FINANCIAL_REGION~GB}
*/
@Override
public ExternalId convert(Object notUsed) {
return GB_REGION;
}
}