/*
* BeanShellFacade.java - A BeanShell facade
* :tabSize=4:indentSize=4:noTabs=false:
* :folding=explicit:collapseFolds=1:
*
* Copyright (C) 2007 Matthieu Casanova
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.gjt.sp.jedit;
//{{{ Imports
import java.lang.reflect.InvocationTargetException;
import org.gjt.sp.jedit.bsh.BshClassManager;
import org.gjt.sp.jedit.bsh.BshMethod;
import org.gjt.sp.jedit.bsh.CallStack;
import org.gjt.sp.jedit.bsh.Interpreter;
import org.gjt.sp.jedit.bsh.NameSpace;
import org.gjt.sp.jedit.bsh.Primitive;
import org.gjt.sp.jedit.bsh.TargetError;
import org.gjt.sp.jedit.bsh.UtilEvalError;
import org.gjt.sp.jedit.bsh.classpath.ClassManagerImpl;
import org.gjt.sp.jedit.textarea.TextArea;
import org.gjt.sp.util.Log;
//}}}
/**
* This class will be the interface for beanshell interaction.
* In jEdit it will be used with the static methods of {@link BeanShell}
* @author Matthieu Casanova
* @since jEdit 4.3pre13
*/
public abstract class BeanShellFacade<T>
{
//{{{ BeanShellFacade constructor
protected BeanShellFacade()
{
classManager = new ClassManagerImpl();
global = new NameSpace(classManager,
"jEdit embedded BeanShell interpreter");
interpForMethods = createInterpreter(global);
init();
} //}}}
//{{{ init() method
/**
* Initialize things. It is called by the constructor.
* You can override it to import other packages
*/
protected void init()
{
global.importPackage("org.gjt.sp.jedit");
global.importPackage("org.gjt.sp.jedit.buffer");
global.importPackage("org.gjt.sp.jedit.syntax");
global.importPackage("org.gjt.sp.jedit.textarea");
global.importPackage("org.gjt.sp.util");
} //}}}
//{{{ evalSelection() method
/**
* Evaluates the text selected in the specified text area.
* @param param some sort of parameter
* @param textArea the textArea
*/
public void evalSelection(T param, TextArea textArea)
{
String command = textArea.getSelectedText();
if(command == null)
{
javax.swing.UIManager.getLookAndFeel().provideErrorFeedback(null);
return;
}
Object returnValue = eval(param,global,command);
if(returnValue != null)
textArea.setSelectedText(returnValue.toString());
} //}}}
//{{{ eval() method
/**
* Evaluates the specified BeanShell expression with the global namespace
* @param param The parameter
* @param command The expression
* @return an object
*/
public Object eval(T param, String command)
{
return eval(param, global, command);
} //}}}
//{{{ eval() method
/**
* Evaluates the specified BeanShell expression. Errors are reported in
* a dialog box.
* @param param The parameter
* @param namespace The namespace
* @param command The expression
* @return an object
*/
public Object eval(T param, NameSpace namespace, String command)
{
try
{
return _eval(param,namespace,command);
}
catch(Throwable e)
{
Log.log(Log.ERROR,BeanShellFacade.class,e);
handleException(param,null,e);
}
return null;
} //}}}
//{{{ _eval() method
/**
* Evaluates the specified BeanShell expression. Unlike
* <code>eval()</code>, this method passes any exceptions to the caller.
*
* @param view The view. Within the script, references to
* <code>buffer</code>, <code>textArea</code> and <code>editPane</code>
* are determined with reference to this parameter.
* @param namespace The namespace
* @param command The expression
* @return an object
* @exception Exception instances are thrown when various BeanShell
* errors occur
*/
public Object _eval(T view, NameSpace namespace, String command)
throws Exception
{
Interpreter interp = createInterpreter(namespace);
try
{
setupDefaultVariables(namespace,view);
if(Debug.BEANSHELL_DEBUG)
Log.log(Log.DEBUG,BeanShellFacade.class,command);
return interp.eval(command);
}
catch(Exception e)
{
unwrapException(e);
// never called
return null;
}
finally
{
try
{
resetDefaultVariables(namespace);
}
catch(UtilEvalError e)
{
// do nothing
}
}
} //}}}
//{{{ cacheBlock() method
/**
* Caches a block of code, returning a handle that can be passed to
* runCachedBlock().
* @param id An identifier.
* @param code The code
* @param namespace If true, the namespace will be set
* @return a hsh method
* @exception Exception instances are thrown when various BeanShell errors
* occur
*/
public BshMethod cacheBlock(String id, String code, boolean namespace)
throws Exception
{
// Make local namespace so that the method could be GCed
// if it becomes unnecessary.
NameSpace local = new NameSpace(global, "__internal_" + id);
// This name should be unique enough not to shadow any outer
// identifier.
String name = "__runCachedMethod";
if(namespace)
{
_eval(null,local,name + "(ns) {\nthis.callstack.set(0,ns);\n" + code + "\n}");
return local.getMethod(name,new Class[] { NameSpace.class });
}
else
{
_eval(null,local,name + "() {\n" + code + "\n}");
return local.getMethod(name,new Class[0]);
}
} //}}}
//{{{ runCachedBlock() method
/**
* Runs a cached block of code in the specified namespace. Faster than
* evaluating the block each time.
* @param method The method instance returned by cacheBlock()
* @param param a parameter
* @param namespace The namespace to run the code in
* @return an object
* @exception Exception instances are thrown when various BeanShell
* errors occur
*/
public Object runCachedBlock(BshMethod method, T param,
NameSpace namespace) throws Exception
{
boolean useNamespace;
if(namespace == null)
{
useNamespace = false;
namespace = global;
}
else
useNamespace = true;
try
{
setupDefaultVariables(namespace,param);
Object retVal = method.invoke(useNamespace
? new Object[] { namespace }
: NO_ARGS,
interpForMethods,new CallStack(), null);
if(retVal instanceof Primitive)
{
if(retVal == Primitive.VOID)
return null;
else
return ((Primitive)retVal).getValue();
}
else
return retVal;
}
catch(Exception e)
{
return null;
}
finally
{
resetDefaultVariables(namespace);
}
} //}}}
//{{{ getNameSpace() method
/**
* @return the global namespace.
*/
public NameSpace getNameSpace()
{
return global;
} //}}}
//{{{ resetClassManager() method
/**
* Causes BeanShell internal structures to drop references to cached
* Class instances.
*/
void resetClassManager()
{
classManager.reset();
} //}}}
//{{{ setVariable() method
/**
* Set a beanshell variable in the namespace without overriding it
* @param nameSpace the namespace
* @param name the name of the variable
* @param object the value of the variable
* @throws UtilEvalError when there is an error
*/
protected void setVariable(NameSpace nameSpace, String name, Object object) throws UtilEvalError
{
if (nameSpace.getVariable(name) == Primitive.VOID)
nameSpace.setVariable(name,object, false);
} //}}}
//{{{ setupDefaultVariables() method
protected abstract void setupDefaultVariables(NameSpace namespace, T param)
throws UtilEvalError;
//}}}
//{{{ resetDefaultVariables() method
protected abstract void resetDefaultVariables(NameSpace namespace)
throws UtilEvalError;
//}}}
//{{{ handleException() method
protected abstract void handleException(T param, String path, Throwable t);
//}}}
//{{{ createInterpreter() method
protected static Interpreter createInterpreter(NameSpace nameSpace)
{
return new Interpreter(null,System.out,System.err,false,nameSpace);
} //}}}
//{{{ unwrapException() method
/**
* This extracts an exception from a 'wrapping' exception, as BeanShell
* sometimes throws. This gives the user a more accurate error traceback
* @param e the exception
* @throws Exception on error
*/
protected static void unwrapException(Exception e) throws Exception
{
if(e instanceof TargetError)
{
Throwable t = ((TargetError)e).getTarget();
if(t instanceof Exception)
throw (Exception)t;
else if(t instanceof Error)
throw (Error)t;
}
if(e instanceof InvocationTargetException)
{
Throwable t = ((InvocationTargetException)e).getTargetException();
if(t instanceof Exception)
throw (Exception)t;
else if(t instanceof Error)
throw (Error)t;
}
throw e;
} //}}}
//{{{ Static variables
protected NameSpace global;
protected BshClassManager classManager;
private static Interpreter interpForMethods;
private static final Object[] NO_ARGS = new Object[0];
//}}}
}