001: /*
002: * SwitchCmd.java
003: *
004: * Copyright (c) 1997 Sun Microsystems, Inc.
005: *
006: * See the file "license.terms" for information on usage and
007: * redistribution of this file, and for a DISCLAIMER OF ALL
008: * WARRANTIES.
009: *
010: * RCS: @(#) $Id: SwitchCmd.java,v 1.3 2005/11/04 21:02:14 mdejong Exp $
011: *
012: */
013:
014: package tcl.lang;
015:
016: /**
017: * This class implements the built-in "switch" command in Tcl.
018: */
019:
020: class SwitchCmd implements Command {
021:
022: static final private String validCmds[] = { "-exact", "-glob",
023: "-regexp", "--" };
024: private static final int EXACT = 0;
025: private static final int GLOB = 1;
026: private static final int REGEXP = 2;
027: private static final int LAST = 3;
028:
029: /*
030: *-----------------------------------------------------------------------------
031: *
032: * cmdProc --
033: *
034: * This procedure is invoked to process the "switch" Tcl statement.
035: * See the user documentation for details on what it does.
036: *
037: * Results:
038: * None.
039: *
040: * Side effects:
041: * See the user documentation.
042: *
043: *-----------------------------------------------------------------------------
044: */
045:
046: public void cmdProc(Interp interp, // Current interpreter.
047: TclObject[] objv) // Arguments to "switch" statement.
048: throws TclException {
049: int i, mode, pbStart, pbOffset;
050: boolean matched, foundmode, splitObjs;
051: String string;
052: TclObject[] switchObjv = null;
053:
054: mode = EXACT;
055: foundmode = false;
056: for (i = 1; i < objv.length; i++) {
057: if (!objv[i].toString().startsWith("-")) {
058: break;
059: }
060: int opt = TclIndex.get(interp, objv[i], validCmds,
061: "option", 0);
062: if (opt == LAST) {
063: i++;
064: break;
065: } else if (opt > LAST) {
066: throw new TclException(interp,
067: "SwitchCmd.cmdProc: bad option " + opt
068: + " index to validCmds");
069: } else {
070: if (foundmode) {
071: throw new TclException(interp, "bad option \""
072: + objv[i] + "\": " + validCmds[mode]
073: + " option already found");
074: }
075: foundmode = true;
076: mode = opt;
077: }
078: }
079:
080: if (objv.length - i < 2) {
081: throw new TclNumArgsException(interp, 1, objv,
082: "?switches? string pattern body ... ?default body?");
083: }
084: string = objv[i].toString();
085: i++;
086:
087: // If all of the pattern/command pairs are lumped into a single
088: // argument, split them out again.
089:
090: splitObjs = false;
091: if (objv.length - i == 1) {
092: switchObjv = TclList.getElements(interp, objv[i]);
093:
094: // Ensure that the list is non-empty.
095:
096: if (switchObjv.length == 0) {
097: throw new TclNumArgsException(interp, 1, objv,
098: "?switches? string {pattern body ... ?default body?}");
099: }
100: pbStart = 0;
101: splitObjs = true;
102: } else {
103: switchObjv = objv;
104: pbStart = i;
105: }
106:
107: if (((switchObjv.length - pbStart) % 2) != 0) {
108: interp.resetResult();
109:
110: // Check if this can be due to a badly placed comment
111: // in the switch block.
112: //
113: // The following is an heuristic to detect the infamous
114: // "comment in switch" error: just check if a pattern
115: // begins with '#'.
116:
117: if (splitObjs) {
118: for (int off = pbStart; off < switchObjv.length; off += 2) {
119: if (switchObjv[off].toString().startsWith("#")) {
120: throw new TclException(
121: interp,
122: "extra switch pattern with no body"
123: + ", this may be due to a "
124: + "comment incorrectly placed outside of a "
125: + "switch body - see the \"switch\" "
126: + "documentation");
127: }
128: }
129: }
130: throw new TclException(interp,
131: "extra switch pattern with no body");
132: }
133:
134: // Find pattern that matches string, return offset from first pattern
135:
136: pbOffset = SwitchCmd.getBodyOffset(interp, switchObjv, pbStart,
137: string, mode);
138:
139: if (pbOffset != -1) {
140: try {
141: interp.eval(switchObjv[pbStart + pbOffset], 0);
142: return;
143: } catch (TclException e) {
144: // Figure out which pattern matched the body
145: int pIndex;
146: for (pIndex = pbStart + pbOffset - 1; pIndex >= pbStart; pIndex -= 2) {
147: if (!switchObjv[pIndex].toString().equals("-")) {
148: break;
149: }
150: }
151:
152: if (e.getCompletionCode() == TCL.ERROR) {
153: interp.addErrorInfo("\n (\""
154: + switchObjv[pIndex] + "\" arm line "
155: + interp.errorLine + ")");
156: }
157: throw e;
158: }
159: }
160:
161: // Nothing matched: return nothing.
162: }
163:
164: // Util method that accepts switch command arguments and
165: // returns an index indicating an offset from the first
166: // pattern element for the script that should be executed.
167: // This method assumes that a null body element is valid
168: // and returns the index.
169:
170: static int getBodyOffset(Interp interp, TclObject[] switchObjv, // array that contains pattern/body strings
171: int pbStart, // index of first pattern in switchObjv
172: String string, // String argument
173: int mode) // Match mode
174: throws TclException {
175: int body;
176: boolean matched;
177: final int slen = switchObjv.length;
178:
179: // Complain if the last body is a continuation. Note that this
180: // check assumes that the list is non-empty!
181:
182: if (switchObjv[slen - 1] != null
183: && switchObjv[slen - 1].toString().equals("-")) {
184: interp.resetResult();
185: throw new TclException(interp,
186: "no body specified for pattern \""
187: + switchObjv[slen - 2].toString() + "\"");
188: }
189:
190: for (int i = pbStart; i < slen; i += 2) {
191: // See if the pattern matches the string.
192:
193: matched = false;
194: String pattern = switchObjv[i].toString();
195:
196: if ((i == slen - 2) && pattern.equals("default")) {
197: matched = true;
198: } else {
199: switch (mode) {
200: case EXACT:
201: matched = string.equals(pattern);
202: break;
203: case GLOB:
204: matched = Util.stringMatch(string, pattern);
205: break;
206: case REGEXP:
207: matched = Util.regExpMatch(interp, string,
208: switchObjv[i]);
209: break;
210: }
211: }
212: if (!matched) {
213: continue;
214: }
215:
216: // We've got a match. Find a body to execute, skipping bodies
217: // that are "-".
218:
219: for (body = i + 1;; body += 2) {
220: if (body >= slen) {
221: // This shouldn't happen since we've checked that the
222: // last body is not a continuation...
223: throw new TclRuntimeError(
224: "fall-out when searching for body to match pattern");
225: }
226:
227: if (switchObjv[body] == null
228: || !switchObjv[body].toString().equals("-")) {
229: break;
230: }
231: }
232:
233: // Return offset of match (from first pattern)
234: return body - pbStart;
235: }
236:
237: // Nothing matched: return nothing.
238: return -1;
239: }
240:
241: } // end SwitchCmd
|