/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2009 Jaspersoft Corporation. All rights reserved.
* http://www.jaspersoft.com
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is part of JasperReports.
*
* JasperReports is free software: you can redistribute it and/or modify
* it 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.
*
* JasperReports 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.jasperreports.compilers;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.JRExpressionChunk;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.fill.JREvaluator;
import net.sf.jasperreports.engine.fill.JRFillField;
import net.sf.jasperreports.engine.fill.JRFillParameter;
import net.sf.jasperreports.engine.fill.JRFillVariable;
import net.sf.jasperreports.engine.util.JRClassLoader;
import net.sf.jasperreports.engine.util.JRStringUtil;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.ScriptableObject;
/**
* JavaScript expression evaluator.
*
* @author Lucian Chirita (lucianc@users.sourceforge.net)
* @version $Id: JavaScriptEvaluator.java 3717 2010-04-09 10:01:33Z teodord $
*/
public class JavaScriptEvaluator extends JREvaluator
{
/**
* Base JavaScript value class.
*/
public abstract static class JSValue
{
private final ScriptableObject scope;
protected JSValue(ScriptableObject scope)
{
this.scope = scope;
}
protected final Object toJSValue(Object value)
{
return Context.javaToJS(value, scope);
}
}
/**
* Parameter class used in JavaScript expressions.
*/
public static class JSParameter extends JSValue
{
private final JRFillParameter parameter;
public JSParameter(JRFillParameter parameter, ScriptableObject scope)
{
super(scope);
this.parameter = parameter;
}
public Object getValue()
{
return toJSValue(parameter.getValue());
}
}
/**
* Field class used in JavaScript expressions.
*/
public static class JSField extends JSValue
{
private final JRFillField field;
public JSField(JRFillField field, ScriptableObject scope)
{
super(scope);
this.field = field;
}
public Object getValue()
{
return toJSValue(field.getValue());
}
public Object getOldValue()
{
return toJSValue(field.getOldValue());
}
}
/**
* Variable class used in JavaScript expressions.
*/
public static class JSVariable extends JSValue
{
private final JRFillVariable variable;
public JSVariable(JRFillVariable variable, ScriptableObject scope)
{
super(scope);
this.variable = variable;
}
public Object getValue()
{
return toJSValue(variable.getValue());
}
public Object getOldValue()
{
return toJSValue(variable.getOldValue());
}
public Object getEstimatedValue()
{
return toJSValue(variable.getEstimatedValue());
}
}
protected static JavaScriptCompileData.Expression createJSExpression(JRExpression expression)
{
StringBuffer defaultExpr = new StringBuffer();
StringBuffer oldExpr = new StringBuffer();
StringBuffer estimatedExpr = new StringBuffer();
JRExpressionChunk[] chunks = expression.getChunks();
if (chunks == null)
{
defaultExpr.append("null");
oldExpr.append("null");
estimatedExpr.append("null");
}
else
{
for (int i = 0; i < chunks.length; i++)
{
JRExpressionChunk chunk = chunks[i];
switch (chunk.getType())
{
case JRExpressionChunk.TYPE_TEXT:
defaultExpr.append(chunk.getText());
oldExpr.append(chunk.getText());
estimatedExpr.append(chunk.getText());
break;
case JRExpressionChunk.TYPE_PARAMETER:
String paramName = getParameterVar(chunk.getText());
defaultExpr.append(paramName);
defaultExpr.append(".getValue()");
oldExpr.append(paramName);
oldExpr.append(".getValue()");
estimatedExpr.append(paramName);
estimatedExpr.append(".getValue()");
break;
case JRExpressionChunk.TYPE_VARIABLE:
String varName = getVariableVar(chunk.getText());
defaultExpr.append(varName);
defaultExpr.append(".getValue()");
oldExpr.append(varName);
oldExpr.append(".getOldValue()");
estimatedExpr.append(varName);
estimatedExpr.append(".getEstimatedValue()");
break;
case JRExpressionChunk.TYPE_FIELD:
String fieldName = getFieldVar(chunk.getText());
defaultExpr.append(fieldName);
defaultExpr.append(".getValue()");
oldExpr.append(fieldName);
oldExpr.append(".getOldValue()");
estimatedExpr.append(fieldName);
estimatedExpr.append(".getValue()");
break;
}
}
}
return new JavaScriptCompileData.Expression(expression.getValueClassName(),
defaultExpr.toString(), estimatedExpr.toString(), oldExpr.toString());
}
protected static String getParameterVar(String name)
{
return "param_" + JRStringUtil.getJavaIdentifier(name);
}
protected static String getVariableVar(String name)
{
return "var_" + JRStringUtil.getJavaIdentifier(name);
}
protected static String getFieldVar(String name)
{
return "field_" + JRStringUtil.getJavaIdentifier(name);
}
private final JavaScriptCompileData compileData;
private Context context;
private ScriptableObject scope;
private Map loadedTypes = new HashMap();
private Map compiledExpressions = new HashMap();
/**
* Create a JavaScript expression evaluator.
*
* @param compileData the report compile data
*/
public JavaScriptEvaluator(JavaScriptCompileData compileData)
{
this.compileData = compileData;
}
protected void customizedInit(Map parametersMap, Map fieldsMap,
Map variablesMap) throws JRException
{
context = ContextFactory.getGlobal().enterContext();//TODO exit context
context.getWrapFactory().setJavaPrimitiveWrap(false);
scope = context.initStandardObjects();
for (Iterator it = parametersMap.entrySet().iterator(); it.hasNext();)
{
Map.Entry entry = (Map.Entry) it.next();
String name = (String) entry.getKey();
JRFillParameter param = (JRFillParameter) entry.getValue();
JSParameter jsParam = new JSParameter(param, scope);
scope.put(getParameterVar(name), scope, jsParam);
}
for (Iterator it = variablesMap.entrySet().iterator(); it.hasNext();)
{
Map.Entry entry = (Map.Entry) it.next();
String name = (String) entry.getKey();
JRFillVariable var = (JRFillVariable) entry.getValue();
JSVariable jsVar = new JSVariable(var, scope);
scope.put(getVariableVar(name), scope, jsVar);
}
if (fieldsMap != null)
{
for (Iterator it = fieldsMap.entrySet().iterator(); it.hasNext();)
{
Map.Entry entry = (Map.Entry) it.next();
String name = (String) entry.getKey();
JRFillField field = (JRFillField) entry.getValue();
JSField jsField = new JSField(field, scope);
scope.put(getFieldVar(name), scope, jsField);
}
}
}
protected Object evaluate(int id) throws Throwable //NOSONAR
{
JavaScriptCompileData.Expression expression = getExpression(id);
return evaluateExpression(expression.getJavaType(),
expression.getDefaultExpression());
}
protected Object evaluateEstimated(int id) throws Throwable //NOSONAR
{
JavaScriptCompileData.Expression expression = getExpression(id);
return evaluateExpression(expression.getJavaType(),
expression.getEstimatedExpression());
}
protected Object evaluateOld(int id) throws Throwable //NOSONAR
{
JavaScriptCompileData.Expression expression = getExpression(id);
return evaluateExpression(expression.getJavaType(),
expression.getOldExpression());
}
protected JavaScriptCompileData.Expression getExpression(int id)
{
return compileData.getExpression(id);
}
protected Object evaluateExpression(String type, String expression)
{
Script compiledExpression = getCompiledExpression(expression);
Object value = compiledExpression.exec(context, scope);
Class typeClass = getTypeClass(type);
Object javaValue;
try
{
javaValue = Context.jsToJava(value, typeClass);
}
catch (EvaluatorException e)
{
throw new JRRuntimeException(e);
}
return javaValue;
}
protected Script getCompiledExpression(String expression)
{
Script compiledExpression = (Script) compiledExpressions.get(expression);
if (compiledExpression == null)
{
compiledExpression = context.compileString(expression, "expression", 0, null);
compiledExpressions.put(expression, compiledExpression);
}
return compiledExpression;
}
protected Class getTypeClass(String type)
{
Class typeClass = (Class) loadedTypes.get(type);
if (typeClass == null)
{
try
{
typeClass = JRClassLoader.loadClassForName(type);
}
catch (ClassNotFoundException e)
{
throw new JRRuntimeException("Unable to load class " + type, e);
}
loadedTypes.put(type, typeClass);
}
return typeClass;
}
}