/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.tools.expression.internal;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import com.rapidminer.tools.expression.DoubleCallable;
import com.rapidminer.tools.expression.ExpressionContext;
import com.rapidminer.tools.expression.ExpressionEvaluator;
import com.rapidminer.tools.expression.ExpressionType;
import com.rapidminer.tools.expression.Function;
import com.rapidminer.tools.expression.FunctionDescription;
import com.rapidminer.tools.expression.FunctionInput;
import com.rapidminer.tools.expression.Resolver;
/**
* Provides expression-specific information, such as the function mapping, constants and variables.
* Stores all {@link Function}s and has access to all variables, dynamic variables and scope
* constants that can be used during such an evaluation via their {@link Resolver}s.
*
* @author Gisa Schaefer
*
*/
public class SimpleExpressionContext implements ExpressionContext {
private Map<String, Function> functionMap;
private List<Resolver> dynamicResolvers;
private List<Resolver> scopeResolvers;
private List<Resolver> constantResolvers;
/**
* Creates a {@link ExpressionContext} that uses the given functions and resolvers.
*
* @param functions
* the functions to use in expressions
* @param scopeResolvers
* the scope resolvers to use
* @param dynamicResolvers
* the resolvers for dynamic variables to use
* @param constantResolvers
* the resolvers for constants
*/
public SimpleExpressionContext(List<Function> functions, List<Resolver> scopeResolvers, List<Resolver> dynamicResolvers,
List<Resolver> constantResolvers) {
this.scopeResolvers = scopeResolvers;
this.dynamicResolvers = dynamicResolvers;
this.constantResolvers = constantResolvers;
this.functionMap = new LinkedHashMap<>();
for (Function function : functions) {
// take the first function with a certain function name if there is more than one
if (!this.functionMap.containsKey(function.getFunctionName())) {
this.functionMap.put(function.getFunctionName(), function);
}
}
}
@Override
public Function getFunction(String functionName) {
return functionMap.get(functionName);
}
@Override
public ExpressionEvaluator getVariable(String variableName) {
// A variable can either be a constant coming from a {@link Resolver} for constants or a
// dynamic variable. This is done to keep compatibility with the old parser where
// alpha-numeric strings could stand for constants or attribute values. Attribute values are
// now a special case of dynamic variables.
ExpressionEvaluator constant = getConstant(variableName);
if (constant != null) {
return constant;
} else {
return getDynamicVariable(variableName);
}
}
/**
* Looks for the first resolver in the resolvers list that knows the variableName.
*
* @param variableName
* the name to look for
*/
private Resolver getResolverWithKnowledge(List<Resolver> resolvers, String variableName) {
for (Resolver resolver : resolvers) {
if (resolver.getVariableType(variableName) != null) {
return resolver;
}
}
return null;
}
/**
* Creates an constant {@link ExpressionEvaluator} for the variableName using the resolver.
*/
private ExpressionEvaluator getExpressionEvaluator(String variableName, Resolver resolver) {
ExpressionType type = resolver.getVariableType(variableName);
switch (type) {
case DOUBLE:
case INTEGER:
return new SimpleExpressionEvaluator(resolver.getDoubleValue(variableName), type);
case DATE:
return new SimpleExpressionEvaluator(resolver.getDateValue(variableName), type);
case STRING:
return new SimpleExpressionEvaluator(resolver.getStringValue(variableName), type);
case BOOLEAN:
return new SimpleExpressionEvaluator(resolver.getBooleanValue(variableName), type);
default:
return null;
}
}
@Override
public ExpressionEvaluator getDynamicVariable(String variableName) {
Resolver resolver = getResolverWithKnowledge(dynamicResolvers, variableName);
if (resolver == null) {
return null;
}
return getDynamicExpressionEvaluator(variableName, resolver);
}
/**
* Creates an non-constant {@link ExpressionEvaluator} for the variableName using the resolver.
*/
private ExpressionEvaluator getDynamicExpressionEvaluator(final String variableName, final Resolver resolver) {
ExpressionType type = resolver.getVariableType(variableName);
switch (type) {
case DOUBLE:
case INTEGER:
DoubleCallable doubleCallable = new DoubleCallable() {
@Override
public double call() throws Exception {
return resolver.getDoubleValue(variableName);
}
};
return new SimpleExpressionEvaluator(doubleCallable, type, false);
case DATE:
Callable<Date> dateCallable = new Callable<Date>() {
@Override
public Date call() throws Exception {
return resolver.getDateValue(variableName);
}
};
return new SimpleExpressionEvaluator(type, dateCallable, false);
case STRING:
Callable<String> stringCallable = new Callable<String>() {
@Override
public String call() throws Exception {
return resolver.getStringValue(variableName);
}
};
return new SimpleExpressionEvaluator(stringCallable, type, false);
case BOOLEAN:
Callable<Boolean> booleanCallable = new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return resolver.getBooleanValue(variableName);
}
};
return new SimpleExpressionEvaluator(booleanCallable, false, type);
default:
return null;
}
}
@Override
public ExpressionEvaluator getScopeConstant(String scopeName) {
Resolver resolver = getResolverWithKnowledge(scopeResolvers, scopeName);
if (resolver == null) {
return null;
}
return getExpressionEvaluator(scopeName, resolver);
}
@Override
public String getScopeString(String scopeName) {
Resolver resolver = getResolverWithKnowledge(scopeResolvers, scopeName);
if (resolver == null) {
return null;
}
ExpressionType type = resolver.getVariableType(scopeName);
switch (type) {
case DOUBLE:
case INTEGER:
return resolver.getDoubleValue(scopeName) + "";
case DATE:
return resolver.getDateValue(scopeName).toString();
case STRING:
return resolver.getStringValue(scopeName);
case BOOLEAN:
return resolver.getBooleanValue(scopeName) + "";
default:
return null;
}
}
@Override
public List<FunctionDescription> getFunctionDescriptions() {
List<FunctionDescription> descriptions = new ArrayList<>(functionMap.size());
for (Function function : functionMap.values()) {
descriptions.add(function.getFunctionDescription());
}
return descriptions;
}
@Override
public List<FunctionInput> getFunctionInputs() {
List<FunctionInput> allFunctionInputs = new LinkedList<>();
for (Resolver resolver : dynamicResolvers) {
allFunctionInputs.addAll(resolver.getAllVariables());
}
for (Resolver resolver : constantResolvers) {
allFunctionInputs.addAll(resolver.getAllVariables());
}
for (Resolver resolver : scopeResolvers) {
allFunctionInputs.addAll(resolver.getAllVariables());
}
return allFunctionInputs;
}
@Override
public ExpressionEvaluator getConstant(String constantName) {
Resolver resolver = getResolverWithKnowledge(constantResolvers, constantName);
if (resolver != null) {
return getExpressionEvaluator(constantName, resolver);
} else {
return null;
}
}
}