package org.molgenis.data.mapper.algorithmgenerator.generator;
import org.molgenis.data.mapper.service.UnitResolver;
import org.molgenis.data.meta.AttributeType;
import org.molgenis.data.meta.model.Attribute;
import org.molgenis.data.meta.model.EntityType;
import org.springframework.beans.factory.annotation.Autowired;
import javax.measure.converter.ConversionException;
import javax.measure.converter.UnitConverter;
import javax.measure.quantity.Quantity;
import javax.measure.unit.Unit;
import java.util.Arrays;
import java.util.List;
import static java.util.Objects.requireNonNull;
import static org.molgenis.data.meta.AttributeType.*;
public class NumericAlgorithmGenerator implements AlgorithmGenerator
{
private final UnitResolver unitResolver;
@Autowired
public NumericAlgorithmGenerator(UnitResolver unitResolver)
{
this.unitResolver = requireNonNull(unitResolver);
}
public String generate(Attribute targetAttribute, List<Attribute> sourceAttributes, EntityType targetEntityType,
EntityType sourceEntityType)
{
StringBuilder algorithm = new StringBuilder();
if (sourceAttributes.size() == 1)
{
algorithm.append(generateUnitConversionAlgorithm(targetAttribute, targetEntityType, sourceAttributes.get(0),
sourceEntityType));
}
else if (sourceAttributes.size() > 1)
{
algorithm.append("var counter = 0;\nvar SUM=newValue(0);\n");
for (Attribute sourceAttribute : sourceAttributes)
{
String generate = generate(targetAttribute, Arrays.asList(sourceAttribute), targetEntityType,
sourceEntityType);
algorithm.append("if(!$('").append(sourceAttribute.getName()).append("').isNull().value()){\n\t")
.append("SUM.plus(")
.append(generate.endsWith(";") ? generate.substring(0, generate.length() - 1) : generate)
.append(");\n\tcounter++;\n}\n");
}
algorithm.append("if(counter !== 0){\n\tSUM.div(counter);\n\tSUM.value();\n}else{\n\tnull;\n}");
}
return algorithm.toString();
}
public boolean isSuitable(Attribute targetAttribute, List<Attribute> sourceAttributes)
{
return isNumericDataType(targetAttribute) && (sourceAttributes.stream().allMatch(this::isNumericDataType));
}
boolean isNumericDataType(Attribute attribute)
{
AttributeType enumType = attribute.getDataType();
return enumType == INT || enumType == LONG || enumType == DECIMAL;
}
String generateUnitConversionAlgorithm(Attribute targetAttribute, EntityType targetEntityType,
Attribute sourceAttribute, EntityType sourceEntityType)
{
String algorithm = null;
Unit<? extends Quantity> targetUnit = unitResolver.resolveUnit(targetAttribute, targetEntityType);
Unit<? extends Quantity> sourceUnit = unitResolver.resolveUnit(sourceAttribute, sourceEntityType);
if (sourceUnit != null)
{
if (targetUnit != null && !sourceUnit.equals(targetUnit))
{
// if units are convertible, create convert algorithm
UnitConverter unitConverter;
try
{
unitConverter = sourceUnit.getConverterTo(targetUnit);
}
catch (ConversionException e)
{
unitConverter = null;
// algorithm sets source unit and assigns source value to target
algorithm = String
.format("$('%s').unit('%s').value();", sourceAttribute.getName(), sourceUnit.toString());
}
if (unitConverter != null)
{
// algorithm sets source unit and assigns value converted to target unit to target
algorithm = String.format("$('%s').unit('%s').toUnit('%s').value();", sourceAttribute.getName(),
sourceUnit.toString(), targetUnit.toString());
}
}
else
{
// algorithm sets source unit and assigns source value to target
algorithm = String
.format("$('%s').unit('%s').value();", sourceAttribute.getName(), sourceUnit.toString());
}
}
if (algorithm == null)
{
// algorithm assigns source value to target
algorithm = String.format("$('%s').value();", sourceAttribute.getName());
}
return algorithm;
}
}