PageRenderTime 142ms CodeModel.GetById 30ms app.highlight 51ms RepoModel.GetById 54ms app.codeStats 1ms

/jEdit/tags/jedit-4-0-pre3/org/gjt/sp/jedit/BeanShell.java

#
Java | 551 lines | 351 code | 69 blank | 131 comment | 55 complexity | bc20eb6a162d63b57615273e066608b4 MD5 | raw file
  1/*
  2 * BeanShell.java - BeanShell scripting support
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2000, 2001 Slava Pestov
  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 bsh.*;
 27import javax.swing.text.Segment;
 28import javax.swing.JFileChooser;
 29import java.lang.reflect.InvocationTargetException;
 30import java.io.*;
 31import org.gjt.sp.jedit.io.*;
 32import org.gjt.sp.jedit.gui.BeanShellErrorDialog;
 33import org.gjt.sp.jedit.textarea.*;
 34import org.gjt.sp.util.Log;
 35//}}}
 36
 37public class BeanShell
 38{
 39	//{{{ evalSelection() method
 40	/**
 41	 * Evaluates the text selected in the specified text area.
 42	 * @since jEdit 2.7pre2
 43	 */
 44	public static void evalSelection(View view, JEditTextArea textArea)
 45	{
 46		String command = textArea.getSelectedText();
 47		if(command == null)
 48		{
 49			view.getToolkit().beep();
 50			return;
 51		}
 52		Object returnValue = eval(view,command,false);
 53		if(returnValue != null)
 54			textArea.setSelectedText(returnValue.toString());
 55	} //}}}
 56
 57	//{{{ showEvaluateDialog() method
 58	/**
 59	 * Prompts for a BeanShell expression to evaluate.
 60	 * @since jEdit 2.7pre2
 61	 */
 62	public static void showEvaluateDialog(View view)
 63	{
 64		String command = GUIUtilities.input(view,"beanshell-eval-input",null);
 65		if(command != null)
 66		{
 67			if(!command.endsWith(";"))
 68				command = command + ";";
 69
 70			int repeat = view.getInputHandler().getRepeatCount();
 71
 72			if(view.getMacroRecorder() != null)
 73			{
 74				view.getMacroRecorder().record(repeat,command);
 75			}
 76
 77			Object returnValue = null;
 78			try
 79			{
 80				for(int i = 0; i < repeat; i++)
 81				{
 82					returnValue = eval(view,command,true);
 83				}
 84			}
 85			catch(Throwable t)
 86			{
 87				// BeanShell error occurred, abort execution
 88			}
 89
 90			if(returnValue != null)
 91			{
 92				String[] args = { returnValue.toString() };
 93				GUIUtilities.message(view,"beanshell-eval",args);
 94			}
 95		}
 96	} //}}}
 97
 98	//{{{ showEvaluateLinesDialog() method
 99	/**
100	 * Evaluates the specified script for each selected line.
101	 * @since jEdit 4.0pre1
102	 */
103	public static void showEvaluateLinesDialog(View view)
104	{
105		String command = GUIUtilities.input(view,"beanshell-eval-line",null);
106		if(command != null)
107		{
108			if(!command.endsWith(";"))
109				command = command + ";";
110
111			if(view.getMacroRecorder() != null)
112				view.getMacroRecorder().record(1,command);
113
114			JEditTextArea textArea = view.getTextArea();
115			Buffer buffer = view.getBuffer();
116
117			try
118			{
119				buffer.beginCompoundEdit();
120
121				Selection[] selection = textArea.getSelection();
122				for(int i = 0; i < selection.length; i++)
123				{
124					Selection s = selection[i];
125					for(int j = s.getStartLine(); j <= s.getEndLine(); j++)
126					{
127						// if selection ends on the start of a
128						// line, don't filter that line
129						if(s.getEnd() == textArea.getLineStartOffset(j))
130							break;
131
132						global.setVariable("line",new Integer(j));
133						global.setVariable("index",new Integer(
134							j - s.getStartLine()));
135						int start = s.getStart(buffer,j);
136						int end = s.getEnd(buffer,j);
137						String text = buffer.getText(start,
138							end - start);
139						global.setVariable("text",text);
140
141						Object returnValue = eval(view,command,true);
142						if(returnValue != null)
143						{
144							buffer.remove(start,end - start);
145							buffer.insert(start,
146								returnValue.toString());
147						}
148					}
149				}
150			}
151			catch(Throwable e)
152			{
153				// BeanShell error occurred, abort execution
154			}
155			finally
156			{
157				buffer.endCompoundEdit();
158			}
159
160			textArea.selectNone();
161		}
162	} //}}}
163
164	//{{{ showRunScriptDialog() method
165	/**
166	 * Prompts for a BeanShell script to run.
167	 * @since jEdit 2.7pre2
168	 */
169	public static void showRunScriptDialog(View view)
170	{
171		String[] paths = GUIUtilities.showVFSFileDialog(view,
172			null,JFileChooser.OPEN_DIALOG,true);
173		if(paths != null)
174		{
175			Buffer buffer = view.getBuffer();
176			try
177			{
178				buffer.beginCompoundEdit();
179
180				for(int i = 0; i < paths.length; i++)
181					runScript(view,paths[i],true,false);
182			}
183			finally
184			{
185				buffer.endCompoundEdit();
186			}
187		}
188	} //}}}
189
190	//{{{ runScript() method
191	/**
192	 * Runs a BeanShell script.
193	 * @param view The view
194	 * @param path The path name of the script. May be a jEdit VFS path
195	 * @param ownNamespace Macros are run in their own namespace, startup
196	 * scripts are run on the global namespace
197	 * @param rethrowBshErrors Rethrow BeanShell errors, in addition to
198	 * showing an error dialog box
199	 * @since jEdit 2.7pre3
200	 */
201	public static void runScript(View view, String path,
202		boolean ownNamespace, boolean rethrowBshErrors)
203	{
204		Reader in;
205		Buffer buffer = jEdit.getBuffer(path);
206
207		VFS vfs = VFSManager.getVFSForPath(path);
208		Object session = vfs.createVFSSession(path,view);
209		if(session == null)
210		{
211			// user cancelled???
212			return;
213		}
214
215		try
216		{
217			if(buffer != null)
218			{
219				if(!buffer.isLoaded())
220					VFSManager.waitForRequests();
221
222				in = new StringReader(buffer.getText(0,
223					buffer.getLength()));
224			}
225			else
226			{
227				in = new BufferedReader(new InputStreamReader(
228					vfs._createInputStream(session,path,
229					true,view)));
230			}
231
232			runScript(view,path,in,ownNamespace,rethrowBshErrors);
233		}
234		catch(IOException e)
235		{
236			Log.log(Log.ERROR,BeanShell.class,e);
237			GUIUtilities.error(view,"read-error",
238				new String[] { path, e.toString() });
239			return;
240		}
241		finally
242		{
243			try
244			{
245				vfs._endVFSSession(session,view);
246			}
247			catch(IOException io)
248			{
249				Log.log(Log.ERROR,BeanShell.class,io);
250				GUIUtilities.error(view,"read-error",
251					new String[] { path, io.toString() });
252			}
253		}
254	} //}}}
255
256	//{{{ runScript() method
257	/**
258	 * Runs a BeanShell script.
259	 * @param view The view
260	 * @param path For error reporting only
261	 * @param in The reader to read the script from
262	 * @param ownNamespace Macros are run in their own namespace, startup
263	 * scripts are run on the global namespace
264	 * @param rethrowBshErrors Rethrow BeanShell errors, in addition to
265	 * showing an error dialog box
266	 * @since jEdit 3.2pre4
267	 */
268	public static void runScript(View view, String path, Reader in,
269		boolean ownNamespace, boolean rethrowBshErrors)
270	{
271		Log.log(Log.MESSAGE,BeanShell.class,"Running script " + path);
272
273		NameSpace namespace;
274		if(ownNamespace)
275			namespace = new NameSpace(global,"script namespace");
276		else
277			namespace = global;
278
279		Interpreter interp = createInterpreter(namespace);
280
281		try
282		{
283			if(view != null)
284			{
285				EditPane editPane = view.getEditPane();
286				interp.set("view",view);
287				interp.set("editPane",editPane);
288				interp.set("buffer",editPane.getBuffer());
289				interp.set("textArea",editPane.getTextArea());
290			}
291
292			running = true;
293
294			interp.eval(in,namespace,path);
295		}
296		catch(Throwable e)
297		{
298			if(e instanceof TargetError)
299				e = ((TargetError)e).getTarget();
300
301			if(e instanceof InvocationTargetException)
302				e = ((InvocationTargetException)e).getTargetException();
303
304			Log.log(Log.ERROR,BeanShell.class,e);
305
306			new BeanShellErrorDialog(view,e.toString());
307
308			if(e instanceof Error && rethrowBshErrors)
309				throw (Error)e;
310		}
311		finally
312		{
313			running = false;
314		}
315	} //}}}
316
317	//{{{ eval() method
318	/**
319	 * Evaluates the specified BeanShell expression.
320	 * @param view The view (may be null)
321	 * @param command The expression
322	 * @param rethrowBshErrors If true, BeanShell errors will
323	 * be re-thrown to the caller
324	 * @since jEdit 2.7pre3
325	 */
326	public static Object eval(View view, String command,
327		boolean rethrowBshErrors)
328	{
329		return eval(view,global,command,rethrowBshErrors);
330	} //}}}
331
332	//{{{ eval() method
333	/**
334	 * Evaluates the specified BeanShell expression.
335	 * @param view The view (may be null)
336	 * @param namespace The namespace
337	 * @param command The expression
338	 * @param rethrowBshErrors If true, BeanShell errors will
339	 * be re-thrown to the caller
340	 * @since jEdit 3.2pre7
341	 */
342	public static Object eval(View view, NameSpace namespace,
343		String command, boolean rethrowBshErrors)
344	{
345		Interpreter interp = createInterpreter(namespace);
346
347		try
348		{
349			if(view != null)
350			{
351				EditPane editPane = view.getEditPane();
352				interp.set("view",view);
353				interp.set("editPane",editPane);
354				interp.set("buffer",editPane.getBuffer());
355				interp.set("textArea",editPane.getTextArea());
356			}
357
358			return interp.eval(command);
359		}
360		catch(Throwable e)
361		{
362			if(e instanceof TargetError)
363				e = ((TargetError)e).getTarget();
364
365			if(e instanceof InvocationTargetException)
366				e = ((InvocationTargetException)e).getTargetException();
367
368			Log.log(Log.ERROR,BeanShell.class,e);
369
370			new BeanShellErrorDialog(view,e.toString());
371
372			if(e instanceof Error && rethrowBshErrors)
373				throw (Error)e;
374		}
375
376		return null;
377	} //}}}
378
379	//{{{ cacheBlock() method
380	/**
381	 * Caches a block of code, returning a handle that can be passed to
382	 * runCachedBlock().
383	 * @param id An identifier. If null, a unique identifier is generated
384	 * @param code The code
385	 * @param childNamespace If the method body should be run in a new
386	 * namespace (slightly faster). Note that you must pass a null namespace
387	 * to the runCachedBlock() method if you do this
388	 * @since jEdit 3.2pre5
389	 */
390	public static String cacheBlock(String id, String code, boolean childNamespace)
391	{
392		String name;
393		if(id == null)
394			name = "b_" + (cachedBlockCounter++);
395		else
396			name = "b_" + id;
397
398		code = "setNameSpace(__cruft.namespace);\n"
399			+ name
400			+ "(ns) {\n"
401			+ "setNameSpace(ns);"
402			+ code
403			+ "\n}";
404
405		eval(null,code,false);
406
407		return name;
408	} //}}}
409
410	//{{{ runCachedBlock() method
411	/**
412	 * Runs a cached block of code in the specified namespace. Faster than
413	 * evaluating the block each time.
414	 * @param id The identifier returned by cacheBlock()
415	 * @param view The view
416	 * @param namespace The namespace to run the code in. Can only be null if
417	 * childNamespace parameter was true in cacheBlock() call
418	 * @since jEdit 3.2pre5
419	 */
420	public static Object runCachedBlock(String id, View view, NameSpace namespace)
421	{
422		if(namespace == null)
423			namespace = global;
424
425		Object[] args = { namespace };
426
427		try
428		{
429			if(view != null)
430			{
431				namespace.setVariable("view",view);
432				EditPane editPane = view.getEditPane();
433				namespace.setVariable("editPane",editPane);
434				namespace.setVariable("buffer",editPane.getBuffer());
435				namespace.setVariable("textArea",editPane.getTextArea());
436			}
437
438			Object retVal = internal.invokeMethod(id,args,interpForMethods);
439			if(retVal instanceof Primitive)
440			{
441				if(retVal == Primitive.VOID)
442					return null;
443				else
444					return ((Primitive)retVal).getValue();
445			}
446			else
447				return retVal;
448		}
449		catch(Throwable e)
450		{
451			if(e instanceof TargetError)
452				e = ((TargetError)e).getTarget();
453
454			if(e instanceof InvocationTargetException)
455				e = ((InvocationTargetException)e).getTargetException();
456
457			Log.log(Log.ERROR,BeanShell.class,e);
458
459			new BeanShellErrorDialog(view,e.toString());
460		}
461		finally
462		{
463			try
464			{
465				namespace.setVariable("view",null);
466				namespace.setVariable("editPane",null);
467				namespace.setVariable("buffer",null);
468				namespace.setVariable("textArea",null);
469			}
470			catch(EvalError e)
471			{
472				// can't do much
473			}
474		}
475
476		return null;
477	} //}}}
478
479	//{{{ isScriptRunning() method
480	/**
481	 * Returns if a BeanShell script or macro is currently running.
482	 * @since jEdit 2.7pre2
483	 */
484	public static boolean isScriptRunning()
485	{
486		return running;
487	} //}}}
488
489	//{{{ getNameSpace() method
490	/**
491	 * Returns the global namespace.
492	 * @since jEdit 3.2pre5
493	 */
494	public static NameSpace getNameSpace()
495	{
496		return global;
497	} //}}}
498
499	//{{{ Package-private members
500
501	//{{{ init() method
502	static void init()
503	{
504		Log.log(Log.DEBUG,BeanShell.class,"Initializing BeanShell"
505			+ " interpreter");
506
507		BshClassManager.setClassLoader(new JARClassLoader());
508
509		global = new NameSpace("jEdit embedded BeanShell Interpreter");
510		interpForMethods = createInterpreter(global);
511
512		try
513		{
514			Interpreter interp = createInterpreter(global);
515
516			BufferedReader in = new BufferedReader(new InputStreamReader(
517				BeanShell.class.getResourceAsStream("jedit.bsh")));
518
519			interp.eval(in,global,"jedit.bsh");
520		}
521		catch(Throwable t)
522		{
523			Log.log(Log.ERROR,BeanShell.class,t);
524			System.exit(1);
525		}
526
527		// jedit object in global namespace is set up by jedit.bsh
528		internal = (NameSpace)eval(null,"__cruft.namespace;",false);
529	} //}}}
530
531	//}}}
532
533	//{{{ Private members
534
535	//{{{ Instance variables
536	private static Interpreter interpForMethods;
537	private static NameSpace global;
538	private static NameSpace internal;
539	private static boolean running;
540	private static int cachedBlockCounter;
541	//}}}
542
543	//{{{ createInterpreter() method
544	private static Interpreter createInterpreter(NameSpace nameSpace)
545	{
546		return new Interpreter(null,System.out,System.err,
547			false,nameSpace);
548	} //}}}
549
550	//}}}
551}