001: /*
002: * ArrayCmd.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: ArrayCmd.java,v 1.7 2006/05/23 05:34:33 mdejong Exp $
011: *
012: */
013:
014: package tcl.lang;
015:
016: import java.util.*;
017:
018: /**
019: * This class implements the built-in "array" command in Tcl.
020: */
021:
022: class ArrayCmd implements Command {
023: static Class procClass = null;
024:
025: static final private String validCmds[] = { "anymore",
026: "donesearch", "exists", "get", "names", "nextelement",
027: "set", "size", "startsearch", "unset" };
028:
029: static final int OPT_ANYMORE = 0;
030: static final int OPT_DONESEARCH = 1;
031: static final int OPT_EXISTS = 2;
032: static final int OPT_GET = 3;
033: static final int OPT_NAMES = 4;
034: static final int OPT_NEXTELEMENT = 5;
035: static final int OPT_SET = 6;
036: static final int OPT_SIZE = 7;
037: static final int OPT_STARTSEARCH = 8;
038: static final int OPT_UNSET = 9;
039:
040: /**
041: * This procedure is invoked to process the "array" Tcl command.
042: * See the user documentation for details on what it does.
043: */
044:
045: public void cmdProc(Interp interp, TclObject[] objv)
046: throws TclException {
047: Var var = null, array = null;
048: boolean notArray = false;
049: String varName, msg;
050: int index, result;
051:
052: if (objv.length < 3) {
053: throw new TclNumArgsException(interp, 1, objv,
054: "option arrayName ?arg ...?");
055: }
056:
057: index = TclIndex.get(interp, objv[1], validCmds, "option", 0);
058:
059: // Locate the array variable (and it better be an array).
060:
061: varName = objv[2].toString();
062: Var[] retArray = Var.lookupVar(interp, varName, null, 0, null,
063: false, false);
064:
065: // Assign the values returned in the array
066: if (retArray != null) {
067: var = retArray[0];
068: array = retArray[1];
069: }
070:
071: if ((var == null) || !var.isVarArray() || var.isVarUndefined()) {
072: notArray = true;
073: }
074:
075: // Special array trace used to keep the env array in sync for
076: // array names, array get, etc.
077:
078: if (var != null && var.traces != null) {
079: msg = Var.callTraces(interp, array, var, varName, null,
080: (TCL.LEAVE_ERR_MSG | TCL.NAMESPACE_ONLY
081: | TCL.GLOBAL_ONLY | TCL.TRACE_ARRAY));
082: if (msg != null) {
083: throw new TclVarException(interp, varName, null,
084: "trace array", msg);
085: }
086: }
087:
088: switch (index) {
089: case OPT_ANYMORE: {
090: if (objv.length != 4) {
091: throw new TclNumArgsException(interp, 2, objv,
092: "arrayName searchId");
093: }
094: if (notArray) {
095: errorNotArray(interp, objv[2].toString());
096: }
097:
098: if (var.sidVec == null) {
099: errorIllegalSearchId(interp, objv[2].toString(),
100: objv[3].toString());
101: }
102:
103: Iterator iter = var.getSearch(objv[3].toString());
104: if (iter == null) {
105: errorIllegalSearchId(interp, objv[2].toString(),
106: objv[3].toString());
107: }
108:
109: if (iter.hasNext()) {
110: interp.setResult(true);
111: } else {
112: interp.setResult(false);
113: }
114: break;
115: }
116: case OPT_DONESEARCH: {
117:
118: if (objv.length != 4) {
119: throw new TclNumArgsException(interp, 2, objv,
120: "arrayName searchId");
121: }
122: if (notArray) {
123: errorNotArray(interp, objv[2].toString());
124: }
125:
126: boolean rmOK = true;
127: if (var.sidVec != null) {
128: rmOK = (var.removeSearch(objv[3].toString()));
129: }
130: if ((var.sidVec == null) || !rmOK) {
131: errorIllegalSearchId(interp, objv[2].toString(),
132: objv[3].toString());
133: }
134: break;
135: }
136: case OPT_EXISTS: {
137:
138: if (objv.length != 3) {
139: throw new TclNumArgsException(interp, 2, objv,
140: "arrayName");
141: }
142: interp.setResult(!notArray);
143: break;
144: }
145: case OPT_GET: {
146: // Due to the differences in the hashtable implementation
147: // from the Tcl core and Java, the output will be rearranged.
148: // This is not a negative side effect, however, test results
149: // will differ.
150:
151: if ((objv.length != 3) && (objv.length != 4)) {
152: throw new TclNumArgsException(interp, 2, objv,
153: "arrayName ?pattern?");
154: }
155: if (notArray) {
156: return;
157: }
158:
159: String pattern = null;
160: if (objv.length == 4) {
161: pattern = objv[3].toString();
162: }
163:
164: HashMap table = var.arraymap;
165: TclObject tobj = TclList.newInstance();
166: String arrayName = objv[2].toString();
167: String key, strValue;
168: Var var2;
169:
170: // Go through each key in the hash table. If there is a
171: // pattern, test for a match. Each valid key and its value
172: // is written into sbuf, which is returned.
173:
174: // FIXME : do we need to port over the 8.1 code for this loop?
175:
176: for (Iterator iter = table.entrySet().iterator(); iter
177: .hasNext();) {
178: Map.Entry entry = (Map.Entry) iter.next();
179: key = (String) entry.getKey();
180: var2 = (Var) entry.getValue();
181: if (var2.isVarUndefined()) {
182: continue;
183: }
184:
185: if (pattern != null && !Util.stringMatch(key, pattern)) {
186: continue;
187: }
188:
189: strValue = interp.getVar(arrayName, key, 0).toString();
190:
191: TclList
192: .append(interp, tobj, TclString
193: .newInstance(key));
194: TclList.append(interp, tobj, TclString
195: .newInstance(strValue));
196: }
197: interp.setResult(tobj);
198: break;
199: }
200: case OPT_NAMES: {
201:
202: if ((objv.length != 3) && (objv.length != 4)) {
203: throw new TclNumArgsException(interp, 2, objv,
204: "arrayName ?pattern?");
205: }
206: if (notArray) {
207: return;
208: }
209:
210: String pattern = null;
211: if (objv.length == 4) {
212: pattern = objv[3].toString();
213: }
214:
215: HashMap table = var.arraymap;
216: TclObject tobj = TclList.newInstance();
217: String key;
218:
219: // Go through each key in the hash table. If there is a
220: // pattern, test for a match. Each valid key and its value
221: // is written into sbuf, which is returned.
222:
223: for (Iterator iter = table.entrySet().iterator(); iter
224: .hasNext();) {
225: Map.Entry entry = (Map.Entry) iter.next();
226: key = (String) entry.getKey();
227: Var elem = (Var) entry.getValue();
228: if (!elem.isVarUndefined()) {
229: if (pattern != null) {
230: if (!Util.stringMatch(key, pattern)) {
231: continue;
232: }
233: }
234: TclList.append(interp, tobj, TclString
235: .newInstance(key));
236: }
237: }
238: interp.setResult(tobj);
239: break;
240: }
241: case OPT_NEXTELEMENT: {
242:
243: if (objv.length != 4) {
244: throw new TclNumArgsException(interp, 2, objv,
245: "arrayName searchId");
246: }
247: if (notArray) {
248: errorNotArray(interp, objv[2].toString());
249: }
250:
251: if (var.sidVec == null) {
252: errorIllegalSearchId(interp, objv[2].toString(),
253: objv[3].toString());
254: }
255:
256: Iterator iter = var.getSearch(objv[3].toString());
257: if (iter == null) {
258: errorIllegalSearchId(interp, objv[2].toString(),
259: objv[3].toString());
260: }
261: if (iter.hasNext()) {
262: Map.Entry entry = (Map.Entry) iter.next();
263: String key = (String) entry.getKey();
264: Var elem = (Var) entry.getValue();
265:
266: if (!elem.isVarUndefined()) {
267: interp.setResult(key);
268: } else {
269: interp.setResult("");
270: }
271: }
272: break;
273: }
274: case OPT_SET: {
275:
276: if (objv.length != 4) {
277: throw new TclNumArgsException(interp, 2, objv,
278: "arrayName list");
279: }
280: int size = TclList.getLength(interp, objv[3]);
281: if (size % 2 != 0) {
282: throw new TclException(interp,
283: "list must have an even number of elements");
284: }
285:
286: int i;
287: String name1 = objv[2].toString();
288: String name2, strValue;
289:
290: // Set each of the array variable names in the interp
291:
292: for (i = 0; i < size; i++) {
293: name2 = TclList.index(interp, objv[3], i++).toString();
294: strValue = TclList.index(interp, objv[3], i).toString();
295: interp.setVar(name1, name2, TclString
296: .newInstance(strValue), 0);
297: }
298: break;
299: }
300: case OPT_SIZE: {
301:
302: if (objv.length != 3) {
303: throw new TclNumArgsException(interp, 2, objv,
304: "arrayName");
305: }
306: if (notArray) {
307: interp.setResult(0);
308: } else {
309: HashMap table = var.arraymap;
310: int size = 0;
311:
312: for (Iterator iter = table.entrySet().iterator(); iter
313: .hasNext();) {
314: Map.Entry entry = (Map.Entry) iter.next();
315: String key = (String) entry.getKey();
316: Var elem = (Var) entry.getValue();
317: if (!elem.isVarUndefined()) {
318: size++;
319: }
320: }
321: interp.setResult(size);
322: }
323: break;
324: }
325: case OPT_STARTSEARCH: {
326:
327: if (objv.length != 3) {
328: throw new TclNumArgsException(interp, 2, objv,
329: "arrayName");
330: }
331: if (notArray) {
332: errorNotArray(interp, objv[2].toString());
333: }
334:
335: if (var.sidVec == null) {
336: var.sidVec = new ArrayList();
337: }
338:
339: // Create a SearchId Object:
340: // To create a new SearchId object, a unique string
341: // identifier needs to be composed and we need to
342: // create an Iterator of the array keys. The
343: // unique string identifier is created from three
344: // strings:
345: //
346: // "s-" is the default prefix
347: // "i" is a unique number that is 1+ the greatest
348: // SearchId index currently on the ArrayVar.
349: // "name" is the name of the array
350: //
351: // Once the SearchId string is created we construct a
352: // new SearchId object using the string and the
353: // Iterator. From now on the string is used to
354: // uniquely identify the SearchId object.
355:
356: int i = var.getNextIndex();
357: String s = "s-" + i + "-" + objv[2].toString();
358: HashMap table = var.arraymap;
359: Iterator iter = table.entrySet().iterator();
360: var.sidVec.add(new SearchId(iter, s, i));
361: interp.setResult(s);
362: break;
363: }
364: case OPT_UNSET: {
365: String pattern;
366: String name;
367:
368: if ((objv.length != 3) && (objv.length != 4)) {
369: throw new TclNumArgsException(interp, 2, objv,
370: "arrayName ?pattern?");
371: }
372: if (notArray) {
373: errorNotArray(interp, objv[2].toString());
374: }
375: if (objv.length == 3) {
376: // When no pattern is given, just unset the whole array
377:
378: interp.unsetVar(objv[2], 0);
379: } else {
380: pattern = objv[3].toString();
381: HashMap table = var.arraymap;
382:
383: for (Iterator iter = table.entrySet().iterator(); iter
384: .hasNext();) {
385: Map.Entry entry = (Map.Entry) iter.next();
386: name = (String) entry.getKey();
387: Var elem = (Var) entry.getValue();
388: if (elem.isVarUndefined()) {
389: continue;
390: }
391: if (Util.stringMatch(name, pattern)) {
392: interp.unsetVar(varName, name, 0);
393: // Reset iterator in case unset
394: // modified the table.
395: iter = table.entrySet().iterator();
396: }
397: }
398: }
399: break;
400: }
401: }
402: }
403:
404: /**
405: * Error meassage thrown when an invalid identifier is used
406: * to access an array.
407: *
408: * @param interp currrent interpreter.
409: * @param String var is the string representation of the
410: * variable that was passed in.
411: */
412:
413: private static void errorNotArray(Interp interp, String var)
414: throws TclException {
415: throw new TclException(interp, "\"" + var + "\" isn't an array");
416: }
417:
418: /**
419: * Error message thrown when an invalid SearchId is used. The
420: * string used to reference the SearchId is parced to determine
421: * the reason for the failure.
422: *
423: * @param interp currrent interpreter.
424: * @param String sid is the string represenation of the
425: * SearchId that was passed in.
426: */
427:
428: static void errorIllegalSearchId(Interp interp, String varName,
429: String sid) throws TclException {
430:
431: int val = validSearchId(sid.toCharArray(), varName);
432:
433: if (val == 1) {
434: throw new TclException(interp, "couldn't find search \""
435: + sid + "\"");
436: } else if (val == 0) {
437: throw new TclException(interp,
438: "illegal search identifier \"" + sid + "\"");
439: } else {
440: throw new TclException(interp, "search identifier \"" + sid
441: + "\" isn't for variable \"" + varName + "\"");
442: }
443: }
444:
445: /**
446: * A valid SearchId is represented by the format s-#-arrayName. If
447: * the SearchId string does not match this format than it is illegal,
448: * else we cannot find it. This method is used by the
449: * ErrorIllegalSearchId method to determine the type of error message.
450: *
451: * @param char pattern[] is the string use dto identify the SearchId
452: * @return 1 if its a valid searchID; 0 if it is not a valid searchId,
453: * but it is for the array, -1 if it is not a valid searchId and NOT
454: * for the array.
455: */
456:
457: private static int validSearchId(char pattern[], String varName) {
458: int i;
459:
460: if ((pattern[0] != 's') || (pattern[1] != '-')
461: || (pattern[2] < '0') || (pattern[2] > '9')) {
462: return 0;
463: }
464: for (i = 3; (i < pattern.length && pattern[i] != '-'); i++) {
465: if (pattern[i] < '0' || pattern[i] > '9') {
466: return 0;
467: }
468: }
469: if (++i >= pattern.length) {
470: return 0;
471: }
472: if (varName
473: .equals(new String(pattern, i, (pattern.length - i)))) {
474: return 1;
475: } else {
476: return -1;
477:
478: }
479: }
480: }
|