/jEdit/tags/jedit-4-2-pre12/org/gjt/sp/jedit/BeanShell.java

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