0001: /*
0002: * NamespaceCmd.java
0003: *
0004: * Copyright (c) 1993-1997 Lucent Technologies.
0005: * Copyright (c) 1997 Sun Microsystems, Inc.
0006: * Copyright (c) 1998-1999 by Scriptics Corporation.
0007: * Copyright (c) 1999 Moses DeJong
0008: *
0009: * Originally implemented by
0010: * Michael J. McLennan
0011: * Bell Labs Innovations for Lucent Technologies
0012: * mmclennan@lucent.com
0013: *
0014: * See the file "license.terms" for information on usage and
0015: * redistribution of this file, and for a DISCLAIMER OF ALL
0016: * WARRANTIES.
0017: *
0018: * RCS: @(#) $Id: NamespaceCmd.java,v 1.21 2006/01/26 19:49:18 mdejong Exp $
0019: */
0020:
0021: package tcl.lang;
0022:
0023: import java.util.*;
0024:
0025: /**
0026: * This class implements the built-in "namespace" command in Tcl.
0027: * See the user documentation for details on what it does.
0028: */
0029:
0030: public class NamespaceCmd implements InternalRep, Command {
0031: // This value corresponds to the Tcl_Obj.otherValuePtr pointer used
0032: // in the C version of Tcl 8.1. Use it to keep track of a ResolvedNsName.
0033:
0034: Namespace.ResolvedNsName otherValue = null;
0035:
0036: /*
0037: *----------------------------------------------------------------------
0038: *
0039: * Tcl_NamespaceObjCmd -> cmdProc
0040: *
0041: * Invoked to implement the "namespace" command that creates, deletes,
0042: * or manipulates Tcl namespaces. Handles the following syntax:
0043: *
0044: * namespace children ?name? ?pattern?
0045: * namespace code arg
0046: * namespace current
0047: * namespace delete ?name name...?
0048: * namespace eval name arg ?arg...?
0049: * namespace export ?-clear? ?pattern pattern...?
0050: * namespace forget ?pattern pattern...?
0051: * namespace import ?-force? ?pattern pattern...?
0052: * namespace inscope name arg ?arg...?
0053: * namespace origin name
0054: * namespace parent ?name?
0055: * namespace qualifiers string
0056: * namespace tail string
0057: * namespace which ?-command? ?-variable? name
0058: *
0059: * Results:
0060: * Returns if the command is successful. Raises Exception if
0061: * anything goes wrong.
0062: *
0063: * Side effects:
0064: * Based on the subcommand name (e.g., "import"), this procedure
0065: * dispatches to a corresponding member commands in this class.
0066: * This method's side effects depend on whatever that subcommand does.
0067: *----------------------------------------------------------------------
0068: */
0069:
0070: private static final String[] validCmds = { "children", "code",
0071: "current", "delete", "eval", "export", "forget", "import",
0072: "inscope", "origin", "parent", "qualifiers", "tail",
0073: "which" };
0074:
0075: static final private int OPT_CHILDREN = 0;
0076: static final private int OPT_CODE = 1;
0077: static final private int OPT_CURRENT = 2;
0078: static final private int OPT_DELETE = 3;
0079: static final private int OPT_EVAL = 4;
0080: static final private int OPT_EXPORT = 5;
0081: static final private int OPT_FORGET = 6;
0082: static final private int OPT_IMPORT = 7;
0083: static final private int OPT_INSCOPE = 8;
0084: static final private int OPT_ORIGIN = 9;
0085: static final private int OPT_PARENT = 10;
0086: static final private int OPT_QUALIFIERS = 11;
0087: static final private int OPT_TAIL = 12;
0088: static final private int OPT_WHICH = 13;
0089:
0090: public void cmdProc(Interp interp, TclObject[] objv)
0091: throws TclException {
0092:
0093: int i, opt;
0094:
0095: if (objv.length < 2) {
0096: throw new TclNumArgsException(interp, 1, objv,
0097: "subcommand ?arg ...?");
0098: }
0099:
0100: opt = TclIndex.get(interp, objv[1], validCmds, "option", 0);
0101:
0102: switch (opt) {
0103: case OPT_CHILDREN: {
0104: childrenCmd(interp, objv);
0105: return;
0106: }
0107: case OPT_CODE: {
0108: codeCmd(interp, objv);
0109: return;
0110: }
0111: case OPT_CURRENT: {
0112: currentCmd(interp, objv);
0113: return;
0114: }
0115: case OPT_DELETE: {
0116: deleteCmd(interp, objv);
0117: return;
0118: }
0119: case OPT_EVAL: {
0120: evalCmd(interp, objv);
0121: return;
0122: }
0123: case OPT_EXPORT: {
0124: exportCmd(interp, objv);
0125: return;
0126: }
0127: case OPT_FORGET: {
0128: forgetCmd(interp, objv);
0129: return;
0130: }
0131: case OPT_IMPORT: {
0132: importCmd(interp, objv);
0133: return;
0134: }
0135: case OPT_INSCOPE: {
0136: inscopeCmd(interp, objv);
0137: return;
0138: }
0139: case OPT_ORIGIN: {
0140: originCmd(interp, objv);
0141: return;
0142: }
0143: case OPT_PARENT: {
0144: parentCmd(interp, objv);
0145: return;
0146: }
0147: case OPT_QUALIFIERS: {
0148: qualifiersCmd(interp, objv);
0149: return;
0150: }
0151: case OPT_TAIL: {
0152: tailCmd(interp, objv);
0153: return;
0154: }
0155: case OPT_WHICH: {
0156: whichCmd(interp, objv);
0157: return;
0158: }
0159: } // end switch(opt)
0160:
0161: }
0162:
0163: /*
0164: *----------------------------------------------------------------------
0165: *
0166: * NamespaceChildrenCmd -> childrenCmd
0167: *
0168: * Invoked to implement the "namespace children" command that returns a
0169: * list containing the fully-qualified names of the child namespaces of
0170: * a given namespace. Handles the following syntax:
0171: *
0172: * namespace children ?name? ?pattern?
0173: *
0174: * Results:
0175: * Nothing.
0176: *
0177: * Side effects:
0178: * Returns a result in the interpreter's result object. If anything
0179: * goes wrong, the result is an error message.
0180: *
0181: *----------------------------------------------------------------------
0182: */
0183:
0184: private static void childrenCmd(Interp interp, TclObject[] objv)
0185: throws TclException {
0186: Namespace namespace;
0187: Namespace ns, childNs;
0188: Namespace globalNs = Namespace.getGlobalNamespace(interp);
0189: String pattern = null;
0190: StringBuffer buffer;
0191: Enumeration search;
0192: TclObject list, elem;
0193:
0194: // Get a pointer to the specified namespace, or the current namespace.
0195:
0196: if (objv.length == 2) {
0197: ns = Namespace.getCurrentNamespace(interp);
0198: } else if ((objv.length == 3) || (objv.length == 4)) {
0199: ns = getNamespaceFromObj(interp, objv[2]);
0200: if (ns == null) {
0201: throw new TclException(interp, "unknown namespace \""
0202: + objv[2].toString()
0203: + "\" in namespace children command");
0204: }
0205: } else {
0206: throw new TclNumArgsException(interp, 2, objv,
0207: "?name? ?pattern?");
0208: }
0209:
0210: // Get the glob-style pattern, if any, used to narrow the search.
0211:
0212: buffer = new StringBuffer();
0213: if (objv.length == 4) {
0214: String name = objv[3].toString();
0215:
0216: if (name.startsWith("::")) {
0217: pattern = name;
0218: } else {
0219: buffer.append(ns.fullName);
0220: if (ns != globalNs) {
0221: buffer.append("::");
0222: }
0223: buffer.append(name);
0224: pattern = buffer.toString();
0225: }
0226: }
0227:
0228: // Create a list containing the full names of all child namespaces
0229: // whose names match the specified pattern, if any.
0230:
0231: list = TclList.newInstance();
0232: for (Iterator iter = ns.childTable.entrySet().iterator(); iter
0233: .hasNext();) {
0234: Map.Entry entry = (Map.Entry) iter.next();
0235: childNs = (Namespace) entry.getValue();
0236: if ((pattern == null)
0237: || Util.stringMatch(childNs.fullName, pattern)) {
0238: elem = TclString.newInstance(childNs.fullName);
0239: TclList.append(interp, list, elem);
0240: }
0241: }
0242:
0243: interp.setResult(list);
0244: return;
0245: }
0246:
0247: /*
0248: *----------------------------------------------------------------------
0249: *
0250: * NamespaceCodeCmd -> codeCmd
0251: *
0252: * Invoked to implement the "namespace code" command to capture the
0253: * namespace context of a command. Handles the following syntax:
0254: *
0255: * namespace code arg
0256: *
0257: * Here "arg" can be a list. "namespace code arg" produces a result
0258: * equivalent to that produced by the command
0259: *
0260: * list namespace inscope [namespace current] $arg
0261: *
0262: * However, if "arg" is itself a scoped value starting with
0263: * "namespace inscope", then the result is just "arg".
0264: *
0265: * Results:
0266: * Nothing.
0267: *
0268: * Side effects:
0269: * If anything goes wrong, this procedure returns an error
0270: * message as the result in the interpreter's result object.
0271: *
0272: *----------------------------------------------------------------------
0273: */
0274:
0275: private static void codeCmd(Interp interp, TclObject[] objv)
0276: throws TclException {
0277: Namespace currNs;
0278: TclObject list, obj;
0279: String arg, p;
0280: int length;
0281: int p_ind;
0282:
0283: if (objv.length != 3) {
0284: throw new TclNumArgsException(interp, 2, objv, "arg");
0285: }
0286:
0287: // If "arg" is already a scoped value, then return it directly.
0288:
0289: arg = objv[2].toString();
0290: length = arg.length();
0291:
0292: // FIXME : we need a test for this inscope code if there is not one already!
0293: if ((length > 17) && (arg.charAt(0) == 'n')
0294: && arg.startsWith("namespace")) {
0295: for (p_ind = 9; (p_ind < length)
0296: && (arg.charAt(p_ind) == ' '); p_ind++) {
0297: // empty body: skip over spaces
0298: }
0299: if (((length - p_ind) >= 7) && (arg.charAt(p_ind) == 'i')
0300: && arg.startsWith("inscope", p_ind)) {
0301: interp.setResult(objv[2]);
0302: return;
0303: }
0304: }
0305:
0306: // Otherwise, construct a scoped command by building a list with
0307: // "namespace inscope", the full name of the current namespace, and
0308: // the argument "arg". By constructing a list, we ensure that scoped
0309: // commands are interpreted properly when they are executed later,
0310: // by the "namespace inscope" command.
0311:
0312: list = TclList.newInstance();
0313: TclList
0314: .append(interp, list, TclString
0315: .newInstance("namespace"));
0316: TclList.append(interp, list, TclString.newInstance("inscope"));
0317:
0318: currNs = Namespace.getCurrentNamespace(interp);
0319: if (currNs == Namespace.getGlobalNamespace(interp)) {
0320: obj = TclString.newInstance("::");
0321: } else {
0322: obj = TclString.newInstance(currNs.fullName);
0323: }
0324:
0325: TclList.append(interp, list, obj);
0326: TclList.append(interp, list, objv[2]);
0327:
0328: interp.setResult(list);
0329: return;
0330: }
0331:
0332: /*
0333: *----------------------------------------------------------------------
0334: *
0335: * NamespaceCurrentCmd -> currentCmd
0336: *
0337: * Invoked to implement the "namespace current" command which returns
0338: * the fully-qualified name of the current namespace. Handles the
0339: * following syntax:
0340: *
0341: * namespace current
0342: *
0343: * Results:
0344: * Returns if successful, raises TclException if something goes wrong.
0345: *
0346: * Side effects:
0347: * Returns a result in the interpreter's result object. If anything
0348: * goes wrong, the result is an error message.
0349: *
0350: *----------------------------------------------------------------------
0351: */
0352:
0353: private static void currentCmd(Interp interp, TclObject[] objv)
0354: throws TclException {
0355:
0356: Namespace currNs;
0357:
0358: if (objv.length != 2) {
0359: throw new TclNumArgsException(interp, 2, objv, null);
0360: }
0361:
0362: // The "real" name of the global namespace ("::") is the null string,
0363: // but we return "::" for it as a convenience to programmers. Note that
0364: // "" and "::" are treated as synonyms by the namespace code so that it
0365: // is still easy to do things like:
0366: //
0367: // namespace [namespace current]::bar { ... }
0368:
0369: currNs = Namespace.getCurrentNamespace(interp);
0370:
0371: if (currNs == Namespace.getGlobalNamespace(interp)) {
0372: // FIXME : appending to te result really screws everything up!
0373: // need to figure out how to disallow this!
0374: //TclString.append(interp.getResult(), "::");
0375: interp.setResult("::");
0376: } else {
0377: //TclString.append(interp.getResult(), currNs.fullName);
0378: interp.setResult(currNs.fullName);
0379: }
0380: }
0381:
0382: /*
0383: *----------------------------------------------------------------------
0384: *
0385: * NamespaceDeleteCmd -> deleteCmd
0386: *
0387: * Invoked to implement the "namespace delete" command to delete
0388: * namespace(s). Handles the following syntax:
0389: *
0390: * namespace delete ?name name...?
0391: *
0392: * Each name identifies a namespace. It may include a sequence of
0393: * namespace qualifiers separated by "::"s. If a namespace is found, it
0394: * is deleted: all variables and procedures contained in that namespace
0395: * are deleted. If that namespace is being used on the call stack, it
0396: * is kept alive (but logically deleted) until it is removed from the
0397: * call stack: that is, it can no longer be referenced by name but any
0398: * currently executing procedure that refers to it is allowed to do so
0399: * until the procedure returns. If the namespace can't be found, this
0400: * procedure returns an error. If no namespaces are specified, this
0401: * command does nothing.
0402: *
0403: * Results:
0404: * Returns if successful, raises TclException if something goes wrong.
0405: *
0406: * Side effects:
0407: * Deletes the specified namespaces. If anything goes wrong, this
0408: * procedure returns an error message in the interpreter's
0409: * result object.
0410: *
0411: *----------------------------------------------------------------------
0412: */
0413:
0414: private static void deleteCmd(Interp interp, TclObject[] objv)
0415: throws TclException {
0416: Namespace namespace;
0417: String name;
0418: int i;
0419:
0420: if (objv.length < 2) {
0421: throw new TclNumArgsException(interp, 2, objv,
0422: "?name name...?");
0423: }
0424:
0425: // Destroying one namespace may cause another to be destroyed. Break
0426: // this into two passes: first check to make sure that all namespaces on
0427: // the command line are valid, and report any errors.
0428:
0429: for (i = 2; i < objv.length; i++) {
0430: name = objv[i].toString();
0431: namespace = Namespace.findNamespace(interp, name, null, 0);
0432:
0433: if (namespace == null) {
0434: throw new TclException(interp, "unknown namespace \""
0435: + objv[i].toString()
0436: + "\" in namespace delete command");
0437: }
0438: }
0439:
0440: // Okay, now delete each namespace.
0441:
0442: for (i = 2; i < objv.length; i++) {
0443: name = objv[i].toString();
0444: namespace = Namespace.findNamespace(interp, name, null, 0);
0445:
0446: if (namespace != null) {
0447: Namespace.deleteNamespace(namespace);
0448: }
0449: }
0450: }
0451:
0452: /*
0453: *----------------------------------------------------------------------
0454: *
0455: * NamespaceEvalCmd -> evalCmd
0456: *
0457: * Invoked to implement the "namespace eval" command. Executes
0458: * commands in a namespace. If the namespace does not already exist,
0459: * it is created. Handles the following syntax:
0460: *
0461: * namespace eval name arg ?arg...?
0462: *
0463: * If more than one arg argument is specified, the command that is
0464: * executed is the result of concatenating the arguments together with
0465: * a space between each argument.
0466: *
0467: * Results:
0468: * Returns if successful, raises TclException if something goes wrong.
0469: *
0470: * Side effects:
0471: * Returns the result of the command in the interpreter's result
0472: * object. If anything goes wrong, this procedure returns an error
0473: * message as the result.
0474: *
0475: *----------------------------------------------------------------------
0476: */
0477:
0478: private static void evalCmd(Interp interp, TclObject[] objv)
0479: throws TclException {
0480: Namespace namespace;
0481: CallFrame frame;
0482: String cmd;
0483: String name;
0484: int length;
0485:
0486: if (objv.length < 4) {
0487: throw new TclNumArgsException(interp, 2, objv,
0488: "name arg ?arg...?");
0489: }
0490:
0491: // Try to resolve the namespace reference, caching the result in the
0492: // namespace object along the way.
0493:
0494: namespace = getNamespaceFromObj(interp, objv[2]);
0495:
0496: // If the namespace wasn't found, try to create it.
0497:
0498: if (namespace == null) {
0499: name = objv[2].toString();
0500: namespace = Namespace.createNamespace(interp, name, null);
0501: if (namespace == null) {
0502: // FIXME : result hack, we get the interp result and throw it!
0503: throw new TclException(interp, interp.getResult()
0504: .toString());
0505: }
0506: }
0507:
0508: // Make the specified namespace the current namespace and evaluate
0509: // the command(s).
0510:
0511: frame = interp.newCallFrame();
0512: Namespace.pushCallFrame(interp, frame, namespace, false);
0513:
0514: try {
0515: if (objv.length == 4) {
0516: interp.eval(objv[3], 0);
0517: } else {
0518: TclObject obj = Util.concat(3, objv.length, objv);
0519:
0520: // eval() will delete the object when it decrements its
0521: // refcount after eval'ing it.
0522:
0523: interp.eval(obj, 0); // do not pass TCL_EVAL_DIRECT, for compiler only
0524: }
0525: } catch (TclException ex) {
0526: if (ex.getCompletionCode() == TCL.ERROR) {
0527: interp.addErrorInfo("\n (in namespace eval \""
0528: + namespace.fullName + "\" script line "
0529: + interp.errorLine + ")");
0530: }
0531: throw ex;
0532: } finally {
0533: Namespace.popCallFrame(interp);
0534: }
0535:
0536: return;
0537: }
0538:
0539: /*
0540: *----------------------------------------------------------------------
0541: *
0542: * NamespaceExportCmd -> exportCmd
0543: *
0544: * Invoked to implement the "namespace export" command that specifies
0545: * which commands are exported from a namespace. The exported commands
0546: * are those that can be imported into another namespace using
0547: * "namespace import". Both commands defined in a namespace and
0548: * commands the namespace has imported can be exported by a
0549: * namespace. This command has the following syntax:
0550: *
0551: * namespace export ?-clear? ?pattern pattern...?
0552: *
0553: * Each pattern may contain "string match"-style pattern matching
0554: * special characters, but the pattern may not include any namespace
0555: * qualifiers: that is, the pattern must specify commands in the
0556: * current (exporting) namespace. The specified patterns are appended
0557: * onto the namespace's list of export patterns.
0558: *
0559: * To reset the namespace's export pattern list, specify the "-clear"
0560: * flag.
0561: *
0562: * If there are no export patterns and the "-clear" flag isn't given,
0563: * this command returns the namespace's current export list.
0564: *
0565: * Results:
0566: * Returns if successful, raises TclException if something goes wrong.
0567: *
0568: * Side effects:
0569: * Returns a result in the interpreter's result object. If anything
0570: * goes wrong, the result is an error message.
0571: *
0572: *----------------------------------------------------------------------
0573: */
0574:
0575: private static void exportCmd(Interp interp, TclObject[] objv)
0576: throws TclException {
0577: Namespace currNs = Namespace.getCurrentNamespace(interp);
0578: String pattern, string;
0579: boolean resetListFirst = false;
0580: int firstArg, patternCt, i;
0581:
0582: if (objv.length < 2) {
0583: throw new TclNumArgsException(interp, 2, objv,
0584: "?-clear? ?pattern pattern...?");
0585: }
0586:
0587: // Process the optional "-clear" argument.
0588:
0589: firstArg = 2;
0590: if (firstArg < objv.length) {
0591: string = objv[firstArg].toString();
0592: if (string.equals("-clear")) {
0593: resetListFirst = true;
0594: firstArg++;
0595: }
0596: }
0597:
0598: // If no pattern arguments are given, and "-clear" isn't specified,
0599: // return the namespace's current export pattern list.
0600:
0601: patternCt = (objv.length - firstArg);
0602: if (patternCt == 0) {
0603: if (firstArg > 2) {
0604: return;
0605: } else { // create list with export patterns
0606: TclObject list = TclList.newInstance();
0607: Namespace.appendExportList(interp, currNs, list);
0608: interp.setResult(list);
0609: return;
0610: }
0611: }
0612:
0613: // Add each pattern to the namespace's export pattern list.
0614:
0615: for (i = firstArg; i < objv.length; i++) {
0616: pattern = objv[i].toString();
0617: Namespace.exportList(interp, currNs, pattern,
0618: ((i == firstArg) ? resetListFirst : false));
0619: }
0620: return;
0621: }
0622:
0623: /*
0624: *----------------------------------------------------------------------
0625: *
0626: * NamespaceForgetCmd -> forgetCmd
0627: *
0628: * Invoked to implement the "namespace forget" command to remove
0629: * imported commands from a namespace. Handles the following syntax:
0630: *
0631: * namespace forget ?pattern pattern...?
0632: *
0633: * Each pattern is a name like "foo::*" or "a::b::x*". That is, the
0634: * pattern may include the special pattern matching characters
0635: * recognized by the "string match" command, but only in the command
0636: * name at the end of the qualified name; the special pattern
0637: * characters may not appear in a namespace name. All of the commands
0638: * that match that pattern are checked to see if they have an imported
0639: * command in the current namespace that refers to the matched
0640: * command. If there is an alias, it is removed.
0641: *
0642: * Results:
0643: * Returns if successful, raises TclException if something goes wrong.
0644: *
0645: * Side effects:
0646: * Imported commands are removed from the current namespace. If
0647: * anything goes wrong, this procedure returns an error message in the
0648: * interpreter's result object.
0649: *
0650: *----------------------------------------------------------------------
0651: */
0652:
0653: private static void forgetCmd(Interp interp, TclObject[] objv)
0654: throws TclException {
0655:
0656: String pattern;
0657: int i;
0658:
0659: if (objv.length < 2) {
0660: throw new TclNumArgsException(interp, 2, objv,
0661: "?pattern pattern...?");
0662: }
0663:
0664: for (i = 2; i < objv.length; i++) {
0665: pattern = objv[i].toString();
0666: Namespace.forgetImport(interp, null, pattern);
0667: }
0668: return;
0669: }
0670:
0671: /*
0672: *----------------------------------------------------------------------
0673: *
0674: * NamespaceImportCmd -> importCmd
0675: *
0676: * Invoked to implement the "namespace import" command that imports
0677: * commands into a namespace. Handles the following syntax:
0678: *
0679: * namespace import ?-force? ?pattern pattern...?
0680: *
0681: * Each pattern is a namespace-qualified name like "foo::*",
0682: * "a::b::x*", or "bar::p". That is, the pattern may include the
0683: * special pattern matching characters recognized by the "string match"
0684: * command, but only in the command name at the end of the qualified
0685: * name; the special pattern characters may not appear in a namespace
0686: * name. All of the commands that match the pattern and which are
0687: * exported from their namespace are made accessible from the current
0688: * namespace context. This is done by creating a new "imported command"
0689: * in the current namespace that points to the real command in its
0690: * original namespace; when the imported command is called, it invokes
0691: * the real command.
0692: *
0693: * If an imported command conflicts with an existing command, it is
0694: * treated as an error. But if the "-force" option is included, then
0695: * existing commands are overwritten by the imported commands.
0696: *
0697: * Results:
0698: * Returns if successful, raises TclException if something goes wrong.
0699: *
0700: * Side effects:
0701: * Adds imported commands to the current namespace. If anything goes
0702: * wrong, this procedure returns an error message in the interpreter's
0703: * result object.
0704: *
0705: *----------------------------------------------------------------------
0706: */
0707:
0708: private static void importCmd(Interp interp, TclObject[] objv)
0709: throws TclException {
0710:
0711: boolean allowOverwrite = false;
0712: String string, pattern;
0713: int i;
0714: int firstArg;
0715:
0716: if (objv.length < 2) {
0717: throw new TclNumArgsException(interp, 2, objv,
0718: "?-force? ?pattern pattern...?");
0719: }
0720:
0721: // Skip over the optional "-force" as the first argument.
0722:
0723: firstArg = 2;
0724: if (firstArg < objv.length) {
0725: string = objv[firstArg].toString();
0726: if (string.equals("-force")) {
0727: allowOverwrite = true;
0728: firstArg++;
0729: }
0730: }
0731:
0732: // Handle the imports for each of the patterns.
0733:
0734: for (i = firstArg; i < objv.length; i++) {
0735: pattern = objv[i].toString();
0736: Namespace.importList(interp, null, pattern, allowOverwrite);
0737: }
0738: return;
0739: }
0740:
0741: /*
0742: *----------------------------------------------------------------------
0743: *
0744: * NamespaceInscopeCmd -> inscopeCmd
0745: *
0746: * Invoked to implement the "namespace inscope" command that executes a
0747: * script in the context of a particular namespace. This command is not
0748: * expected to be used directly by programmers; calls to it are
0749: * generated implicitly when programs use "namespace code" commands
0750: * to register callback scripts. Handles the following syntax:
0751: *
0752: * namespace inscope name arg ?arg...?
0753: *
0754: * The "namespace inscope" command is much like the "namespace eval"
0755: * command except that it has lappend semantics and the namespace must
0756: * already exist. It treats the first argument as a list, and appends
0757: * any arguments after the first onto the end as proper list elements.
0758: * For example,
0759: *
0760: * namespace inscope ::foo a b c d
0761: *
0762: * is equivalent to
0763: *
0764: * namespace eval ::foo [concat a [list b c d]]
0765: *
0766: * This lappend semantics is important because many callback scripts
0767: * are actually prefixes.
0768: *
0769: * Results:
0770: * Returns if successful, raises TclException if something goes wrong.
0771: *
0772: * Side effects:
0773: * Returns a result in the Tcl interpreter's result object.
0774: *
0775: *----------------------------------------------------------------------
0776: */
0777:
0778: private static void inscopeCmd(Interp interp, TclObject[] objv)
0779: throws TclException {
0780: Namespace namespace;
0781: CallFrame frame;
0782: int i, result;
0783:
0784: if (objv.length < 4) {
0785: throw new TclNumArgsException(interp, 2, objv,
0786: "name arg ?arg...?");
0787: }
0788:
0789: // Resolve the namespace reference.
0790:
0791: namespace = getNamespaceFromObj(interp, objv[2]);
0792: if (namespace == null) {
0793: throw new TclException(interp, "unknown namespace \""
0794: + objv[2].toString()
0795: + "\" in inscope namespace command");
0796: }
0797:
0798: // Make the specified namespace the current namespace.
0799:
0800: frame = interp.newCallFrame();
0801: Namespace.pushCallFrame(interp, frame, namespace, false);
0802:
0803: // Execute the command. If there is just one argument, just treat it as
0804: // a script and evaluate it. Otherwise, create a list from the arguments
0805: // after the first one, then concatenate the first argument and the list
0806: // of extra arguments to form the command to evaluate.
0807:
0808: try {
0809: if (objv.length == 4) {
0810: interp.eval(objv[3], 0);
0811: } else {
0812: TclObject[] concatObjv = new TclObject[2];
0813: TclObject list;
0814: TclObject cmd;
0815:
0816: list = TclList.newInstance();
0817: for (i = 4; i < objv.length; i++) {
0818: try {
0819: TclList.append(interp, list, objv[i]);
0820: } catch (TclException ex) {
0821: list.release(); // free unneeded obj
0822: throw ex;
0823: }
0824: }
0825:
0826: concatObjv[0] = objv[3];
0827: concatObjv[1] = list;
0828: list.preserve();
0829: cmd = Util.concat(0, 1, concatObjv);
0830: try {
0831: interp.eval(cmd, 0); // do not pass TCL_EVAL_DIRECT, for compiler only
0832: } finally {
0833: list.release(); // we're done with the list object
0834: }
0835: }
0836: } catch (TclException ex) {
0837: if (ex.getCompletionCode() == TCL.ERROR) {
0838: interp.addErrorInfo("\n (in namespace inscope \""
0839: + namespace.fullName + "\" script line "
0840: + interp.errorLine + ")");
0841: }
0842: throw ex;
0843: } finally {
0844: Namespace.popCallFrame(interp);
0845: }
0846:
0847: return;
0848: }
0849:
0850: /*
0851: *----------------------------------------------------------------------
0852: *
0853: * NamespaceOriginCmd -> originCmd
0854: *
0855: * Invoked to implement the "namespace origin" command to return the
0856: * fully-qualified name of the "real" command to which the specified
0857: * "imported command" refers. Handles the following syntax:
0858: *
0859: * namespace origin name
0860: *
0861: * Results:
0862: * An imported command is created in an namespace when that namespace
0863: * imports a command from another namespace. If a command is imported
0864: * into a sequence of namespaces a, b,...,n where each successive
0865: * namespace just imports the command from the previous namespace, this
0866: * command returns the fully-qualified name of the original command in
0867: * the first namespace, a. If "name" does not refer to an alias, its
0868: * fully-qualified name is returned. The returned name is stored in the
0869: * interpreter's result object. This procedure returns TCL_OK if
0870: * successful, and TCL_ERROR if anything goes wrong.
0871: *
0872: * Side effects:
0873: * If anything goes wrong, this procedure returns an error message in
0874: * the interpreter's result object.
0875: *
0876: *----------------------------------------------------------------------
0877: */
0878:
0879: private static void originCmd(Interp interp, TclObject[] objv)
0880: throws TclException {
0881: WrappedCommand command, origCommand;
0882:
0883: if (objv.length != 3) {
0884: throw new TclNumArgsException(interp, 2, objv, "name");
0885: }
0886:
0887: // FIXME : is this the right way to search for a command?
0888:
0889: //command = Tcl_GetCommandFromObj(interp, objv[2]);
0890: command = Namespace.findCommand(interp, objv[2].toString(),
0891: null, 0);
0892:
0893: if (command == null) {
0894: throw new TclException(interp, "invalid command name \""
0895: + objv[2].toString() + "\"");
0896: }
0897:
0898: origCommand = Namespace.getOriginalCommand(command);
0899: if (origCommand == null) {
0900: // The specified command isn't an imported command. Return the
0901: // command's name qualified by the full name of the namespace it
0902: // was defined in.
0903:
0904: interp.setResult(interp.getCommandFullName(command));
0905: } else {
0906: interp.setResult(interp.getCommandFullName(origCommand));
0907: }
0908: return;
0909: }
0910:
0911: /*
0912: *----------------------------------------------------------------------
0913: *
0914: * NamespaceParentCmd -> parentCmd
0915: *
0916: * Invoked to implement the "namespace parent" command that returns the
0917: * fully-qualified name of the parent namespace for a specified
0918: * namespace. Handles the following syntax:
0919: *
0920: * namespace parent ?name?
0921: *
0922: * Results:
0923: * Returns if successful, raises TclException if something goes wrong.
0924: *
0925: * Side effects:
0926: * Returns a result in the interpreter's result object. If anything
0927: * goes wrong, the result is an error message.
0928: *
0929: *----------------------------------------------------------------------
0930: */
0931:
0932: private static void parentCmd(Interp interp, TclObject[] objv)
0933: throws TclException {
0934: Namespace ns;
0935:
0936: if (objv.length == 2) {
0937: ns = Namespace.getCurrentNamespace(interp);
0938: } else if (objv.length == 3) {
0939: ns = getNamespaceFromObj(interp, objv[2]);
0940: if (ns == null) {
0941: throw new TclException(interp, "unknown namespace \""
0942: + objv[2].toString()
0943: + "\" in namespace parent command");
0944: }
0945: } else {
0946: throw new TclNumArgsException(interp, 2, objv, "?name?");
0947: }
0948:
0949: // Report the parent of the specified namespace.
0950:
0951: if (ns.parent != null) {
0952: interp.setResult(ns.parent.fullName);
0953: }
0954: }
0955:
0956: /*
0957: *----------------------------------------------------------------------
0958: *
0959: * NamespaceQualifiersCmd -> qualifiersCmd
0960: *
0961: * Invoked to implement the "namespace qualifiers" command that returns
0962: * any leading namespace qualifiers in a string. These qualifiers are
0963: * namespace names separated by "::"s. For example, for "::foo::p" this
0964: * command returns "::foo", and for "::" it returns "". This command
0965: * is the complement of the "namespace tail" command. Note that this
0966: * command does not check whether the "namespace" names are, in fact,
0967: * the names of currently defined namespaces. Handles the following
0968: * syntax:
0969: *
0970: * namespace qualifiers string
0971: *
0972: * Results:
0973: * Returns if successful, raises TclException if something goes wrong.
0974: *
0975: * Side effects:
0976: * Returns a result in the interpreter's result object. If anything
0977: * goes wrong, the result is an error message.
0978: *
0979: *----------------------------------------------------------------------
0980: */
0981:
0982: private static void qualifiersCmd(Interp interp, TclObject[] objv)
0983: throws TclException {
0984: String name;
0985: int p;
0986:
0987: if (objv.length != 3) {
0988: throw new TclNumArgsException(interp, 2, objv, "string");
0989: }
0990:
0991: // Find the end of the string, then work backward and find
0992: // the start of the last "::" qualifier.
0993:
0994: name = objv[2].toString();
0995: p = name.length();
0996:
0997: while (--p >= 0) {
0998: if ((name.charAt(p) == ':') && (p > 0)
0999: && (name.charAt(p - 1) == ':')) {
1000: p -= 2; // back up over the ::
1001: while ((p >= 0) && (name.charAt(p) == ':')) {
1002: p--; // back up over the preceeding :
1003: }
1004: break;
1005: }
1006: }
1007:
1008: if (p >= 0) {
1009: interp.setResult(name.substring(0, p + 1));
1010: }
1011: // When no result is set the empty string is the result
1012: return;
1013: }
1014:
1015: /*
1016: *----------------------------------------------------------------------
1017: *
1018: * NamespaceTailCmd -> tailCmd
1019: *
1020: * Invoked to implement the "namespace tail" command that returns the
1021: * trailing name at the end of a string with "::" namespace
1022: * qualifiers. These qualifiers are namespace names separated by
1023: * "::"s. For example, for "::foo::p" this command returns "p", and for
1024: * "::" it returns "". This command is the complement of the "namespace
1025: * qualifiers" command. Note that this command does not check whether
1026: * the "namespace" names are, in fact, the names of currently defined
1027: * namespaces. Handles the following syntax:
1028: *
1029: * namespace tail string
1030: *
1031: * Results:
1032: * Returns if successful, raises TclException if something goes wrong.
1033: *
1034: * Side effects:
1035: * Returns a result in the interpreter's result object. If anything
1036: * goes wrong, the result is an error message.
1037: *
1038: *----------------------------------------------------------------------
1039: */
1040:
1041: private static void tailCmd(Interp interp, TclObject[] objv)
1042: throws TclException {
1043: String name, tail;
1044:
1045: if (objv.length != 3) {
1046: throw new TclNumArgsException(interp, 2, objv, "string");
1047: }
1048:
1049: name = objv[2].toString();
1050: tail = NamespaceCmd.tail(name);
1051: interp.setResult(tail);
1052: return;
1053: }
1054:
1055: // Given a possibly qualified name, return the namespace tail
1056: // substring that appears after the last pair of colons "::".
1057:
1058: static String tail(String qname) {
1059:
1060: // Find the last location of "::" in the string.
1061:
1062: int i = qname.lastIndexOf("::");
1063: String tail;
1064:
1065: if (i == -1) {
1066: tail = qname;
1067: } else {
1068: i += 2; // just after last "::"
1069: if (i >= qname.length()) {
1070: tail = "";
1071: } else {
1072: tail = qname.substring(i);
1073: }
1074: }
1075: return tail;
1076: }
1077:
1078: /*
1079: *----------------------------------------------------------------------
1080: *
1081: * NamespaceWhichCmd -> whichCmd
1082: *
1083: * Invoked to implement the "namespace which" command that returns the
1084: * fully-qualified name of a command or variable. If the specified
1085: * command or variable does not exist, it returns "". Handles the
1086: * following syntax:
1087: *
1088: * namespace which ?-command? ?-variable? name
1089: *
1090: * Results:
1091: * Returns if successful, raises TclException if something goes wrong.
1092: *
1093: * Side effects:
1094: * Returns a result in the interpreter's result object. If anything
1095: * goes wrong, the result is an error message.
1096: *
1097: *----------------------------------------------------------------------
1098: */
1099:
1100: private static void whichCmd(Interp interp, TclObject[] objv)
1101: throws TclException {
1102: String arg;
1103: WrappedCommand cmd;
1104: Var variable;
1105: int argIndex, lookup;
1106:
1107: if (objv.length < 3) {
1108: throw new TclNumArgsException(interp, 2, objv,
1109: "?-command? ?-variable? name");
1110: }
1111:
1112: // Look for a flag controlling the lookup.
1113:
1114: argIndex = 2;
1115: lookup = 0; // assume command lookup by default
1116: arg = objv[2].toString();
1117: if ((arg.length() > 1) && (arg.charAt(0) == '-')) {
1118: if (arg.equals("-command")) {
1119: lookup = 0;
1120: } else if (arg.equals("-variable")) {
1121: lookup = 1;
1122: } else {
1123: throw new TclNumArgsException(interp, 2, objv,
1124: "?-command? ?-variable? name");
1125: }
1126: argIndex = 3;
1127: }
1128: if (objv.length != (argIndex + 1)) {
1129: throw new TclNumArgsException(interp, 2, objv,
1130: "?-command? ?-variable? name");
1131: }
1132:
1133: // FIXME : check that this implementation works!
1134:
1135: switch (lookup) {
1136: case 0: // -command
1137: arg = objv[argIndex].toString();
1138:
1139: // FIXME : is this the right way to lookup a Command token?
1140: //cmd = Tcl_GetCommandFromObj(interp, objv[argIndex]);
1141: cmd = Namespace.findCommand(interp, arg, null, 0);
1142:
1143: if (cmd == null) {
1144: return; // cmd not found, just return (no error)
1145: }
1146: interp.setResult(interp.getCommandFullName(cmd));
1147: return;
1148:
1149: case 1: // -variable
1150: arg = objv[argIndex].toString();
1151: variable = Namespace.findNamespaceVar(interp, arg, null, 0);
1152: if (variable != null) {
1153: interp.setResult(Var.getVariableFullName(interp,
1154: variable));
1155: }
1156: return;
1157: }
1158:
1159: return;
1160: }
1161:
1162: /*
1163: *----------------------------------------------------------------------
1164: *
1165: * FreeNsNameInternalRep -> dispose
1166: *
1167: * Frees the resources associated with a object's internal
1168: * representation. See src/tcljava/tcl/lang/InternalRep.java
1169: *
1170: * Results:
1171: * None.
1172: *
1173: * Side effects:
1174: * Decrements the ref count of any Namespace structure pointed
1175: * to by the nsName's internal representation. If there are no more
1176: * references to the namespace, it's structure will be freed.
1177: *
1178: *----------------------------------------------------------------------
1179: */
1180:
1181: public void dispose() {
1182: final boolean debug = false;
1183: if (debug) {
1184: System.out.println("dispose() called for namespace object "
1185: + (otherValue == null ? null : otherValue.ns));
1186: }
1187:
1188: Namespace.ResolvedNsName resName = otherValue;
1189: Namespace ns;
1190:
1191: // Decrement the reference count of the namespace. If there are no
1192: // more references, free it up.
1193:
1194: if (resName != null) {
1195: resName.refCount--;
1196: if (resName.refCount == 0) {
1197:
1198: // Decrement the reference count for the cached namespace. If
1199: // the namespace is dead, and there are no more references to
1200: // it, free it.
1201:
1202: ns = resName.ns;
1203: ns.refCount--;
1204: if ((ns.refCount == 0)
1205: && ((ns.flags & Namespace.NS_DEAD) != 0)) {
1206: Namespace.free(ns);
1207: }
1208: otherValue = null;
1209: }
1210: }
1211: }
1212:
1213: /*
1214: *----------------------------------------------------------------------
1215: *
1216: * DupNsNameInternalRep -> duplicate
1217: *
1218: * Get a copy of this Object for copy-on-write
1219: * operations. We just increment its useCount and return the same
1220: * ReflectObject because ReflectObject's cannot be modified, so
1221: * they don't need copy-on-write protections.
1222: *
1223: * Results:
1224: * None.
1225: *
1226: * Side effects:
1227: * None.
1228: *
1229: *----------------------------------------------------------------------
1230: */
1231:
1232: public InternalRep duplicate() {
1233: final boolean debug = false;
1234: if (debug) {
1235: System.out
1236: .println("duplicate() called for namespace object "
1237: + (otherValue == null ? null
1238: : otherValue.ns));
1239: }
1240:
1241: Namespace.ResolvedNsName resName = otherValue;
1242:
1243: if (resName != null) {
1244: resName.refCount++;
1245: }
1246:
1247: return this ;
1248: }
1249:
1250: /*
1251: *----------------------------------------------------------------------
1252: *
1253: * SetNsNameFromAny -> setNsNameFromAny
1254: *
1255: * Attempt to generate a nsName internal representation for a
1256: * TclObject.
1257: *
1258: * Results:
1259: * Returns if the value could be converted to a proper
1260: * namespace reference. Otherwise, raises TclException.
1261: *
1262: * Side effects:
1263: * If successful, the object is made a nsName object. Its internal rep
1264: * is set to point to a ResolvedNsName, which contains a cached pointer
1265: * to the Namespace. Reference counts are kept on both the
1266: * ResolvedNsName and the Namespace, so we can keep track of their
1267: * usage and free them when appropriate.
1268: *
1269: *----------------------------------------------------------------------
1270: */
1271:
1272: static void setNsNameFromAny(Interp interp, // Reference to the namespace in which to
1273: // resolve name. Also used for error
1274: // reporting if not null.
1275: TclObject tobj // The TclObject to convert.
1276: ) throws TclException // If object could not be converted
1277: {
1278: String name;
1279: Namespace ns;
1280: Namespace.ResolvedNsName resName;
1281:
1282: // Get the string representation.
1283: name = tobj.toString();
1284:
1285: // Look for the namespace "name" in the current namespace. If there is
1286: // an error parsing the (possibly qualified) name, return an error.
1287: // If the namespace isn't found, we convert the object to an nsName
1288: // object with a null ResolvedNsName internal rep.
1289:
1290: Namespace.GetNamespaceForQualNameResult gnfqnr = interp.getnfqnResult;
1291: Namespace.getNamespaceForQualName(interp, name, null,
1292: Namespace.FIND_ONLY_NS, gnfqnr);
1293: ns = gnfqnr.ns;
1294:
1295: // If we found a namespace, then create a new ResolvedNsName structure
1296: // that holds a reference to it.
1297:
1298: if (ns != null) {
1299: Namespace currNs = Namespace.getCurrentNamespace(interp);
1300:
1301: ns.refCount++;
1302: resName = new Namespace.ResolvedNsName();
1303: resName.ns = ns;
1304: resName.nsId = ns.nsId;
1305: resName.refNs = currNs;
1306: resName.refCount = 1;
1307: } else {
1308: resName = null;
1309: }
1310:
1311: // By setting the new internal rep we free up the old one.
1312:
1313: // FIXME : should a NamespaceCmd wrap a ResolvedNsName?
1314: // this is confusing because it seems like the C code uses
1315: // a ResolvedNsName like it is the InternalRep.
1316:
1317: NamespaceCmd wrap = new NamespaceCmd();
1318: wrap.otherValue = resName;
1319: tobj.setInternalRep(wrap);
1320:
1321: return;
1322: }
1323:
1324: /*
1325: *----------------------------------------------------------------------
1326: *
1327: * GetNamespaceFromObj -> getNamespaceFromObj
1328: *
1329: * Returns the namespace specified by the name in a TclObject.
1330: *
1331: * Results:
1332: * This method will return the Namespace object whose name
1333: * is stored in the obj argument. If the namespace can't be found,
1334: * a TclException is raised.
1335: *
1336: * Side effects:
1337: * May update the internal representation for the object, caching the
1338: * namespace reference. The next time this procedure is called, the
1339: * namespace value can be found quickly.
1340: *
1341: * If anything goes wrong, an error message is left in the
1342: * interpreter's result object.
1343: *
1344: *----------------------------------------------------------------------
1345: */
1346:
1347: static Namespace getNamespaceFromObj(Interp interp, // The current interpreter.
1348: TclObject obj // The object to be resolved as the name
1349: // of a namespace.
1350: ) throws TclException {
1351: Namespace.ResolvedNsName resName;
1352: Namespace ns;
1353: Namespace currNs = Namespace.getCurrentNamespace(interp);
1354: int result;
1355:
1356: // Get the internal representation, converting to a namespace type if
1357: // needed. The internal representation is a ResolvedNsName that points
1358: // to the actual namespace.
1359:
1360: if (!(obj.getInternalRep() instanceof NamespaceCmd)) {
1361: NamespaceCmd.setNsNameFromAny(interp, obj);
1362: }
1363: resName = ((NamespaceCmd) obj.getInternalRep()).otherValue;
1364:
1365: // Check the context namespace of the resolved symbol to make sure that
1366: // it is fresh. If not, then force another conversion to the namespace
1367: // type, to discard the old rep and create a new one. Note that we
1368: // verify that the namespace id of the cached namespace is the same as
1369: // the id when we cached it; this insures that the namespace wasn't
1370: // deleted and a new one created at the same address.
1371:
1372: ns = null;
1373: if ((resName != null) && (resName.refNs == currNs)
1374: && (resName.nsId == resName.ns.nsId)) {
1375: ns = resName.ns;
1376: if ((ns.flags & Namespace.NS_DEAD) != 0) {
1377: ns = null;
1378: }
1379: }
1380: if (ns == null) { // try again
1381: NamespaceCmd.setNsNameFromAny(interp, obj);
1382: resName = ((NamespaceCmd) obj.getInternalRep()).otherValue;
1383: if (resName != null) {
1384: ns = resName.ns;
1385: if ((ns.flags & Namespace.NS_DEAD) != 0) {
1386: ns = null;
1387: }
1388: }
1389: }
1390: return ns;
1391: }
1392:
1393: /*
1394: *----------------------------------------------------------------------
1395: *
1396: * UpdateStringOfNsName -> toString
1397: *
1398: * Return the string representation for a nsName object.
1399: * This method is called only by TclObject.toString()
1400: * when TclObject.stringRep is null.
1401: *
1402: * Results:
1403: * None.
1404: *
1405: * Side effects:
1406: * None.
1407: *
1408: *----------------------------------------------------------------------
1409: */
1410:
1411: public String toString() {
1412: final boolean debug = false;
1413:
1414: if (debug) {
1415: System.out
1416: .println("toString() called for namespace object "
1417: + (otherValue == null ? null
1418: : otherValue.ns));
1419: }
1420:
1421: Namespace.ResolvedNsName resName = otherValue;
1422: Namespace ns;
1423: String name = "";
1424:
1425: if ((resName != null) && (resName.nsId == resName.ns.nsId)) {
1426: ns = resName.ns;
1427: if ((ns.flags & Namespace.NS_DEAD) != 0) {
1428: ns = null;
1429: }
1430: if (ns != null) {
1431: name = ns.fullName;
1432: }
1433: }
1434:
1435: return name;
1436: }
1437:
1438: }
|