/jEdit/tags/jedit-4-3-pre11/org/gjt/sp/jedit/BeanShell.java

# · Java · 814 lines · 459 code · 80 blank · 275 comment · 55 complexity · 3f6523c3d35d83c1486bfaa705422a8b 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, 2004 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. package org.gjt.sp.jedit;
  23. //{{{ Imports
  24. import bsh.*;
  25. import bsh.classpath.ClassManagerImpl;
  26. import java.io.*;
  27. import java.lang.ref.*;
  28. import java.lang.reflect.InvocationTargetException;
  29. import java.util.*;
  30. import org.gjt.sp.jedit.io.*;
  31. import org.gjt.sp.jedit.gui.BeanShellErrorDialog;
  32. import org.gjt.sp.jedit.textarea.*;
  33. import org.gjt.sp.util.Log;
  34. //}}}
  35. /**
  36. * BeanShell is jEdit's extension language.<p>
  37. *
  38. * When run from jEdit, BeanShell code has access to the following predefined
  39. * variables:
  40. *
  41. * <ul>
  42. * <li><code>view</code> - the currently active {@link View}.</li>
  43. * <li><code>editPane</code> - the currently active {@link EditPane}.</li>
  44. * <li><code>textArea</code> - the edit pane's {@link JEditTextArea}.</li>
  45. * <li><code>buffer</code> - the edit pane's {@link Buffer}.</li>
  46. * <li><code>wm</code> - the view's {@link
  47. * org.gjt.sp.jedit.gui.DockableWindowManager}.</li>
  48. * <li><code>scriptPath</code> - the path name of the currently executing
  49. * BeanShell script.</li>
  50. * </ul>
  51. *
  52. * @author Slava Pestov
  53. * @version $Id: BeanShell.java 5555 2006-07-07 22:28:15Z kpouer $
  54. */
  55. public class BeanShell
  56. {
  57. private static final String REQUIRED_VERSION = "2.0b1.1-jedit-1";
  58. //{{{ evalSelection() method
  59. /**
  60. * Evaluates the text selected in the specified text area.
  61. * @since jEdit 2.7pre2
  62. */
  63. public static void evalSelection(View view, JEditTextArea textArea)
  64. {
  65. String command = textArea.getSelectedText();
  66. if(command == null)
  67. {
  68. view.getToolkit().beep();
  69. return;
  70. }
  71. Object returnValue = eval(view,global,command);
  72. if(returnValue != null)
  73. textArea.setSelectedText(returnValue.toString());
  74. } //}}}
  75. //{{{ showEvaluateDialog() method
  76. /**
  77. * Prompts for a BeanShell expression to evaluate.
  78. * @since jEdit 2.7pre2
  79. */
  80. public static void showEvaluateDialog(View view)
  81. {
  82. String command = GUIUtilities.input(view,"beanshell-eval-input",null);
  83. if(command != null)
  84. {
  85. if(!command.endsWith(";"))
  86. command = command + ";";
  87. int repeat = view.getInputHandler().getRepeatCount();
  88. if(view.getMacroRecorder() != null)
  89. {
  90. view.getMacroRecorder().record(repeat,command);
  91. }
  92. Object returnValue = null;
  93. try
  94. {
  95. for(int i = 0; i < repeat; i++)
  96. {
  97. returnValue = _eval(view,global,command);
  98. }
  99. }
  100. catch(Throwable e)
  101. {
  102. Log.log(Log.ERROR,BeanShell.class,e);
  103. handleException(view,null,e);
  104. }
  105. if(returnValue != null)
  106. {
  107. String[] args = { returnValue.toString() };
  108. GUIUtilities.message(view,"beanshell-eval",args);
  109. }
  110. }
  111. } //}}}
  112. //{{{ showEvaluateLinesDialog() method
  113. /**
  114. * Evaluates the specified script for each selected line.
  115. * @since jEdit 4.0pre1
  116. */
  117. public static void showEvaluateLinesDialog(View view)
  118. {
  119. String command = GUIUtilities.input(view,"beanshell-eval-line",null);
  120. JEditTextArea textArea = view.getTextArea();
  121. Buffer buffer = view.getBuffer();
  122. if(command == null || command.length() == 0)
  123. return;
  124. Selection[] selection = textArea.getSelection();
  125. if(selection.length == 0)
  126. {
  127. view.getToolkit().beep();
  128. return;
  129. }
  130. if(!command.endsWith(";"))
  131. command = command + ";";
  132. String script = "int[] lines = textArea.getSelectedLines();\n"
  133. + "for(int i = 0; i < lines.length; i++)\n"
  134. + "{\n"
  135. + "line = lines[i];\n"
  136. + "index = line - lines[0];\n"
  137. + "start = buffer.getLineStartOffset(line);\n"
  138. + "end = buffer.getLineEndOffset(line);\n"
  139. + "text = buffer.getText(start,end - start - 1);\n"
  140. + "newText = " + command + "\n"
  141. + "if(newText != null)\n"
  142. + "{\n"
  143. + "buffer.remove(start,end - start - 1);\n"
  144. + "buffer.insert(start,String.valueOf(newText));\n"
  145. + "}\n"
  146. + "}\n";
  147. if(view.getMacroRecorder() != null)
  148. view.getMacroRecorder().record(1,script);
  149. try
  150. {
  151. buffer.beginCompoundEdit();
  152. BeanShell.eval(view,global,script);
  153. }
  154. finally
  155. {
  156. buffer.endCompoundEdit();
  157. }
  158. textArea.selectNone();
  159. } //}}}
  160. //{{{ runScript() method
  161. /**
  162. * Runs a BeanShell script. Errors are shown in a dialog box.<p>
  163. *
  164. * If the <code>in</code> parameter is non-null, the script is
  165. * read from that stream; otherwise it is read from the file identified
  166. * by <code>path</code>.<p>
  167. *
  168. * The <code>scriptPath</code> BeanShell variable is set to the path
  169. * name of the script.
  170. *
  171. * @param view The view. Within the script, references to
  172. * <code>buffer</code>, <code>textArea</code> and <code>editPane</code>
  173. * are determined with reference to this parameter.
  174. * @param path The script file's VFS path.
  175. * @param in The reader to read the script from, or <code>null</code>.
  176. * @param ownNamespace If set to <code>false</code>, methods and
  177. * variables defined in the script will be available to all future
  178. * uses of BeanShell; if set to <code>true</code>, they will be lost as
  179. * soon as the script finishes executing. jEdit uses a value of
  180. * <code>false</code> when running startup scripts, and a value of
  181. * <code>true</code> when running all other macros.
  182. *
  183. * @since jEdit 4.0pre7
  184. */
  185. public static void runScript(View view, String path, Reader in,
  186. boolean ownNamespace)
  187. {
  188. try
  189. {
  190. _runScript(view,path,in,ownNamespace);
  191. }
  192. catch(Throwable e)
  193. {
  194. Log.log(Log.ERROR,BeanShell.class,e);
  195. handleException(view,path,e);
  196. }
  197. } //}}}
  198. //{{{ runScript() method
  199. /**
  200. * Runs a BeanShell script. Errors are shown in a dialog box.<p>
  201. *
  202. * If the <code>in</code> parameter is non-null, the script is
  203. * read from that stream; otherwise it is read from the file identified
  204. * by <code>path</code>.<p>
  205. *
  206. * The <code>scriptPath</code> BeanShell variable is set to the path
  207. * name of the script.
  208. *
  209. * @param view The view. Within the script, references to
  210. * <code>buffer</code>, <code>textArea</code> and <code>editPane</code>
  211. * are determined with reference to this parameter.
  212. * @param path The script file's VFS path.
  213. * @param in The reader to read the script from, or <code>null</code>.
  214. * @param namespace The namespace to run the script in.
  215. *
  216. * @since jEdit 4.2pre5
  217. */
  218. public static void runScript(View view, String path, Reader in,
  219. NameSpace namespace)
  220. {
  221. try
  222. {
  223. _runScript(view,path,in,namespace);
  224. }
  225. catch(Throwable e)
  226. {
  227. Log.log(Log.ERROR,BeanShell.class,e);
  228. handleException(view,path,e);
  229. }
  230. } //}}}
  231. //{{{ _runScript() method
  232. /**
  233. * Runs a BeanShell script. Errors are passed to the caller.<p>
  234. *
  235. * If the <code>in</code> parameter is non-null, the script is
  236. * read from that stream; otherwise it is read from the file identified
  237. * by <code>path</code>.<p>
  238. *
  239. * The <code>scriptPath</code> BeanShell variable is set to the path
  240. * name of the script.
  241. *
  242. * @param view The view. Within the script, references to
  243. * <code>buffer</code>, <code>textArea</code> and <code>editPane</code>
  244. * are determined with reference to this parameter.
  245. * @param path The script file's VFS path.
  246. * @param in The reader to read the script from, or <code>null</code>.
  247. * @param ownNamespace If set to <code>false</code>, methods and
  248. * variables defined in the script will be available to all future
  249. * uses of BeanShell; if set to <code>true</code>, they will be lost as
  250. * soon as the script finishes executing. jEdit uses a value of
  251. * <code>false</code> when running startup scripts, and a value of
  252. * <code>true</code> when running all other macros.
  253. * @exception Exception instances are thrown when various BeanShell errors
  254. * occur
  255. * @since jEdit 4.0pre7
  256. */
  257. public static void _runScript(View view, String path, Reader in,
  258. boolean ownNamespace) throws Exception
  259. {
  260. _runScript(view,path,in,ownNamespace
  261. ? new NameSpace(global,"namespace")
  262. : global);
  263. } //}}}
  264. //{{{ _runScript() method
  265. /**
  266. * Runs a BeanShell script. Errors are passed to the caller.<p>
  267. *
  268. * If the <code>in</code> parameter is non-null, the script is
  269. * read from that stream; otherwise it is read from the file identified
  270. * by <code>path</code>.<p>
  271. *
  272. * The <code>scriptPath</code> BeanShell variable is set to the path
  273. * name of the script.
  274. *
  275. * @param view The view. Within the script, references to
  276. * <code>buffer</code>, <code>textArea</code> and <code>editPane</code>
  277. * are determined with reference to this parameter.
  278. * @param path The script file's VFS path.
  279. * @param in The reader to read the script from, or <code>null</code>.
  280. * @param namespace The namespace to run the script in.
  281. * @exception Exception instances are thrown when various BeanShell errors
  282. * occur
  283. * @since jEdit 4.2pre5
  284. */
  285. public static void _runScript(View view, String path, Reader in,
  286. NameSpace namespace) throws Exception
  287. {
  288. Log.log(Log.MESSAGE,BeanShell.class,"Running script " + path);
  289. Interpreter interp = createInterpreter(namespace);
  290. VFS vfs = null;
  291. Object session = null;
  292. try
  293. {
  294. if(in == null)
  295. {
  296. Buffer buffer = jEdit.openTemporary(null,
  297. null,path,false);
  298. if(!buffer.isLoaded())
  299. VFSManager.waitForRequests();
  300. in = new StringReader(buffer.getText(0,
  301. buffer.getLength()));
  302. }
  303. setupDefaultVariables(namespace,view);
  304. interp.set("scriptPath",path);
  305. running = true;
  306. interp.eval(in,namespace,path);
  307. }
  308. catch(Exception e)
  309. {
  310. unwrapException(e);
  311. }
  312. finally
  313. {
  314. running = false;
  315. if(session != null)
  316. {
  317. try
  318. {
  319. vfs._endVFSSession(session,view);
  320. }
  321. catch(IOException io)
  322. {
  323. Log.log(Log.ERROR,BeanShell.class,io);
  324. GUIUtilities.error(view,"read-error",
  325. new String[] { path, io.toString() });
  326. }
  327. }
  328. try
  329. {
  330. // no need to do this for macros!
  331. if(namespace == global)
  332. {
  333. resetDefaultVariables(namespace);
  334. interp.unset("scriptPath");
  335. }
  336. }
  337. catch(EvalError e)
  338. {
  339. // do nothing
  340. }
  341. }
  342. } //}}}
  343. //{{{ eval() method
  344. /**
  345. * Evaluates the specified BeanShell expression. Errors are reported in
  346. * a dialog box.
  347. * @param view The view. Within the script, references to
  348. * <code>buffer</code>, <code>textArea</code> and <code>editPane</code>
  349. * are determined with reference to this parameter.
  350. * @param namespace The namespace
  351. * @param command The expression
  352. * @since jEdit 4.0pre8
  353. */
  354. public static Object eval(View view, NameSpace namespace, String command)
  355. {
  356. try
  357. {
  358. return _eval(view,namespace,command);
  359. }
  360. catch(Throwable e)
  361. {
  362. Log.log(Log.ERROR,BeanShell.class,e);
  363. handleException(view,null,e);
  364. }
  365. return null;
  366. } //}}}
  367. //{{{ _eval() method
  368. /**
  369. * Evaluates the specified BeanShell expression. Unlike
  370. * <code>eval()</code>, this method passes any exceptions to the caller.
  371. *
  372. * @param view The view. Within the script, references to
  373. * <code>buffer</code>, <code>textArea</code> and <code>editPane</code>
  374. * are determined with reference to this parameter.
  375. * @param namespace The namespace
  376. * @param command The expression
  377. * @exception Exception instances are thrown when various BeanShell
  378. * errors occur
  379. * @since jEdit 3.2pre7
  380. */
  381. public static Object _eval(View view, NameSpace namespace, String command)
  382. throws Exception
  383. {
  384. Interpreter interp = createInterpreter(namespace);
  385. try
  386. {
  387. setupDefaultVariables(namespace,view);
  388. if(Debug.BEANSHELL_DEBUG)
  389. Log.log(Log.DEBUG,BeanShell.class,command);
  390. return interp.eval(command);
  391. }
  392. catch(Exception e)
  393. {
  394. unwrapException(e);
  395. // never called
  396. return null;
  397. }
  398. finally
  399. {
  400. try
  401. {
  402. resetDefaultVariables(namespace);
  403. }
  404. catch(UtilEvalError e)
  405. {
  406. // do nothing
  407. }
  408. }
  409. } //}}}
  410. //{{{ cacheBlock() method
  411. /**
  412. * Caches a block of code, returning a handle that can be passed to
  413. * runCachedBlock().
  414. * @param id An identifier. If null, a unique identifier is generated
  415. * @param code The code
  416. * @param namespace If true, the namespace will be set
  417. * @exception Exception instances are thrown when various BeanShell errors
  418. * occur
  419. * @since jEdit 4.1pre1
  420. */
  421. public static BshMethod cacheBlock(String id, String code, boolean namespace)
  422. throws Exception
  423. {
  424. String name = "__internal_" + id;
  425. // evaluate a method declaration
  426. if(namespace)
  427. {
  428. _eval(null,global,name + "(ns) {\nthis.callstack.set(0,ns);\n" + code + "\n}");
  429. return global.getMethod(name,new Class[] { NameSpace.class });
  430. }
  431. else
  432. {
  433. _eval(null,global,name + "() {\n" + code + "\n}");
  434. return global.getMethod(name,new Class[0]);
  435. }
  436. } //}}}
  437. //{{{ runCachedBlock() method
  438. /**
  439. * Runs a cached block of code in the specified namespace. Faster than
  440. * evaluating the block each time.
  441. * @param method The method instance returned by cacheBlock()
  442. * @param view The view
  443. * @param namespace The namespace to run the code in
  444. * @exception Exception instances are thrown when various BeanShell
  445. * errors occur
  446. * @since jEdit 4.1pre1
  447. */
  448. public static Object runCachedBlock(BshMethod method, View view,
  449. NameSpace namespace) throws Exception
  450. {
  451. boolean useNamespace;
  452. if(namespace == null)
  453. {
  454. useNamespace = false;
  455. namespace = global;
  456. }
  457. else
  458. useNamespace = true;
  459. try
  460. {
  461. setupDefaultVariables(namespace,view);
  462. Object retVal = method.invoke(useNamespace
  463. ? new Object[] { namespace }
  464. : NO_ARGS,
  465. interpForMethods,new CallStack(), null);
  466. if(retVal instanceof Primitive)
  467. {
  468. if(retVal == Primitive.VOID)
  469. return null;
  470. else
  471. return ((Primitive)retVal).getValue();
  472. }
  473. else
  474. return retVal;
  475. }
  476. catch(Exception e)
  477. {
  478. unwrapException(e);
  479. // never called
  480. return null;
  481. }
  482. finally
  483. {
  484. resetDefaultVariables(namespace);
  485. }
  486. } //}}}
  487. //{{{ isScriptRunning() method
  488. /**
  489. * Returns if a BeanShell script or macro is currently running.
  490. * @since jEdit 2.7pre2
  491. */
  492. public static boolean isScriptRunning()
  493. {
  494. return running;
  495. } //}}}
  496. //{{{ getNameSpace() method
  497. /**
  498. * Returns the global namespace.
  499. * @since jEdit 3.2pre5
  500. */
  501. public static NameSpace getNameSpace()
  502. {
  503. return global;
  504. } //}}}
  505. //{{{ Deprecated functions
  506. //{{{ runScript() method
  507. /**
  508. * @deprecated The <code>rethrowBshErrors</code> parameter is now
  509. * obsolete; call <code>_runScript()</code> or <code>runScript()</code>
  510. * instead.
  511. */
  512. public static void runScript(View view, String path,
  513. boolean ownNamespace, boolean rethrowBshErrors)
  514. {
  515. runScript(view,path,null,ownNamespace);
  516. } //}}}
  517. //{{{ runScript() method
  518. /**
  519. * @deprecated The <code>rethrowBshErrors</code> parameter is now
  520. * obsolete; call <code>_runScript()</code> or <code>runScript()</code>
  521. * instead.
  522. */
  523. public static void runScript(View view, String path, Reader in,
  524. boolean ownNamespace, boolean rethrowBshErrors)
  525. {
  526. runScript(view,path,in,ownNamespace);
  527. } //}}}
  528. //{{{ eval() method
  529. /**
  530. * @deprecated The <code>rethrowBshErrors</code> parameter is now
  531. * obsolete; call <code>_eval()</code> or <code>eval()</code> instead.
  532. */
  533. public static Object eval(View view, String command,
  534. boolean rethrowBshErrors)
  535. {
  536. return eval(view,global,command);
  537. } //}}}
  538. //{{{ eval() method
  539. /**
  540. * @deprecated The <code>rethrowBshErrors</code> parameter is now
  541. * obsolete; call <code>_eval()</code> or <code>eval()</code> instead.
  542. */
  543. public static Object eval(View view, NameSpace namespace,
  544. String command, boolean rethrowBshErrors)
  545. {
  546. return eval(view,namespace,command);
  547. } //}}}
  548. //}}}
  549. //{{{ Package-private members
  550. //{{{ init() method
  551. static void init()
  552. {
  553. /*try
  554. {
  555. NameSpace.class.getMethod("addCommandPath",
  556. new Class[] { String.class, Class.class });
  557. }
  558. catch(Exception e)
  559. {
  560. Log.log(Log.ERROR,BeanShell.class,"You have BeanShell version " + getVersion() + " in your CLASSPATH.");
  561. Log.log(Log.ERROR,BeanShell.class,"Please remove it from the CLASSPATH since jEdit can only run with the bundled BeanShell version " + REQUIRED_VERSION);
  562. System.exit(1);
  563. } */
  564. classManager = new ClassManagerImpl();
  565. classManager.setClassLoader(new JARClassLoader());
  566. global = new NameSpace(classManager,
  567. "jEdit embedded BeanShell interpreter");
  568. global.importPackage("org.gjt.sp.jedit");
  569. global.importPackage("org.gjt.sp.jedit.browser");
  570. global.importPackage("org.gjt.sp.jedit.buffer");
  571. global.importPackage("org.gjt.sp.jedit.gui");
  572. global.importPackage("org.gjt.sp.jedit.help");
  573. global.importPackage("org.gjt.sp.jedit.io");
  574. global.importPackage("org.gjt.sp.jedit.menu");
  575. global.importPackage("org.gjt.sp.jedit.msg");
  576. global.importPackage("org.gjt.sp.jedit.options");
  577. global.importPackage("org.gjt.sp.jedit.pluginmgr");
  578. global.importPackage("org.gjt.sp.jedit.print");
  579. global.importPackage("org.gjt.sp.jedit.search");
  580. global.importPackage("org.gjt.sp.jedit.syntax");
  581. global.importPackage("org.gjt.sp.jedit.textarea");
  582. global.importPackage("org.gjt.sp.util");
  583. interpForMethods = createInterpreter(global);
  584. } //}}}
  585. //{{{ resetClassManager() method
  586. /**
  587. * Causes BeanShell internal structures to drop references to cached
  588. * Class instances.
  589. */
  590. static void resetClassManager()
  591. {
  592. classManager.reset();
  593. } //}}}
  594. //}}}
  595. //{{{ Private members
  596. //{{{ Static variables
  597. private static final Object[] NO_ARGS = new Object[0];
  598. private static BshClassManager classManager;
  599. private static Interpreter interpForMethods;
  600. private static NameSpace global;
  601. private static boolean running;
  602. //}}}
  603. //{{{ setupDefaultVariables() method
  604. private static void setupDefaultVariables(NameSpace namespace, View view)
  605. throws UtilEvalError
  606. {
  607. if(view != null)
  608. {
  609. EditPane editPane = view.getEditPane();
  610. namespace.setVariable("view",view, false);
  611. namespace.setVariable("editPane",editPane, false);
  612. namespace.setVariable("buffer",editPane.getBuffer(), false);
  613. namespace.setVariable("textArea",editPane.getTextArea(), false);
  614. namespace.setVariable("wm",view.getDockableWindowManager(), false);
  615. }
  616. } //}}}
  617. //{{{ resetDefaultVariables() method
  618. private static void resetDefaultVariables(NameSpace namespace)
  619. throws UtilEvalError
  620. {
  621. namespace.setVariable("view",null, false);
  622. namespace.setVariable("editPane",null, false);
  623. namespace.setVariable("buffer",null, false);
  624. namespace.setVariable("textArea",null, false);
  625. namespace.setVariable("wm",null, false);
  626. } //}}}
  627. //{{{ unwrapException() method
  628. /**
  629. * This extracts an exception from a 'wrapping' exception, as BeanShell
  630. * sometimes throws. This gives the user a more accurate error traceback
  631. */
  632. private static void unwrapException(Exception e) throws Exception
  633. {
  634. if(e instanceof TargetError)
  635. {
  636. Throwable t = ((TargetError)e).getTarget();
  637. if(t instanceof Exception)
  638. throw (Exception)t;
  639. else if(t instanceof Error)
  640. throw (Error)t;
  641. }
  642. if(e instanceof InvocationTargetException)
  643. {
  644. Throwable t = ((InvocationTargetException)e).getTargetException();
  645. if(t instanceof Exception)
  646. throw (Exception)t;
  647. else if(t instanceof Error)
  648. throw (Error)t;
  649. }
  650. throw e;
  651. } //}}}
  652. //{{{ handleException() method
  653. private static void handleException(View view, String path, Throwable t)
  654. {
  655. if(t instanceof IOException)
  656. {
  657. VFSManager.error(view,path,"ioerror.read-error",
  658. new String[] { t.toString() });
  659. }
  660. else
  661. new BeanShellErrorDialog(view,t);
  662. } //}}}
  663. //{{{ createInterpreter() method
  664. private static Interpreter createInterpreter(NameSpace nameSpace)
  665. {
  666. return new Interpreter(null,System.out,System.err,false,nameSpace);
  667. } //}}}
  668. //{{{ getVersion() method
  669. private static String getVersion()
  670. {
  671. try
  672. {
  673. return (String)Interpreter.class.getField("VERSION").get(null);
  674. }
  675. catch(Exception e)
  676. {
  677. return "unknown";
  678. }
  679. } //}}}
  680. //}}}
  681. //{{{ CustomClassManager class
  682. static class CustomClassManager extends ClassManagerImpl
  683. {
  684. private LinkedList listeners = new LinkedList();
  685. private ReferenceQueue refQueue = new ReferenceQueue();
  686. // copy and paste from bsh/classpath/ClassManagerImpl.java...
  687. public synchronized void addListener( Listener l )
  688. {
  689. listeners.add( new WeakReference( l, refQueue) );
  690. // clean up old listeners
  691. Reference deadref;
  692. while ( (deadref = refQueue.poll()) != null )
  693. {
  694. boolean ok = listeners.remove( deadref );
  695. if ( ok )
  696. {
  697. //System.err.println("cleaned up weak ref: "+deadref);
  698. }
  699. else
  700. {
  701. if ( Interpreter.DEBUG ) Interpreter.debug(
  702. "tried to remove non-existent weak ref: "+deadref);
  703. }
  704. }
  705. }
  706. public void removeListener( Listener l )
  707. {
  708. throw new Error("unimplemented");
  709. }
  710. public void reset()
  711. {
  712. classLoaderChanged();
  713. }
  714. protected synchronized void classLoaderChanged()
  715. {
  716. // clear the static caches in BshClassManager
  717. clearCaches();
  718. if (listeners != null)
  719. {
  720. for (Iterator iter = listeners.iterator();
  721. iter.hasNext(); )
  722. {
  723. WeakReference wr = (WeakReference)
  724. iter.next();
  725. Listener l = (Listener)wr.get();
  726. if ( l == null ) // garbage collected
  727. iter.remove();
  728. else
  729. l.classLoaderChanged();
  730. }
  731. }
  732. }
  733. } //}}}
  734. }