PageRenderTime 148ms CodeModel.GetById 128ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/BeanShellFacade.java

#
Java | 338 lines | 188 code | 27 blank | 123 comment | 21 complexity | 10505b8972d618a9559dd74f84ade38b MD5 | raw file
  1/*
  2 * BeanShellFacade.java - A BeanShell facade
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2007 Matthieu Casanova
  7 *
  8 * This program is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU General Public License
 10 * as published by the Free Software Foundation; either version 2
 11 * of the License, or any later version.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU General Public License
 19 * along with this program; if not, write to the Free Software
 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 21 */
 22
 23package org.gjt.sp.jedit;
 24
 25//{{{ Imports
 26import java.lang.reflect.InvocationTargetException;
 27import org.gjt.sp.jedit.bsh.BshClassManager;
 28import org.gjt.sp.jedit.bsh.BshMethod;
 29import org.gjt.sp.jedit.bsh.CallStack;
 30import org.gjt.sp.jedit.bsh.Interpreter;
 31import org.gjt.sp.jedit.bsh.NameSpace;
 32import org.gjt.sp.jedit.bsh.Primitive;
 33import org.gjt.sp.jedit.bsh.TargetError;
 34import org.gjt.sp.jedit.bsh.UtilEvalError;
 35import org.gjt.sp.jedit.bsh.classpath.ClassManagerImpl;
 36import org.gjt.sp.jedit.textarea.TextArea;
 37import org.gjt.sp.util.Log;
 38//}}}
 39
 40/**
 41 * This class will be the interface for beanshell interaction.
 42 * In jEdit it will be used with the static methods of {@link BeanShell}
 43 * @author Matthieu Casanova
 44 * @since jEdit 4.3pre13
 45 */
 46public abstract class BeanShellFacade<T>
 47{
 48	//{{{ BeanShellFacade constructor
 49	protected BeanShellFacade()
 50	{
 51		classManager = new ClassManagerImpl();
 52		global = new NameSpace(classManager,
 53			"jEdit embedded BeanShell interpreter");
 54
 55		interpForMethods = createInterpreter(global);
 56		init();
 57	} //}}}
 58
 59	//{{{ init() method
 60	/**
 61	 * Initialize things. It is called by the constructor.
 62	 * You can override it to import other packages
 63	 */
 64	protected void init()
 65	{
 66		global.importPackage("org.gjt.sp.jedit");
 67		global.importPackage("org.gjt.sp.jedit.buffer");
 68		global.importPackage("org.gjt.sp.jedit.syntax");
 69		global.importPackage("org.gjt.sp.jedit.textarea");
 70		global.importPackage("org.gjt.sp.util");
 71	} //}}}
 72
 73	//{{{ evalSelection() method
 74	/**
 75	 * Evaluates the text selected in the specified text area.
 76	 */
 77	public void evalSelection(T param, TextArea textArea)
 78	{
 79		String command = textArea.getSelectedText();
 80		if(command == null)
 81		{
 82			textArea.getToolkit().beep();
 83			return;
 84		}
 85		Object returnValue = eval(param,global,command);
 86		if(returnValue != null)
 87			textArea.setSelectedText(returnValue.toString());
 88	} //}}}
 89
 90	//{{{ eval() method
 91	/**
 92	 * Evaluates the specified BeanShell expression with the global namespace
 93	 * @param param The parameter
 94	 * @param command The expression
 95	 */
 96	public Object eval(T param, String command)
 97	{
 98		return eval(param, global, command);
 99	} //}}}
100
101	//{{{ eval() method
102	/**
103	 * Evaluates the specified BeanShell expression. Errors are reported in
104	 * a dialog box.
105	 * @param param The parameter
106	 * @param namespace The namespace
107	 * @param command The expression
108	 */
109	public Object eval(T param, NameSpace namespace, String command)
110	{
111		try
112		{
113			return _eval(param,namespace,command);
114		}
115		catch(Throwable e)
116		{
117			Log.log(Log.ERROR,BeanShellFacade.class,e);
118
119			handleException(param,null,e);
120		}
121
122		return null;
123	} //}}}
124
125	//{{{ _eval() method
126	/**
127	 * Evaluates the specified BeanShell expression. Unlike
128	 * <code>eval()</code>, this method passes any exceptions to the caller.
129	 *
130	 * @param view The view. Within the script, references to
131	 * <code>buffer</code>, <code>textArea</code> and <code>editPane</code>
132	 * are determined with reference to this parameter.
133	 * @param namespace The namespace
134	 * @param command The expression
135	 * @exception Exception instances are thrown when various BeanShell
136	 * errors occur
137	 */
138	public Object _eval(T view, NameSpace namespace, String command)
139		throws Exception
140	{
141		Interpreter interp = createInterpreter(namespace);
142
143		try
144		{
145			setupDefaultVariables(namespace,view);
146			if(Debug.BEANSHELL_DEBUG)
147				Log.log(Log.DEBUG,BeanShellFacade.class,command);
148			return interp.eval(command);
149		}
150		catch(Exception e)
151		{
152			unwrapException(e);
153			// never called
154			return null;
155		}
156		finally
157		{
158			try
159			{
160				resetDefaultVariables(namespace);
161			}
162			catch(UtilEvalError e)
163			{
164				// do nothing
165			}
166		}
167	} //}}}
168
169	//{{{ cacheBlock() method
170	/**
171	 * Caches a block of code, returning a handle that can be passed to
172	 * runCachedBlock().
173	 * @param id An identifier.
174	 * @param code The code
175	 * @param namespace If true, the namespace will be set
176	 * @exception Exception instances are thrown when various BeanShell errors
177	 * occur
178	 */
179	public BshMethod cacheBlock(String id, String code, boolean namespace)
180		throws Exception
181	{
182		// Make local namespace so that the method could be GCed
183		// if it becomes unnecessary.
184		NameSpace local = new NameSpace(global, "__internal_" + id);
185		// This name should be unique enough not to shadow any outer
186		// identifier.
187		String name = "__runCachedMethod";
188		if(namespace)
189		{
190			_eval(null,local,name + "(ns) {\nthis.callstack.set(0,ns);\n" + code + "\n}");
191			return local.getMethod(name,new Class[] { NameSpace.class });
192		}
193		else
194		{
195			_eval(null,local,name + "() {\n" + code + "\n}");
196			return local.getMethod(name,new Class[0]);
197		}
198	} //}}}
199
200	//{{{ runCachedBlock() method
201	/**
202	 * Runs a cached block of code in the specified namespace. Faster than
203	 * evaluating the block each time.
204	 * @param method The method instance returned by cacheBlock()
205	 * @param namespace The namespace to run the code in
206	 * @exception Exception instances are thrown when various BeanShell
207	 * errors occur
208	 */
209	public Object runCachedBlock(BshMethod method, T param,
210		NameSpace namespace) throws Exception
211	{
212		boolean useNamespace;
213		if(namespace == null)
214		{
215			useNamespace = false;
216			namespace = global;
217		}
218		else
219			useNamespace = true;
220
221		try
222		{
223			setupDefaultVariables(namespace,param);
224
225			Object retVal = method.invoke(useNamespace
226				? new Object[] { namespace }
227				: NO_ARGS,
228				interpForMethods,new CallStack(), null);
229			if(retVal instanceof Primitive)
230			{
231				if(retVal == Primitive.VOID)
232					return null;
233				else
234					return ((Primitive)retVal).getValue();
235			}
236			else
237				return retVal;
238		}
239		catch(Exception e)
240		{
241			unwrapException(e);
242			// never called
243			return null;
244		}
245		finally
246		{
247			resetDefaultVariables(namespace);
248		}
249	} //}}}
250
251	//{{{ getNameSpace() method
252	/**
253	 * Returns the global namespace.
254	 */
255	public NameSpace getNameSpace()
256	{
257		return global;
258	} //}}}
259
260	//{{{ resetClassManager() method
261	/**
262	 * Causes BeanShell internal structures to drop references to cached
263	 * Class instances.
264	 */
265	void resetClassManager()
266	{
267		classManager.reset();
268	} //}}}
269
270	//{{{ setVariable() method
271	/**
272	 * Set a beanshell variable in the namespace without overriding it
273	 * @param nameSpace the namespace
274	 * @param name the name of the variable
275	 * @param object the value of the variable
276	 * @throws UtilEvalError
277	 */
278	protected void setVariable(NameSpace nameSpace, String name, Object object) throws UtilEvalError
279	{
280		if (nameSpace.getVariable(name) == Primitive.VOID)
281			nameSpace.setVariable(name,object, false);
282	} //}}}
283
284	//{{{ setupDefaultVariables() method
285	protected abstract void setupDefaultVariables(NameSpace namespace, T param)
286		throws UtilEvalError;
287	//}}}
288
289	//{{{ resetDefaultVariables() method
290	protected abstract void resetDefaultVariables(NameSpace namespace)
291		throws UtilEvalError;
292	//}}}
293
294	//{{{ handleException() method
295	protected abstract void handleException(T param, String path, Throwable t);
296	//}}}
297
298	//{{{ createInterpreter() method
299	protected static Interpreter createInterpreter(NameSpace nameSpace)
300	{
301		return new Interpreter(null,System.out,System.err,false,nameSpace);
302	} //}}}
303
304	//{{{ unwrapException() method
305	/**
306	 * This extracts an exception from a 'wrapping' exception, as BeanShell
307	 * sometimes throws. This gives the user a more accurate error traceback
308	 */
309	protected static void unwrapException(Exception e) throws Exception
310	{
311		if(e instanceof TargetError)
312		{
313			Throwable t = ((TargetError)e).getTarget();
314			if(t instanceof Exception)
315				throw (Exception)t;
316			else if(t instanceof Error)
317				throw (Error)t;
318		}
319
320		if(e instanceof InvocationTargetException)
321		{
322			Throwable t = ((InvocationTargetException)e).getTargetException();
323			if(t instanceof Exception)
324				throw (Exception)t;
325			else if(t instanceof Error)
326				throw (Error)t;
327		}
328
329		throw e;
330	} //}}}
331
332	//{{{ Static variables
333	protected NameSpace global;
334	protected BshClassManager classManager;
335	private static Interpreter interpForMethods;
336	private static final Object[] NO_ARGS = new Object[0];
337	//}}}
338}