001: /*
002: * InterpAliasCmd.java --
003: *
004: * Implements the built-in "interp" Tcl command.
005: *
006: * Copyright (c) 2000 Christian Krone.
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: InterpAliasCmd.java,v 1.6 2006/08/03 23:24:02 mdejong Exp $
013: *
014: */
015:
016: package tcl.lang;
017:
018: import java.util.*;
019:
020: /**
021: * This class implements the alias commands, which are created
022: * in response to the built-in "interp alias" command in Tcl.
023: *
024: */
025:
026: class InterpAliasCmd implements CommandWithDispose {
027:
028: // Name of alias command in slave interp.
029:
030: TclObject name;
031:
032: // Interp in which target command will be invoked.
033:
034: private Interp targetInterp;
035:
036: // Tcl list making up the prefix of the target command to be invoked in
037: // the target interpreter. Additional arguments specified when calling
038: // the alias in the slave interp will be appended to the prefix before
039: // the command is invoked.
040:
041: private TclObject prefix;
042:
043: // Source command in slave interpreter, bound to command that invokes
044: // the target command in the target interpreter.
045:
046: private WrappedCommand slaveCmd;
047:
048: // Entry for the alias hash table in slave.
049: // This is used by alias deletion to remove the alias from the slave
050: // interpreter alias table.
051:
052: private String aliasEntry;
053:
054: // Interp in which the command is defined.
055: // This is the interpreter with the aliasTable in Slave.
056:
057: private Interp slaveInterp;
058:
059: /**
060: *----------------------------------------------------------------------
061: *
062: * AliasObjCmd -> cmdProc
063: *
064: * This is the procedure that services invocations of aliases in a
065: * slave interpreter. One such command exists for each alias. When
066: * invoked, this procedure redirects the invocation to the target
067: * command in the master interpreter as designated by the Alias
068: * record associated with this command.
069: *
070: * Results:
071: * A standard Tcl result.
072: *
073: * Side effects:
074: * Causes forwarding of the invocation; all possible side effects
075: * may occur as a result of invoking the command to which the
076: * invocation is forwarded.
077: *
078: *----------------------------------------------------------------------
079: */
080:
081: public void cmdProc(Interp interp, // Current interpreter.
082: TclObject[] argv) // Argument list.
083: throws TclException // A standard Tcl exception.
084: {
085: targetInterp.preserve();
086:
087: try {
088: targetInterp.nestLevel++;
089:
090: targetInterp.resetResult();
091: targetInterp.allowExceptions();
092:
093: // Append the arguments to the command prefix and invoke the command
094: // in the target interp's global namespace.
095:
096: TclObject[] prefv = TclList.getElements(interp, prefix);
097: TclObject cmd = TclList.newInstance();
098: cmd.preserve();
099: TclList.replace(interp, cmd, 0, 0, prefv, 0,
100: prefv.length - 1);
101: TclList.replace(interp, cmd, prefv.length, 0, argv, 1,
102: argv.length - 1);
103: TclObject[] cmdv = TclList.getElements(interp, cmd);
104:
105: int result = targetInterp.invoke(cmdv,
106: Interp.INVOKE_NO_TRACEBACK);
107:
108: cmd.release();
109: targetInterp.nestLevel--;
110:
111: // Check if we are at the bottom of the stack for the target interpreter.
112: // If so, check for special return codes.
113:
114: if (targetInterp.nestLevel == 0) {
115: if (result == TCL.RETURN) {
116: result = targetInterp.updateReturnInfo();
117: }
118: if (result != TCL.OK && result != TCL.ERROR) {
119: try {
120: targetInterp.processUnexpectedResult(result);
121: } catch (TclException e) {
122: result = e.getCompletionCode();
123: }
124: }
125: }
126:
127: interp.transferResult(targetInterp, result);
128: } finally {
129: targetInterp.release();
130: }
131: }
132:
133: /**
134: *----------------------------------------------------------------------
135: *
136: * AliasObjCmdDeleteProc -> disposeCmd
137: *
138: * Is invoked when an alias command is deleted in a slave. Cleans up
139: * all storage associated with this alias.
140: *
141: * Results:
142: * None.
143: *
144: * Side effects:
145: * Deletes the alias record and its entry in the alias table for
146: * the interpreter.
147: *
148: *----------------------------------------------------------------------
149: */
150:
151: public void disposeCmd() {
152: if (aliasEntry != null) {
153: slaveInterp.aliasTable.remove(aliasEntry);
154: }
155:
156: if (slaveCmd != null) {
157: targetInterp.targetTable.remove(slaveCmd);
158: }
159:
160: name.release();
161: prefix.release();
162: }
163:
164: /**
165: *----------------------------------------------------------------------
166: *
167: * AliasCreate -> create
168: *
169: * Helper function to do the work to actually create an alias.
170: *
171: * Results:
172: * A standard Tcl result.
173: *
174: * Side effects:
175: * An alias command is created and entered into the alias table
176: * for the slave interpreter.
177: *
178: *----------------------------------------------------------------------
179: */
180:
181: static void create(Interp interp, // Interp for error reporting.
182: Interp slaveInterp, // Interp where alias cmd will live or from
183: // which alias will be deleted.
184: Interp masterInterp, // Interp in which target command will be
185: // invoked.
186: TclObject name, // Name of alias cmd.
187: TclObject targetName, // Name of target cmd.
188: int objIx, // Offset of first element in objv.
189: TclObject[] objv) // Additional arguments to store with alias
190: throws TclException {
191: String string = name.toString();
192:
193: InterpAliasCmd alias = new InterpAliasCmd();
194:
195: alias.name = name;
196: name.preserve();
197:
198: alias.slaveInterp = slaveInterp;
199: alias.targetInterp = masterInterp;
200:
201: alias.prefix = TclList.newInstance();
202: alias.prefix.preserve();
203: TclList.append(interp, alias.prefix, targetName);
204: TclList.insert(interp, alias.prefix, 1, objv, objIx,
205: objv.length - 1);
206:
207: slaveInterp.createCommand(string, alias);
208: alias.slaveCmd = Namespace.findCommand(slaveInterp, string,
209: null, 0);
210:
211: try {
212: interp.preventAliasLoop(slaveInterp, alias.slaveCmd);
213: } catch (TclException e) {
214: // Found an alias loop! The last call to Tcl_CreateObjCommand made
215: // the alias point to itself. Delete the command and its alias
216: // record. Be careful to wipe out its client data first, so the
217: // command doesn't try to delete itself.
218:
219: slaveInterp.deleteCommandFromToken(alias.slaveCmd);
220: throw e;
221: }
222:
223: // Make an entry in the alias table. If it already exists delete
224: // the alias command. Then retry.
225:
226: if (slaveInterp.aliasTable.containsKey(string)) {
227: InterpAliasCmd oldAlias = (InterpAliasCmd) slaveInterp.aliasTable
228: .get(string);
229: slaveInterp.deleteCommandFromToken(oldAlias.slaveCmd);
230: }
231:
232: alias.aliasEntry = string;
233: slaveInterp.aliasTable.put(string, alias);
234:
235: // Create the new command. We must do it after deleting any old command,
236: // because the alias may be pointing at a renamed alias, as in:
237: //
238: // interp alias {} foo {} bar # Create an alias "foo"
239: // rename foo zop # Now rename the alias
240: // interp alias {} foo {} zop # Now recreate "foo"...
241:
242: masterInterp.targetTable.put(alias.slaveCmd, slaveInterp);
243:
244: interp.setResult(name);
245: }
246:
247: /**
248: *----------------------------------------------------------------------
249: *
250: * AliasDelete -> delete
251: *
252: * Deletes the given alias from the slave interpreter given.
253: *
254: * Results:
255: * A standard Tcl result.
256: *
257: * Side effects:
258: * Deletes the alias from the slave interpreter.
259: *
260: *----------------------------------------------------------------------
261: */
262:
263: static void delete(Interp interp, // Interp for error reporting.
264: Interp slaveInterp, // Interp where alias cmd will live or from
265: // which alias will be deleted.
266: TclObject name) // Name of alias to delete.
267: throws TclException {
268: // If the alias has been renamed in the slave, the master can still use
269: // the original name (with which it was created) to find the alias to
270: // delete it.
271:
272: String string = name.toString();
273: if (!slaveInterp.aliasTable.containsKey(string)) {
274: throw new TclException(interp, "alias \"" + string
275: + "\" not found");
276: }
277:
278: InterpAliasCmd alias = (InterpAliasCmd) slaveInterp.aliasTable
279: .get(string);
280: slaveInterp.deleteCommandFromToken(alias.slaveCmd);
281: }
282:
283: /*
284: *----------------------------------------------------------------------
285: *
286: * AliasDescribe -> describe --
287: *
288: * Sets the interpreter's result object to a Tcl list describing
289: * the given alias in the given interpreter: its target command
290: * and the additional arguments to prepend to any invocation
291: * of the alias.
292: *
293: * Results:
294: * A standard Tcl result.
295: *
296: * Side effects:
297: * None.
298: *
299: *----------------------------------------------------------------------
300: */
301:
302: static void describe(Interp interp, // Interp for error reporting.
303: Interp slaveInterp, // Interp where alias cmd will live or from
304: // which alias will be deleted.
305: TclObject name) // Name of alias to delete.
306: throws TclException {
307: // If the alias has been renamed in the slave, the master can still use
308: // the original name (with which it was created) to find the alias to
309: // describe it.
310:
311: String string = name.toString();
312: if (slaveInterp.aliasTable.containsKey(string)) {
313: InterpAliasCmd alias = (InterpAliasCmd) slaveInterp.aliasTable
314: .get(string);
315: interp.setResult(alias.prefix);
316:
317: }
318: }
319:
320: /**
321: *----------------------------------------------------------------------
322: *
323: * AliasList -> list
324: *
325: * Computes a list of aliases defined in a slave interpreter.
326: *
327: * Results:
328: * A standard Tcl result.
329: *
330: * Side effects:
331: * None.
332: *
333: *----------------------------------------------------------------------
334: */
335:
336: static void list(Interp interp, // Interp for error reporting.
337: Interp slaveInterp) // Interp whose aliases to compute.
338: throws TclException {
339: TclObject result = TclList.newInstance();
340: for (Iterator iter = slaveInterp.aliasTable.entrySet()
341: .iterator(); iter.hasNext();) {
342: Map.Entry entry = (Map.Entry) iter.next();
343: InterpAliasCmd alias = (InterpAliasCmd) entry.getValue();
344: TclList.append(interp, result, alias.name);
345: }
346: interp.setResult(result);
347: }
348:
349: /**
350: *----------------------------------------------------------------------
351: *
352: * getTargetCmd --
353: *
354: * helper function, that returns the WrappedCommand of the target
355: * command (i.e. the command which is called in the master interpreter).
356: *
357: * Results:
358: * The wrapped command.
359: *
360: * Side effects:
361: * None.
362: *
363: *----------------------------------------------------------------------
364: */
365:
366: WrappedCommand getTargetCmd(Interp interp) // Interp for error reporting.
367: throws TclException {
368: TclObject objv[] = TclList.getElements(interp, prefix);
369: String targetName = objv[0].toString();
370: return Namespace.findCommand(targetInterp, targetName, null, 0);
371: }
372:
373: /**
374: *----------------------------------------------------------------------
375: *
376: * getTargetInterp --
377: *
378: * static helper function, that returns the target interpreter of
379: * an alias with the given name in the given slave interpreter.
380: *
381: * Results:
382: * The target interpreter, or null if no alias was found.
383: *
384: * Side effects:
385: * None.
386: *
387: *----------------------------------------------------------------------
388: */
389:
390: static Interp getTargetInterp(Interp slaveInterp, String aliasName) {
391: if (!slaveInterp.aliasTable.containsKey(aliasName)) {
392: return null;
393: }
394:
395: InterpAliasCmd alias = (InterpAliasCmd) slaveInterp.aliasTable
396: .get(aliasName);
397:
398: return alias.targetInterp;
399: }
400:
401: } // end InterpAliasCmd
|