package net.xoetrope.optional.scripts;
import java.lang.reflect.Method;
import net.xoetrope.debug.DebugLogger;
import net.xoetrope.xui.XProject;
import net.xoetrope.xui.build.BuildProperties;
import java.util.Hashtable;
import net.xoetrope.xui.XMethodReference;
import net.xoetrope.xui.evaluator.XAttributeEvaluator;
import net.xoetrope.xui.exception.XExceptionHandler;
/**
* Evaluates a script attribute by passing the scripts
* following the prefix 'script:' to the ScriptEngine.
* The scripts can be an a function that was predifined
* in the page Scripts tag or a script that is just being
* defined.
* eg. <code>script:myFunction();</code> given that myFunction
* was define in the page Scripts tag.
* eg. <code>script:out.println('Hello World');</code> the script
* is just being defined.
* <p>Copyright (c) Xoetrope Ltd., 2002-2007</p>
* <p>License: see license.txt</p>
*/
public class ScriptAttributeEvaluator implements XAttributeEvaluator
{
/**
* The collection of class instances that are known to implement the methods
* of evaluated attributes
*/
protected Hashtable classInstances;
protected Object instance;
protected XExceptionHandler exceptionHandler;
protected Object result;
protected XProject currentProject;
protected ScriptEngine scriptEngine;
/**
* Create a new instance of the evaluator
*/
public ScriptAttributeEvaluator( )
{
}
/**
* Set the current project and complete any initialization that depends on the
* project reference/instance.
* @param project the current or owning project
*/
public void setCurrentProject( XProject project )
{
currentProject = project;
classInstances = (Hashtable)currentProject.getObject( "UserClassReferences" );
if ( classInstances == null ) {
classInstances = new Hashtable( 5 );
currentProject.setObject( "UserClassReferences", classInstances );
}
scriptEngine = (ScriptEngine)currentProject.getObject( "ScriptEngine" );
if ( scriptEngine == null ) {
String scriptEngineClass = project.getStartupParam( "ScriptEngine" );
try {
if (( scriptEngineClass == null ) || ( scriptEngineClass.length() == 0 ))
scriptEngineClass = "net.xoetrope.optional.scripts.JavaScriptEngine";
scriptEngine = (ScriptEngine)Class.forName( scriptEngineClass ).newInstance();
scriptEngine.setProject( project );
currentProject.setObject( "ScriptEngine", scriptEngine );
}
catch ( Exception e )
{
e.printStackTrace();
}
}
}
/**
* Get the value of an attribute.
* @param page the page being loaded
* @param attributeValue the raw value of the attribute
* @return the evaluated value of the attribute
*/
public Object evaluateAttribute( Object instance, String attributeValue )
{
this.instance = instance;
result = attributeValue;
try {
if ( attributeValue.startsWith( "${script." )) {
String arg = attributeValue.substring( "${script.".length(), attributeValue.length() -1 );
result = scriptEngine.executeScript( arg );
}
}
catch ( Exception ex ) {
if ( exceptionHandler != null ) {
if ( exceptionHandler.handleException( instance, ex, this ))
return result;
}
if ( BuildProperties.DEBUG )
DebugLogger.logWarning( "Unable to evaluate script: " + attributeValue );
}
return result;
}
/**
* Get the value of an attribute by evaluating a method reference
*/
public XMethodReference getMethodReference( String attributeValue )
{
return getMethodReference( null, attributeValue );
}
/**
* Get the value of an attribute by evaluating a method reference
*/
public XMethodReference getMethodReference( Object instance, String attributeValue )
{
try {
if ( attributeValue.startsWith( "${script." )) {
String methodName = "executeScript";
String arg = attributeValue.substring( "${script.".length(), attributeValue.length() -1 );
Object args[] = new Object[]{ arg };
Class[] params = new Class[]{ String.class };
// Find the class that implements the method
Class clazz = scriptEngine.getClass();
// Find and invoke the method
Method theMethod = clazz.getMethod( methodName, params );
return new XMethodReference( clazz, scriptEngine, theMethod, args );
}
}
catch ( Exception ex ) {
if ( BuildProperties.DEBUG )
DebugLogger.logWarning( "Unable to evaluate script: " + attributeValue );
}
return null;
}
/**
* Get the current page. In most cases this should correspond to the active
* page at the time the evaluator was last used, but no guarantees can be provided
* that multiple threads are not updating the value. The value should be retrieved
* and cached as soon as the evaluated method is entered as other method calls
* could have the side effect of reseting the value.
* @return the current page object
*/
public Object getObject()
{
return instance;
}
/**
* Set an exception handler for processing exceptions
* @param eh the exception handler
*/
public void setExceptionHandler( XExceptionHandler eh )
{
exceptionHandler = eh;
}
/**
* Explicitly set the result of an evaluation. This method may be used
* by an exception handler to override the result and set a sensible
* value in case of an exception
* @param the new result value
*/
public void setResult( Object value )
{
result = value;
}
/**
* Explicitly get the result of an evaluation. This method may be used
* by an exception handler to determine the result and set a sensible
* value in case of an exception
* @return the current/intermediate result value
*/
public Object getResult()
{
return result;
}
}