001: /*
002: * TraceCmd.java --
003: *
004: * This file implements the Tcl "trace" command.
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: TraceCmd.java,v 1.8 2006/01/26 19:49:18 mdejong Exp $
013: *
014: */
015:
016: package tcl.lang;
017:
018: import java.util.*;
019:
020: /**
021: * The TraceCmd class implements the Command interface for specifying
022: * a new Tcl command. The method cmdProc implements the built-in Tcl
023: * command "trace" which is used to manupilate variable traces. See
024: * user documentation for more details.
025: */
026:
027: class TraceCmd implements Command {
028:
029: // Valid sub-commands for the trace command.
030:
031: static final private String[] validCmds = { "variable", "vdelete",
032: "vinfo", };
033:
034: static final private int OPT_VARIABLE = 0;
035: static final private int OPT_VDELETE = 1;
036: static final private int OPT_VINFO = 2;
037:
038: // An array for quickly generating the Tcl strings corresponding to
039: // the TCL.TRACE_READS, TCL.TRACE_WRITES and TCL.TRACE_UNSETS flags.
040:
041: private static TclObject[] opStr = initOptStr();
042:
043: /*
044: *----------------------------------------------------------------------
045: *
046: * initOptStr --
047: *
048: * This static method is called when the TraceCmd class is loaded
049: * into the VM. It initializes the opStr array.
050: *
051: * Results:
052: * Initial value for opStr.
053: *
054: * Side effects:
055: * The TclObjects stored in opStr are preserve()'ed.
056: *
057: *----------------------------------------------------------------------
058: */
059:
060: private static TclObject[] initOptStr() {
061: TclObject[] strings = new TclObject[8];
062: strings[0] = TclString.newInstance("error");
063: strings[1] = TclString.newInstance("r");
064: strings[2] = TclString.newInstance("w");
065: strings[3] = TclString.newInstance("rw");
066: strings[4] = TclString.newInstance("u");
067: strings[5] = TclString.newInstance("ru");
068: strings[6] = TclString.newInstance("wu");
069: strings[7] = TclString.newInstance("rwu");
070:
071: for (int i = 0; i < 8; i++) {
072: strings[i].preserve();
073: }
074:
075: return strings;
076: }
077:
078: /*
079: *----------------------------------------------------------------------
080: *
081: * Tcl_TraceObjCmd -> TraceCmd.cmdProc
082: *
083: * This procedure is invoked as part of the Command interface to
084: * process the "trace" Tcl command. See the user documentation for
085: * details on what it does.
086: *
087: * Results:
088: * None.
089: *
090: * Side effects:
091: * See the user documentation.
092: *
093: *----------------------------------------------------------------------
094: */
095:
096: public void cmdProc(Interp interp, // Current interpreter.
097: TclObject[] objv) // Argument list.
098: throws TclException // A standard Tcl exception.
099: {
100: int len;
101:
102: if (objv.length < 2) {
103: throw new TclNumArgsException(interp, 1, objv,
104: "option [arg arg ...]");
105: }
106: int opt = TclIndex.get(interp, objv[1], validCmds, "option", 0);
107:
108: switch (opt) {
109: case OPT_VARIABLE:
110: case OPT_VDELETE:
111: if (objv.length != 5) {
112: if (opt == OPT_VARIABLE) {
113: throw new TclNumArgsException(interp, 1, objv,
114: "variable name ops command");
115: } else {
116: throw new TclNumArgsException(interp, 1, objv,
117: "vdelete name ops command");
118: }
119: }
120:
121: int flags = 0;
122: String ops = objv[3].toString();
123: len = ops.length();
124: check_ops: {
125: for (int i = 0; i < len; i++) {
126: switch (ops.charAt(i)) {
127: case 'r':
128: flags |= TCL.TRACE_READS;
129: break;
130: case 'w':
131: flags |= TCL.TRACE_WRITES;
132: break;
133: case 'u':
134: flags |= TCL.TRACE_UNSETS;
135: break;
136: default:
137: flags = 0;
138: break check_ops;
139: }
140: }
141: }
142:
143: if (flags == 0) {
144: throw new TclException(interp, "bad operations \""
145: + objv[3] + "\": should be one or more of rwu");
146: }
147:
148: if (opt == OPT_VARIABLE) {
149: CmdTraceProc trace = new CmdTraceProc(objv[4]
150: .toString(), flags);
151: Var.traceVar(interp, objv[2].toString(), null, flags,
152: trace);
153: } else {
154: // Search through all of our traces on this variable to
155: // see if there's one with the given command. If so, then
156: // delete the first one that matches.
157:
158: ArrayList traces = Var.getTraces(interp, objv[2]
159: .toString(), null, 0);
160: if (traces != null) {
161: len = traces.size();
162: for (int i = 0; i < len; i++) {
163: TraceRecord rec = (TraceRecord) traces.get(i);
164:
165: if (rec.trace instanceof CmdTraceProc) {
166: CmdTraceProc proc = (CmdTraceProc) rec.trace;
167: if (proc.flags == flags
168: && proc.command.toString().equals(
169: objv[4].toString())) {
170: Var.untraceVar(interp, objv[2]
171: .toString(), null, flags, proc);
172: break;
173: }
174: }
175: }
176: }
177: }
178: break;
179:
180: case OPT_VINFO:
181: if (objv.length != 3) {
182: throw new TclNumArgsException(interp, 2, objv, "name");
183: }
184: ArrayList traces = Var.getTraces(interp,
185: objv[2].toString(), null, 0);
186: if (traces != null) {
187: len = traces.size();
188: TclObject list = TclList.newInstance();
189: TclObject cmd = null;
190: list.preserve();
191:
192: try {
193: for (int i = 0; i < len; i++) {
194: TraceRecord rec = (TraceRecord) traces.get(i);
195:
196: if (rec.trace instanceof CmdTraceProc) {
197: CmdTraceProc proc = (CmdTraceProc) rec.trace;
198: int mode = proc.flags;
199: mode &= (TCL.TRACE_READS | TCL.TRACE_WRITES | TCL.TRACE_UNSETS);
200: mode /= TCL.TRACE_READS;
201:
202: cmd = TclList.newInstance();
203: TclList.append(interp, cmd, opStr[mode]);
204: TclList.append(interp, cmd, TclString
205: .newInstance(proc.command));
206: TclList.append(interp, list, cmd);
207: }
208: }
209: interp.setResult(list);
210: } finally {
211: list.release();
212: }
213: }
214: break;
215: }
216: }
217:
218: } // TraceCmd
219:
220: // The CmdTraceProc object holds the information for a specific
221: // trace.
222: class CmdTraceProc implements VarTrace {
223:
224: // The command holds the Tcl script that will execute. The flags
225: // hold the mode flags that define what conditions to fire under.
226:
227: String command;
228: int flags;
229:
230: /*
231: *----------------------------------------------------------------------
232: *
233: * CmdTraceProc --
234: *
235: * This function is a constructor for a CmdTraceProc. It simply
236: * stores the flags and command used for this trace proc.
237: * details on what it does.
238: *
239: * Results:
240: * None.
241: *
242: * Side effects:
243: * None.
244: *
245: *----------------------------------------------------------------------
246: */
247:
248: CmdTraceProc(String cmd, int newFlags) {
249: flags = newFlags;
250: command = cmd;
251: }
252:
253: /*
254: *----------------------------------------------------------------------
255: *
256: * traceProc --
257: *
258: * This function gets called when a variable is used in a way that
259: * would cause this particular trace to fire. It will evaluate
260: * the script associated with this trace.
261: *
262: * Results:
263: * None.
264: *
265: * Side effects:
266: * None.
267: *
268: *----------------------------------------------------------------------
269: */
270:
271: public void traceProc(Interp interp, // The current interpreter.
272: String part1, // A Tcl variable or array name.
273: String part2, // Array element name or NULL.
274: int flags) // Mode flags: TCL.TRACE_READS, TCL.TRACE_WRITES or
275: // TCL.TRACE_UNSETS.
276: throws TclException // A standard Tcl exception.
277: {
278: if (((this .flags & flags) != 0)
279: && ((flags & TCL.INTERP_DESTROYED) == 0)) {
280: StringBuffer sbuf = new StringBuffer(command);
281:
282: try {
283: Util.appendElement(interp, sbuf, part1);
284: if (part2 != null) {
285: Util.appendElement(interp, sbuf, part2);
286: } else {
287: Util.appendElement(interp, sbuf, "");
288: }
289:
290: if ((flags & TCL.TRACE_READS) != 0) {
291: Util.appendElement(interp, sbuf, "r");
292: } else if ((flags & TCL.TRACE_WRITES) != 0) {
293: Util.appendElement(interp, sbuf, "w");
294: } else if ((flags & TCL.TRACE_UNSETS) != 0) {
295: Util.appendElement(interp, sbuf, "u");
296: }
297: } catch (TclException e) {
298: throw new TclRuntimeError("unexpected TclException: "
299: + e);
300: }
301:
302: // Execute the command.
303:
304: interp.eval(sbuf.toString(), 0);
305: }
306: }
307:
308: } // CmdTraceProc
|