001: /*
002: * BgErrorMgr --
003: *
004: * This class manages the background errors for a Tcl interpreter.
005: *
006: * Copyright (c) 1997 Sun Microsystems, Inc.
007: *
008: * See the file "license.terms" for information on usage and
009: * redistribution of this file, and for a DISCLAIMER OF ALL
010: * WARRANTIES.
011: *
012: * RCS: @(#) $Id: BgErrorMgr.java,v 1.7 2006/01/26 19:49:18 mdejong Exp $
013: *
014: */
015:
016: package tcl.lang;
017:
018: import java.util.*;
019: import java.io.*;
020:
021: /*
022: * This class manages the background errors for a Tcl interpreter. It
023: * stores the error information about the interpreter and use an idle
024: * handler to report the error when the notifier is idle.
025: */
026:
027: class BgErrorMgr implements AssocData {
028:
029: /*
030: * We manage the background errors in this interp instance.
031: */
032:
033: Interp interp;
034:
035: /*
036: * A TclObject for invoking the "bgerror" command. We use a TclObject
037: * instead of a String so that we don't need to look up the command
038: * every time.
039: */
040:
041: TclObject bgerrorCmdObj;
042:
043: /*
044: * A list of the pending background error handlers.
045: */
046:
047: ArrayList errors;
048:
049: /*
050: *----------------------------------------------------------------------
051: *
052: * BgErrorMgr --
053: *
054: * Constructs a new BgErrorMgr instance.
055: *
056: * Side effects:
057: * Member fields are initialized.
058: *
059: *----------------------------------------------------------------------
060: */
061:
062: BgErrorMgr(Interp i) // Manage the background errors in this interp.
063: {
064: interp = i;
065: bgerrorCmdObj = TclString.newInstance("bgerror");
066: bgerrorCmdObj.preserve();
067:
068: errors = new ArrayList();
069: }
070:
071: /*
072: *----------------------------------------------------------------------
073: *
074: * addBgError --
075: *
076: * Adds one background error to the list of pending background
077: * errors. The errors will be reported later as idle events.
078: *
079: * The background errors will be reported in the order they were
080: * added by the addBgError() call. This order is guaranteed by the
081: * constructor IdleHandler.IdleHandler(Notifier);
082: *
083: * Results:
084: * None.
085: *
086: * Side effects:
087: * The command "bgerror" is invoked later as an idle handler to
088: * process the error, passing it the error message. If that fails,
089: * then an error message is output on stderr.
090: *
091: *----------------------------------------------------------------------
092: */
093:
094: void addBgError() {
095: BgError bgErr = new BgError(interp.getNotifier());
096:
097: // The addErrorInfo() call below (with an empty string)
098: // ensures that errorInfo gets properly set. It's needed in
099: // cases where the error came from a utility procedure like
100: // Interp.getVar() instead of Interp.eval(); in these cases
101: // errorInfo still won't have been set when this procedure is
102: // called.
103:
104: interp.addErrorInfo("");
105:
106: bgErr.errorMsg = interp.getResult();
107: bgErr.errorInfo = null;
108: try {
109: bgErr.errorInfo = interp.getVar("errorInfo", null,
110: TCL.GLOBAL_ONLY);
111: } catch (TclException e) {
112: // Do nothing if var does not exist.
113: }
114:
115: bgErr.errorCode = null;
116: try {
117: bgErr.errorCode = interp.getVar("errorCode", null,
118: TCL.GLOBAL_ONLY);
119: } catch (TclException e) {
120: // Do nothing if var does not exist.
121: }
122:
123: bgErr.errorMsg.preserve();
124: bgErr.errorInfo.preserve();
125: bgErr.errorCode.preserve();
126:
127: errors.add(bgErr);
128: }
129:
130: /*
131: *----------------------------------------------------------------------
132: *
133: * disposeAssocData --
134: *
135: * This method is called when the interpreter is destroyed or
136: * when Interp.deleteAssocData is called on a registered
137: * AssocData instance.
138: *
139: * Results:
140: * None.
141: *
142: * Side effects:
143: * Removes any bgerror's that haven't been reported.
144: *
145: *----------------------------------------------------------------------
146: */
147:
148: public void disposeAssocData(Interp interp) // The interpreter in which this AssocData
149: // instance is registered in.
150: {
151: for (int i = errors.size() - 1; i >= 0; i--) {
152: BgError bgErr = (BgError) errors.get(i);
153: errors.remove(i);
154: bgErr.cancel();
155:
156: bgErr.errorMsg.release();
157: bgErr.errorMsg = null;
158: bgErr.errorInfo.release();
159: bgErr.errorInfo = null;
160: bgErr.errorCode.release();
161: bgErr.errorCode = null;
162: }
163:
164: bgerrorCmdObj.release();
165: bgerrorCmdObj = null;
166: }
167:
168: /*
169: * This inner class implements an idle handler which reports one
170: * background error.
171: */
172:
173: class BgError extends IdleHandler {
174:
175: /*
176: * The interp's result, errorCode and errorInfo when the bgerror happened.
177: */
178:
179: TclObject errorMsg;
180: TclObject errorCode;
181: TclObject errorInfo;
182:
183: /*
184: *----------------------------------------------------------------------
185: *
186: * BgError --
187: *
188: * Constructs a new BgError instance.
189: *
190: * Side effects:
191: * The idle handler is initialized by the super class's
192: * constructor.
193: *
194: *----------------------------------------------------------------------
195: */
196:
197: BgError(Notifier n) // The notifier to fire the event.
198: {
199: super (n);
200: }
201:
202: /*
203: *----------------------------------------------------------------------
204: *
205: * processIdleEvent --
206: *
207: * Process the idle event.
208: *
209: * Results:
210: * None.
211: *
212: * Side effects:
213: * The bgerror command is executed. It may have arbitrary side
214: * effects.
215: *
216: *----------------------------------------------------------------------
217: */
218:
219: public void processIdleEvent() {
220:
221: // During the execution of this method, elements may be removed from
222: // the errors list (because a TCL.BREAK was returned by the bgerror
223: // command, or because the interp was deleted). We remove this
224: // BgError instance from the list first so that this instance won't
225: // be deleted twice.
226:
227: int index = errors.indexOf(this );
228: errors.remove(index);
229:
230: // Restore important state variables to what they were at
231: // the time the error occurred.
232:
233: try {
234: interp.setVar("errorInfo", null, errorInfo,
235: TCL.GLOBAL_ONLY);
236: } catch (TclException e) {
237:
238: // Ignore any TclException's, possibly caused by variable traces on
239: // the errorInfo variable. This is compatible with the behavior of
240: // the Tcl C API.
241: }
242:
243: try {
244: interp.setVar("errorCode", null, errorCode,
245: TCL.GLOBAL_ONLY);
246: } catch (TclException e) {
247:
248: // Ignore any TclException's, possibly caused by variable traces on
249: // the errorCode variable. This is compatible with the behavior of
250: // the Tcl C API.
251: }
252:
253: // Make sure, that the interpreter will surive the invocation
254: // of the bgerror command.
255:
256: interp.preserve();
257:
258: try {
259:
260: // Invoke the bgerror command.
261:
262: TclObject argv[] = new TclObject[2];
263: argv[0] = bgerrorCmdObj;
264: argv[1] = errorMsg;
265:
266: Parser.evalObjv(interp, argv, 0, TCL.EVAL_GLOBAL);
267: } catch (TclException e) {
268: switch (e.getCompletionCode()) {
269: case TCL.ERROR:
270: try {
271: Channel chan = TclIO
272: .getStdChannel(StdChannel.STDERR);
273: if (interp
274: .getResult()
275: .toString()
276: .equals(
277: "\"bgerror\" is an invalid command name or ambiguous abbreviation")) {
278: chan.write(interp, errorInfo);
279: chan.write(interp, "\n");
280: } else {
281: chan
282: .write(interp,
283: "bgerror failed to handle background error.\n");
284: chan.write(interp, " Original error: ");
285: chan.write(interp, errorMsg);
286: chan.write(interp, "\n");
287: chan
288: .write(interp,
289: " Error in bgerror: ");
290: chan.write(interp, interp.getResult());
291: chan.write(interp, "\n");
292: }
293: chan.flush(interp);
294: } catch (TclException e1) {
295:
296: // Ignore.
297:
298: } catch (IOException e2) {
299:
300: // Ignore, too.
301:
302: }
303: break;
304:
305: case TCL.BREAK:
306:
307: // Break means cancel any remaining error reports for this
308: // interpreter.
309:
310: for (int i = errors.size() - 1; i >= 0; i--) {
311: BgError bgErr = (BgError) errors.get(i);
312: errors.remove(i);
313: bgErr.cancel();
314:
315: bgErr.errorMsg.release();
316: bgErr.errorMsg = null;
317: bgErr.errorInfo.release();
318: bgErr.errorInfo = null;
319: bgErr.errorCode.release();
320: bgErr.errorCode = null;
321: }
322: break;
323: }
324: }
325:
326: interp.release();
327:
328: errorMsg.release();
329: errorMsg = null;
330: errorInfo.release();
331: errorInfo = null;
332: errorCode.release();
333: errorCode = null;
334: }
335:
336: } // end BgErrorMgr.BgError
337:
338: } // end BgErrorMgr
|