package org.molgenis.data.mapper.algorithmgenerator.service.impl;
import org.apache.commons.lang3.StringUtils;
import org.molgenis.data.DataService;
import org.molgenis.data.mapper.algorithmgenerator.bean.GeneratedAlgorithm;
import org.molgenis.data.mapper.algorithmgenerator.generator.AlgorithmGenerator;
import org.molgenis.data.mapper.algorithmgenerator.generator.NumericAlgorithmGenerator;
import org.molgenis.data.mapper.algorithmgenerator.generator.OneToManyCategoryAlgorithmGenerator;
import org.molgenis.data.mapper.algorithmgenerator.generator.OneToOneCategoryAlgorithmGenerator;
import org.molgenis.data.mapper.algorithmgenerator.service.AlgorithmGeneratorService;
import org.molgenis.data.mapper.mapping.model.AttributeMapping.AlgorithmState;
import org.molgenis.data.mapper.service.UnitResolver;
import org.molgenis.data.mapper.service.impl.AlgorithmTemplate;
import org.molgenis.data.mapper.service.impl.AlgorithmTemplateService;
import org.molgenis.data.mapper.utils.AlgorithmGeneratorHelper;
import org.molgenis.data.mapper.utils.MagmaUnitConverter;
import org.molgenis.data.meta.model.Attribute;
import org.molgenis.data.meta.model.EntityType;
import org.molgenis.data.semanticsearch.explain.bean.ExplainedAttribute;
import org.springframework.beans.factory.annotation.Autowired;
import javax.measure.quantity.Quantity;
import javax.measure.unit.Unit;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import static java.util.Objects.requireNonNull;
import static org.molgenis.data.mapper.mapping.model.AttributeMapping.AlgorithmState.GENERATED_HIGH;
import static org.molgenis.data.mapper.mapping.model.AttributeMapping.AlgorithmState.GENERATED_LOW;
public class AlgorithmGeneratorServiceImpl implements AlgorithmGeneratorService
{
private final List<AlgorithmGenerator> generators;
private final AlgorithmTemplateService algorithmTemplateService;
private final UnitResolver unitResolver;
private final MagmaUnitConverter magmaUnitConverter = new MagmaUnitConverter();
@Autowired
public AlgorithmGeneratorServiceImpl(DataService dataService, UnitResolver unitResolver,
AlgorithmTemplateService algorithmTemplateService)
{
this.algorithmTemplateService = requireNonNull(algorithmTemplateService);
this.unitResolver = requireNonNull(unitResolver);
this.generators = Arrays.asList(new OneToOneCategoryAlgorithmGenerator(dataService),
new OneToManyCategoryAlgorithmGenerator(dataService), new NumericAlgorithmGenerator(unitResolver));
}
@Override
public String generate(Attribute targetAttribute, List<Attribute> sourceAttributes, EntityType targetEntityType,
EntityType sourceEntityType)
{
if (sourceAttributes.size() > 0)
{
for (AlgorithmGenerator generator : generators)
{
if (generator.isSuitable(targetAttribute, sourceAttributes))
{
return generator.generate(targetAttribute, sourceAttributes, targetEntityType, sourceEntityType);
}
}
return generateMixedTypes(targetAttribute, sourceAttributes, targetEntityType, sourceEntityType);
}
return StringUtils.EMPTY;
}
String generateMixedTypes(Attribute targetAttribute, List<Attribute> sourceAttributes, EntityType targetEntityType,
EntityType sourceEntityType)
{
StringBuilder stringBuilder = new StringBuilder();
if (sourceAttributes.size() == 1)
{
stringBuilder.append(String.format("$('%s').value();", sourceAttributes.get(0).getName()));
}
else if (sourceAttributes.size() > 1)
{
for (Attribute sourceAttribute : sourceAttributes)
{
stringBuilder.append(generate(targetAttribute, Arrays.asList(sourceAttribute), targetEntityType,
sourceEntityType));
}
}
return stringBuilder.toString();
}
@Override
public GeneratedAlgorithm generate(Attribute targetAttribute, Map<Attribute, ExplainedAttribute> sourceAttributes,
EntityType targetEntityType, EntityType sourceEntityType)
{
String algorithm = StringUtils.EMPTY;
AlgorithmState algorithmState = null;
Set<Attribute> mappedSourceAttributes = null;
if (sourceAttributes.size() > 0)
{
AlgorithmTemplate algorithmTemplate = algorithmTemplateService.find(sourceAttributes).findFirst()
.orElse(null);
if (algorithmTemplate != null)
{
algorithm = algorithmTemplate.render();
mappedSourceAttributes = AlgorithmGeneratorHelper
.extractSourceAttributesFromAlgorithm(algorithm, sourceEntityType);
algorithm = convertUnitForTemplateAlgorithm(algorithm, targetAttribute, targetEntityType,
mappedSourceAttributes, sourceEntityType);
algorithmState = GENERATED_HIGH;
}
else
{
Entry<Attribute, ExplainedAttribute> firstEntry = sourceAttributes.entrySet().stream().findFirst()
.get();
Attribute sourceAttribute = firstEntry.getKey();
algorithm = generate(targetAttribute, Arrays.asList(sourceAttribute), targetEntityType,
sourceEntityType);
mappedSourceAttributes = AlgorithmGeneratorHelper
.extractSourceAttributesFromAlgorithm(algorithm, sourceEntityType);
algorithmState = firstEntry.getValue().isHighQuality() ? GENERATED_HIGH : GENERATED_LOW;
}
}
return GeneratedAlgorithm.create(algorithm, mappedSourceAttributes, algorithmState);
}
String convertUnitForTemplateAlgorithm(String algorithm, Attribute targetAttribute, EntityType targetEntityType,
Set<Attribute> sourceAttributes, EntityType sourceEntityType)
{
Unit<? extends Quantity> targetUnit = unitResolver.resolveUnit(targetAttribute, targetEntityType);
for (Attribute sourceAttribute : sourceAttributes)
{
Unit<? extends Quantity> sourceUnit = unitResolver.resolveUnit(sourceAttribute, sourceEntityType);
String convertUnit = magmaUnitConverter.convertUnit(targetUnit, sourceUnit);
if (StringUtils.isNotBlank(convertUnit))
{
String attrMagamSyntax = String.format("$('%s')", sourceAttribute.getName());
String unitConvertedMagamSyntax = convertUnit.startsWith(".") ?
attrMagamSyntax + convertUnit : attrMagamSyntax + "." + convertUnit;
algorithm = StringUtils.replace(algorithm, attrMagamSyntax, unitConvertedMagamSyntax);
}
}
return algorithm;
}
}