/*
* Copyright (c) 2012 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* HUMBOLDT EU Integrated Project #030962
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.cst.functions.groovy;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.google.common.collect.ListMultimap;
import eu.esdihumboldt.cst.functions.groovy.internal.GroovyUtil;
import eu.esdihumboldt.cst.functions.groovy.internal.InstanceAccessorArrayList;
import eu.esdihumboldt.hale.common.align.model.Cell;
import eu.esdihumboldt.hale.common.align.model.Entity;
import eu.esdihumboldt.hale.common.align.model.EntityDefinition;
import eu.esdihumboldt.hale.common.align.model.impl.PropertyEntityDefinition;
import eu.esdihumboldt.hale.common.align.transformation.engine.TransformationEngine;
import eu.esdihumboldt.hale.common.align.transformation.function.ExecutionContext;
import eu.esdihumboldt.hale.common.align.transformation.function.PropertyValue;
import eu.esdihumboldt.hale.common.align.transformation.function.TransformationException;
import eu.esdihumboldt.hale.common.align.transformation.function.impl.AbstractSingleTargetPropertyTransformation;
import eu.esdihumboldt.hale.common.align.transformation.function.impl.NoResultException;
import eu.esdihumboldt.hale.common.align.transformation.report.TransformationLog;
import eu.esdihumboldt.hale.common.core.io.Value;
import eu.esdihumboldt.hale.common.instance.groovy.InstanceBuilder;
import eu.esdihumboldt.hale.common.schema.model.TypeDefinition;
import eu.esdihumboldt.util.groovy.sandbox.GroovyService;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
/**
* Property transformation based on a Groovy script. With greedy variables in
* contrast to {@link GroovyTransformation}.
*
* @author Kai Schwierczek
*/
public class GroovyGreedyTransformation extends
AbstractSingleTargetPropertyTransformation<TransformationEngine>implements GroovyConstants {
/**
* The function/transformation ID.
*/
public static final String ID = "eu.esdihumboldt.cst.functions.groovy.greedy";
/**
* Name of the parameter specifying if instances should be used as variables
* in the binding.
*/
public static final String PARAM_INSTANCE_VARIABLES = "variablesAsInstances";
@Override
protected Object evaluate(String transformationIdentifier, TransformationEngine engine,
ListMultimap<String, PropertyValue> variables, String resultName,
PropertyEntityDefinition resultProperty, Map<String, String> executionParameters,
TransformationLog log) throws TransformationException, NoResultException {
// determine if instances should be used in variables or their values
boolean useInstanceVariables = getOptionalParameter(PARAM_INSTANCE_VARIABLES,
Value.of(false)).as(Boolean.class);
// instance builder
InstanceBuilder builder = GroovyTransformation.createBuilder(resultProperty);
// create the script binding
Binding binding = createGroovyBinding(variables.get(ENTITY_VARIABLE),
getCell().getSource().get(ENTITY_VARIABLE), getCell(), getTypeCell(), builder,
useInstanceVariables, log, getExecutionContext(),
resultProperty.getDefinition().getPropertyType());
Object result;
try {
GroovyService service = getExecutionContext().getService(GroovyService.class);
Script groovyScript = GroovyUtil.getScript(this, binding, service);
// evaluate the script
result = GroovyTransformation.evaluate(groovyScript, builder,
resultProperty.getDefinition().getPropertyType(), service);
} catch (NoResultException | TransformationException e) {
throw e;
} catch (Throwable e) {
throw new TransformationException("Error evaluating the cell script", e);
}
if (result == null) {
throw new NoResultException();
}
return result;
}
/**
* Create a Groovy binding from the list of variables.
*
* @param vars the variable values
* @param varDefs definition of the assigned variables, in case some
* variable values are not set, may be <code>null</code>
* @param cell the cell the binding is created for
* @param typeCell the type cell the binding is created for, may be
* <code>null</code>
* @param builder the instance builder for creating target instances, or
* <code>null</code> if not applicable
* @param useInstanceVariables if instances should be used as variables for
* the binding instead of extracting the instance values
* @param log the transformation log
* @param context the execution context
* @param targetInstanceType the type of the target instance
* @return the binding for use with {@link GroovyShell}
*/
public static Binding createGroovyBinding(List<PropertyValue> vars,
List<? extends Entity> varDefs, Cell cell, Cell typeCell, InstanceBuilder builder,
boolean useInstanceVariables, TransformationLog log, ExecutionContext context,
TypeDefinition targetInstanceType) {
Binding binding = GroovyUtil.createBinding(builder, cell, typeCell, log, context,
targetInstanceType);
// collect definitions to check if all were provided
Set<EntityDefinition> notDefined = new HashSet<>();
if (varDefs != null) {
for (Entity entity : varDefs) {
notDefined.add(entity.getDefinition());
}
}
// keep only defs where no value is provided
if (!notDefined.isEmpty()) {
for (PropertyValue var : vars) {
notDefined.remove(var.getProperty());
}
}
// add empty lists to environment if necessary
if (!notDefined.isEmpty()) {
for (EntityDefinition entity : notDefined) {
GroovyTransformation.addToBinding(binding, (PropertyEntityDefinition) entity,
Collections.emptyList());
}
}
Map<PropertyEntityDefinition, InstanceAccessorArrayList<Object>> bindingMap = new HashMap<>();
// collect the values
for (PropertyValue var : vars) {
PropertyEntityDefinition property = var.getProperty();
InstanceAccessorArrayList<Object> valueList = bindingMap.get(property);
if (valueList == null) {
valueList = new InstanceAccessorArrayList<>();
bindingMap.put(property, valueList);
}
valueList.add(GroovyTransformation.getUseValue(var.getValue(), useInstanceVariables));
}
// add collected values to environment
for (Entry<PropertyEntityDefinition, InstanceAccessorArrayList<Object>> entry : bindingMap
.entrySet()) {
GroovyTransformation.addToBinding(binding, entry.getKey(), entry.getValue());
}
return binding;
}
}