/*
* 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.numeric;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.google.common.base.Joiner;
import com.google.common.collect.ListMultimap;
import com.iabcinc.jmep.Environment;
import com.iabcinc.jmep.Expression;
import com.iabcinc.jmep.XExpression;
import com.iabcinc.jmep.hooks.Constant;
import eu.esdihumboldt.hale.common.align.model.ChildContext;
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.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.report.TransformationLog;
import net.jcip.annotations.Immutable;
/**
* Mathematical expression evaluation function.
*
* @author Simon Templer
*/
@Immutable
public class MathematicalExpression
extends AbstractSingleTargetPropertyTransformation<TransformationEngine>
implements MathematicalExpressionFunction {
/**
* @see AbstractSingleTargetPropertyTransformation#evaluate(String,
* TransformationEngine, ListMultimap, String,
* PropertyEntityDefinition, Map, TransformationLog)
*/
@Override
protected Object evaluate(String transformationIdentifier, TransformationEngine engine,
ListMultimap<String, PropertyValue> variables, String resultName,
PropertyEntityDefinition resultProperty, Map<String, String> executionParameters,
TransformationLog log) throws TransformationException {
// get the mathematical expression
String expression = getParameterChecked(PARAMETER_EXPRESSION).as(String.class);
// replace transformation variables in expression
expression = getExecutionContext().getVariables().replaceVariables(expression);
List<PropertyValue> vars = variables.get(ENTITY_VARIABLE);
try {
return evaluateExpression(expression, vars);
} catch (XExpression e) {
throw new TransformationException("Error evaluating the cell expression", e);
}
}
/**
* Evaluate a mathematical expression.
*
* @param expression the mathematical expression. It may contain references
* to variables
* @param vars the list of available property values that may be bound to
* variables
* @return the evaluated expression, which can be Double, Integer or String
* @throws XExpression if the expression could not be evaluated
*/
public static Object evaluateExpression(String expression, List<PropertyValue> vars)
throws XExpression {
Environment env = new Environment();
for (PropertyValue var : vars) {
// add the variable to the environment
// determine the variable value
Object value = var.getValue();
Number number;
if (value instanceof Number) {
number = (Number) value;
}
else {
// try conversion to Double as default
number = var.getValueAs(Double.class);
}
// the JMEP library only supports Integer and Doubles, but e.g. no
// Floats
if (!(number instanceof Integer) && !(number instanceof Double)) {
number = number.doubleValue();
}
// determine the variable name
String name = var.getProperty().getDefinition().getName().getLocalPart();
Constant varValue = new Constant(number);
// add with short name, but ensure no variable with only a short
// name is overridden
if (env.getVariable(name) == null || var.getProperty().getPropertyPath().size() == 1) {
env.addVariable(name, varValue);
}
// add with long name if applicable
if (var.getProperty().getPropertyPath().size() > 1) {
List<String> names = new ArrayList<String>();
for (ChildContext context : var.getProperty().getPropertyPath()) {
names.add(context.getChild().getName().getLocalPart());
}
String longName = Joiner.on('.').join(names);
env.addVariable(longName, varValue);
}
}
Expression ex = new Expression(expression, env);
return ex.evaluate();
}
}