/*
* *************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved. *
* http://esper.codehaus.org *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
* *************************************************************************************
*/
package com.espertech.esper.epl.script;
import com.asper.sources.javax.script.CompiledScript;
import com.espertech.esper.client.hook.EPLScriptContext;
import com.espertech.esper.epl.core.EngineImportException;
import com.espertech.esper.epl.expression.*;
import com.espertech.esper.epl.script.jsr223.ExpressionScriptCompiledJSR223;
import com.espertech.esper.epl.script.jsr223.JSR223Helper;
import com.espertech.esper.epl.script.mvel.ExpressionScriptCompiledMVEL;
import com.espertech.esper.epl.script.mvel.MVELHelper;
import com.espertech.esper.epl.spec.ExpressionScriptCompiled;
import com.espertech.esper.epl.spec.ExpressionScriptProvided;
import com.espertech.esper.util.JavaClassHelper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ExprNodeScript extends ExprNodeBase {
public static String CONTEXT_BINDING_NAME = "epl";
private static final long serialVersionUID = 2661218104424440161L;
private final String defaultDialect;
private final ExpressionScriptProvided script;
private final List<ExprNode> parameters;
private transient ExprEvaluator evaluator;
public ExprNodeScript(String defaultDialect, ExpressionScriptProvided script, List<ExprNode> parameters) {
this.defaultDialect = defaultDialect;
this.script = script;
this.parameters = parameters;
}
public ExprEvaluator getExprEvaluator() {
return evaluator;
}
public List<ExprNode> getParameters() {
return parameters;
}
public String toExpressionString() {
StringBuilder buffer = new StringBuilder();
buffer.append(script.getName());
ExprNodeUtility.toExpressionStringIncludeParen(parameters, buffer);
return buffer.toString();
}
public ExpressionScriptProvided getScript() {
return script;
}
public boolean isConstantResult() {
return false;
}
public boolean equalsNode(ExprNode node) {
if (this == node) return true;
if (node == null || getClass() != node.getClass()) return false;
ExprNodeScript that = (ExprNodeScript) node;
if (script != null ? !script.equals(that.script) : that.script != null) return false;
return ExprNodeUtility.deepEquals(parameters, that.parameters);
}
public void validate(ExprValidationContext validationContext) throws ExprValidationException {
if (evaluator != null) {
return;
}
if (script.getParameterNames().size() != parameters.size()) {
throw new ExprValidationException("Invalid number of parameters for script '" + script.getName() + "', expected " + script.getParameterNames().size() + " parameters but received " + parameters.size() + " parameters");
}
// validate all expression parameters
List<ExprNode> validatedParameters = new ArrayList<ExprNode>();
for (ExprNode expr : parameters) {
validatedParameters.add(ExprNodeUtility.getValidatedSubtree(expr, validationContext));
}
// set up map of input parameter names and evaluators
String[] inputParamNames = new String[script.getParameterNames().size()];
ExprEvaluator[] evaluators = new ExprEvaluator[script.getParameterNames().size()];
for (int i = 0; i < script.getParameterNames().size(); i++) {
inputParamNames[i] = script.getParameterNames().get(i);
evaluators[i] = validatedParameters.get(i).getExprEvaluator();
}
// Compile script
if (script.getCompiled() == null) {
compileScript(evaluators);
}
// Determine declared return type
Class declaredReturnType = getDeclaredReturnType(script.getOptionalReturnTypeName(), validationContext);
if (script.isOptionalReturnTypeIsArray() && declaredReturnType != null) {
declaredReturnType = JavaClassHelper.getArrayType(declaredReturnType);
}
Class returnType;
if (script.getCompiled().getKnownReturnType() == null && script.getOptionalReturnTypeName() == null) {
returnType = Object.class;
}
else if (script.getCompiled().getKnownReturnType() != null) {
if (declaredReturnType == null) {
returnType = script.getCompiled().getKnownReturnType();
}
else {
Class knownReturnType = script.getCompiled().getKnownReturnType();
if (declaredReturnType.isArray() && knownReturnType.isArray()) {
// we are fine
}
else if (!JavaClassHelper.isAssignmentCompatible(knownReturnType, declaredReturnType)) {
throw new ExprValidationException("Return type and declared type not compatible for script '" + script.getName() + "', known return type is " + knownReturnType.getName() + " versus declared return type " + declaredReturnType.getName());
}
returnType = declaredReturnType;
}
}
else {
returnType = declaredReturnType;
}
if (returnType == null) {
returnType = Object.class;
}
// Prepare evaluator - this sets the evaluator
prepareEvaluator(validationContext.getStatementName(), inputParamNames, evaluators, returnType);
}
private void compileScript(ExprEvaluator[] evaluators)
throws ExprValidationException
{
String dialect = script.getOptionalDialect() == null ? defaultDialect : script.getOptionalDialect();
ExpressionScriptCompiled compiled;
if (dialect.toLowerCase().trim().equals("mvel")) {
Map<String, Class> mvelInputParamTypes = new HashMap<String, Class>();
for (int i = 0; i < script.getParameterNames().size(); i++) {
String mvelParamName = script.getParameterNames().get(i);
mvelInputParamTypes.put(mvelParamName, evaluators[i].getType());
}
mvelInputParamTypes.put(CONTEXT_BINDING_NAME, EPLScriptContext.class);
compiled = MVELHelper.compile(script.getName(), script.getExpression(), mvelInputParamTypes);
}
else {
CompiledScript compiledScript = JSR223Helper.verifyCompileScript(script, dialect);
compiled = new ExpressionScriptCompiledJSR223(compiledScript);
}
script.setCompiled(compiled);
}
private void prepareEvaluator(String statementName, String[] inputParamNames, ExprEvaluator[] evaluators, Class returnType) {
if (script.getCompiled() instanceof ExpressionScriptCompiledMVEL) {
ExpressionScriptCompiledMVEL mvel = (ExpressionScriptCompiledMVEL) script.getCompiled();
evaluator = new ExprNodeScriptEvalMVEL(script.getName(), statementName, inputParamNames, evaluators, returnType, mvel.getCompiled());
}
else {
ExpressionScriptCompiledJSR223 jsr223 = (ExpressionScriptCompiledJSR223) script.getCompiled();
evaluator = new ExprNodeScriptEvalJSR223(script.getName(), statementName, inputParamNames, evaluators, returnType, jsr223.getCompiled());
}
}
private Class getDeclaredReturnType(String returnTypeName, ExprValidationContext validationContext)
throws ExprValidationException
{
if (returnTypeName == null) {
return null;
}
if (returnTypeName.equals("void")) {
return null;
}
Class returnType = JavaClassHelper.getClassForSimpleName(returnTypeName);
if (returnType != null) {
return returnType;
}
try {
return validationContext.getMethodResolutionService().resolveClass(returnTypeName);
}
catch (EngineImportException e1) {
throw new ExprValidationException("Failed to resolve return type '" + returnTypeName + "' specified for script '" + script.getName() + "'");
}
}
}