Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.molgenis.semanticmapper.service.impl.AlgorithmServiceImpl Maven / Gradle / Ivy
package org.molgenis.semanticmapper.service.impl;
import static com.google.common.collect.Sets.newLinkedHashSet;
import static com.google.common.collect.Streams.stream;
import static java.lang.Double.parseDouble;
import static java.lang.Math.round;
import static java.lang.Math.toIntExact;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.molgenis.data.DataConverter.toBoolean;
import static org.molgenis.data.meta.AttributeType.DATE;
import static org.molgenis.data.meta.AttributeType.DATE_TIME;
import static org.molgenis.data.meta.AttributeType.DECIMAL;
import static org.molgenis.data.meta.AttributeType.INT;
import static org.molgenis.data.meta.AttributeType.LONG;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.molgenis.data.Entity;
import org.molgenis.data.EntityManager;
import org.molgenis.data.meta.AttributeType;
import org.molgenis.data.meta.IllegalAttributeTypeException;
import org.molgenis.data.meta.model.Attribute;
import org.molgenis.data.meta.model.EntityType;
import org.molgenis.js.magma.JsMagmaScriptEvaluator;
import org.molgenis.script.core.ScriptException;
import org.molgenis.security.core.runas.RunAsSystem;
import org.molgenis.semanticmapper.algorithmgenerator.bean.GeneratedAlgorithm;
import org.molgenis.semanticmapper.algorithmgenerator.service.AlgorithmGeneratorService;
import org.molgenis.semanticmapper.mapping.model.AttributeMapping;
import org.molgenis.semanticmapper.mapping.model.AttributeMapping.AlgorithmState;
import org.molgenis.semanticmapper.mapping.model.EntityMapping;
import org.molgenis.semanticmapper.service.AlgorithmService;
import org.molgenis.semanticsearch.explain.bean.EntityTypeSearchResults;
import org.molgenis.semanticsearch.explain.bean.ExplainedAttribute;
import org.molgenis.semanticsearch.semantic.Hits;
import org.molgenis.semanticsearch.service.SemanticSearchService;
import org.molgenis.util.UnexpectedEnumException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AlgorithmServiceImpl implements AlgorithmService {
private static final Logger LOG = LoggerFactory.getLogger(AlgorithmServiceImpl.class);
private final SemanticSearchService semanticSearchService;
private final AlgorithmGeneratorService algorithmGeneratorService;
private final JsMagmaScriptEvaluator jsMagmaScriptEvaluator;
private final EntityManager entityManager;
public AlgorithmServiceImpl(
SemanticSearchService semanticSearchService,
AlgorithmGeneratorService algorithmGeneratorService,
EntityManager entityManager,
JsMagmaScriptEvaluator jsMagmaScriptEvaluator) {
this.semanticSearchService = requireNonNull(semanticSearchService);
this.algorithmGeneratorService = requireNonNull(algorithmGeneratorService);
this.entityManager = requireNonNull(entityManager);
this.jsMagmaScriptEvaluator = requireNonNull(jsMagmaScriptEvaluator);
}
@Override
public String generateAlgorithm(
Attribute targetAttribute,
EntityType targetEntityType,
List sourceAttributes,
EntityType sourceEntityType) {
return algorithmGeneratorService.generate(
targetAttribute, sourceAttributes, targetEntityType, sourceEntityType);
}
@Override
public void copyAlgorithms(EntityMapping sourceEntityMapping, EntityMapping targetEntityMapping) {
sourceEntityMapping
.getAttributeMappings()
.forEach(attributeMapping -> copyAlgorithm(attributeMapping, targetEntityMapping));
}
private void copyAlgorithm(AttributeMapping attributeMapping, EntityMapping targetEntityMapping) {
AttributeMapping attributeMappingCopy = AttributeMapping.createCopy(attributeMapping);
attributeMappingCopy.setIdentifier(null);
attributeMappingCopy.setAlgorithmState(AlgorithmState.DISCUSS);
targetEntityMapping.addAttributeMapping(attributeMappingCopy);
}
@Override
@RunAsSystem
public void autoGenerateAlgorithm(
EntityType sourceEntityType, EntityType targetEntityType, EntityMapping mapping) {
EntityTypeSearchResults entityTypeSearchResults =
semanticSearchService.findAttributes(sourceEntityType, targetEntityType);
entityTypeSearchResults
.getAttributeSearchResults()
.forEach(
attributeSearchResults -> {
Attribute targetAttribute = attributeSearchResults.getAttribute();
LOG.debug(
"createAttributeMappingIfOnlyOneMatch: target= {}", targetAttribute.getName());
Hits relevantAttributes = attributeSearchResults.getHits();
GeneratedAlgorithm generatedAlgorithm =
algorithmGeneratorService.generate(
targetAttribute, relevantAttributes, targetEntityType, sourceEntityType);
if (StringUtils.isNotBlank(generatedAlgorithm.getAlgorithm())) {
AttributeMapping attributeMapping =
mapping.addAttributeMapping(targetAttribute.getName());
attributeMapping.setAlgorithm(generatedAlgorithm.getAlgorithm());
attributeMapping
.getSourceAttributes()
.addAll(generatedAlgorithm.getSourceAttributes());
attributeMapping.setAlgorithmState(generatedAlgorithm.getAlgorithmState());
LOG.debug(
"Creating attribute mapping: "
+ targetAttribute.getName()
+ " = "
+ generatedAlgorithm.getAlgorithm());
}
});
}
@Override
public Iterable applyAlgorithm(
Attribute targetAttribute, String algorithm, Iterable sourceEntities, int depth) {
return stream(sourceEntities)
.map(
entity -> {
AlgorithmEvaluation algorithmResult = new AlgorithmEvaluation(entity);
Object derivedValue;
try {
Object result = jsMagmaScriptEvaluator.eval(algorithm, entity, depth);
// jsMagmaScriptEvaluator.eval() catches and returns the error instead of throwing
// it
// so check instance of result object here
if (result instanceof ScriptException) {
return algorithmResult.errorMessage(((ScriptException) result).getMessage());
}
derivedValue = convert(result, targetAttribute);
} catch (RuntimeException e) {
if (e.getMessage() == null) {
return algorithmResult.errorMessage(
"Applying an algorithm on a null source value caused an exception. Is the target attribute required?");
}
return algorithmResult.errorMessage(e.getLocalizedMessage());
}
return algorithmResult.value(derivedValue);
})
.collect(toList());
}
@Override
public Object apply(
AttributeMapping attributeMapping,
Entity sourceEntity,
EntityType sourceEntityType,
int depth) {
String algorithm = attributeMapping.getAlgorithm();
if (isEmpty(algorithm)) {
return null;
}
Object result = jsMagmaScriptEvaluator.eval(algorithm, sourceEntity, depth);
// jsMagmaScriptEvaluator.eval() catches and returns the error instead of throwing it
// so check instance of result object here
if (result instanceof Throwable) {
throw new AlgorithmException((Throwable) result);
}
return convert(result, attributeMapping.getTargetAttribute());
}
@Override
public Collection getSourceAttributeNames(String algorithmScript) {
Collection result = emptyList();
if (!isEmpty(algorithmScript)) {
result = findMatchesForPattern(algorithmScript, "\\$\\('([^\\$\\(\\)]+)'\\)");
if (result.isEmpty()) {
result = findMatchesForPattern(algorithmScript, "\\$\\(([^\\$\\(\\)]+)\\)");
}
}
return result;
}
private static Collection findMatchesForPattern(
String algorithmScript, String patternString) {
LinkedHashSet result = newLinkedHashSet();
Matcher matcher = Pattern.compile(patternString).matcher(algorithmScript);
while (matcher.find()) {
result.add(matcher.group(1).split("\\.")[0]);
}
return result;
}
@SuppressWarnings("unchecked")
private Object convert(Object value, Attribute attr) {
Object convertedValue;
AttributeType attrType = attr.getDataType();
switch (attrType) {
case BOOL:
convertedValue = value != null ? toBoolean(value) : null;
break;
case CATEGORICAL:
case XREF:
case FILE:
convertedValue =
value != null
? entityManager.getReference(
attr.getRefEntity(), convert(value, attr.getRefEntity().getIdAttribute()))
: null;
break;
case CATEGORICAL_MREF:
case MREF:
case ONE_TO_MANY:
Collection valueIds = (Collection) value;
convertedValue =
valueIds
.stream()
.map(
valueId ->
entityManager.getReference(
attr.getRefEntity(),
convert(valueId, attr.getRefEntity().getIdAttribute())))
.collect(toList());
break;
case DATE:
convertedValue = convertToDate(value);
break;
case DATE_TIME:
convertedValue = convertToDateTime(value);
break;
case DECIMAL:
convertedValue = convertToDouble(value);
break;
case EMAIL:
case ENUM:
case HTML:
case HYPERLINK:
case SCRIPT:
case STRING:
case TEXT:
convertedValue = value != null ? value.toString() : null;
break;
case INT:
convertedValue = convertToInteger(value);
break;
case LONG:
convertedValue = convertToLong(value);
break;
case COMPOUND:
throw new IllegalAttributeTypeException(attrType);
default:
throw new UnexpectedEnumException(attrType);
}
return convertedValue;
}
LocalDate convertToDate(Object value) {
try {
return value != null
? Instant.ofEpochMilli(Double.valueOf(value.toString()).longValue())
.atZone(ZoneId.systemDefault())
.toLocalDate()
: null;
} catch (NumberFormatException e) {
LOG.debug("", e);
throw new AlgorithmException(
format("'%s' can't be converted to type '%s'", value.toString(), DATE.toString()));
}
}
private Instant convertToDateTime(Object value) {
try {
return value != null
? Instant.ofEpochMilli(Double.valueOf(value.toString()).longValue())
: null;
} catch (NumberFormatException e) {
LOG.debug("", e);
throw new AlgorithmException(
format("'%s' can't be converted to type '%s'", value.toString(), DATE_TIME.toString()));
}
}
private Double convertToDouble(Object value) {
try {
return value != null ? parseDouble(value.toString()) : null;
} catch (NumberFormatException e) {
LOG.debug("", e);
throw new AlgorithmException(
format("'%s' can't be converted to type '%s'", value.toString(), DECIMAL.toString()));
}
}
private Integer convertToInteger(Object value) {
Integer convertedValue;
try {
convertedValue = value != null ? toIntExact(round(parseDouble(value.toString()))) : null;
} catch (NumberFormatException e) {
LOG.debug("", e);
throw new AlgorithmException(
format("'%s' can't be converted to type '%s'", value.toString(), INT.toString()));
} catch (ArithmeticException e) {
LOG.debug("", e);
throw new AlgorithmException(
format(
"'%s' is larger than the maximum allowed value for type '%s'",
value.toString(), INT.toString()));
}
return convertedValue;
}
private Long convertToLong(Object value) {
Long convertedValue;
try {
convertedValue = value != null ? round(parseDouble(value.toString())) : null;
} catch (NumberFormatException e) {
LOG.debug("", e);
throw new AlgorithmException(
format("'%s' can't be converted to type '%s'", value.toString(), LONG.toString()));
}
return convertedValue;
}
}