001: /*
002: * LindexCmd.java - -
003: *
004: * Implements the built-in "lindex" Tcl command.
005: *
006: * Copyright (c) 1997 Cornell University.
007: * Copyright (c) 1997 Sun Microsystems, Inc.
008: *
009: * See the file "license.terms" for information on usage and
010: * redistribution of this file, and for a DISCLAIMER OF ALL
011: * WARRANTIES.
012: *
013: * RCS: @(#) $Id: LindexCmd.java,v 1.5 2006/06/08 07:44:51 mdejong Exp $
014: *
015: */
016:
017: package tcl.lang;
018:
019: /*
020: * This class implements the built-in "lindex" command in Tcl.
021: */
022:
023: class LindexCmd implements Command {
024:
025: /*
026: *----------------------------------------------------------------------
027: *
028: * cmdProc --
029: *
030: * This procedure is invoked as part of the Command interface to
031: * process the "lindex" Tcl command. See the user documentation
032: * for details on what it does.
033: *
034: * Results:
035: * None.
036: *
037: * Side effects:
038: * See the user documentation.
039: *
040: *----------------------------------------------------------------------
041: */
042:
043: public void cmdProc(Interp interp, // Current interpreter.
044: TclObject[] objv) // Argument objects.
045: throws TclException // A standard Tcl exception.
046: {
047: TclObject elem; // The element being extracted
048:
049: if (objv.length < 2) {
050: throw new TclNumArgsException(interp, 1, objv,
051: "list ?index...?");
052: }
053:
054: // If objv.length == 3, then objv[2] may be either a single index or
055: // a list of indices: go to TclLindexList to determine which.
056: // If objv.length >= 4, or objv.length == 2, then objv[2 .. objv.length-2 ] are all
057: // single indices and processed as such in TclLindexFlat.
058:
059: if (objv.length == 3) {
060: elem = TclLindexList(interp, objv[1], objv, 2);
061: } else {
062: elem = TclLindexFlat(interp, objv[1], objv.length - 2,
063: objv, 2);
064: }
065:
066: // Set the interpreter's object result to the last element extracted
067:
068: if (elem == null) {
069: throw new TclRuntimeError("unexpected null result");
070: } else {
071: interp.setResult(elem);
072: elem.release();
073: }
074: }
075:
076: /*
077: *----------------------------------------------------------------------
078: *
079: * TclLindexList --
080: *
081: * This procedure handles the 'lindex' command when objc==3.
082: *
083: * Results:
084: * Returns a pointer to the object extracted, or null if an
085: * error occurred.
086: *
087: * Side effects:
088: * None.
089: *
090: * If objv[1] can be parsed as a list, TclLindexList handles extraction
091: * of the desired element locally. Otherwise, it invokes
092: * TclLindexFlat to treat objv[1] as a scalar.
093: *
094: * The reference count of the returned object includes one reference
095: * corresponding to the pointer returned. Thus, the calling code will
096: * usually do something like:
097: * Tcl_SetObjResult( interp, result );
098: * Tcl_DecrRefCount( result );
099: *
100: *----------------------------------------------------------------------
101: */
102:
103: static TclObject TclLindexList(Interp interp, // Tcl interpreter
104: TclObject list, // List being unpacked
105: TclObject[] objv, // Array containing arg object
106: int argIndex) // Index of arg object in objv
107: throws TclException {
108: int listLen; // Length of the list being manipulated.
109: int index; // Index into the list
110: int i; // Current index number
111: TclObject[] indices; // Array of list indices
112: TclObject oldList; // Temp location to preserve the list
113: // pointer when replacing it with a sublist
114:
115: // Determine whether arg designates a list or a single index.
116: // We have to be careful about the order of the checks to avoid
117: // repeated shimmering; see TIP#22 and TIP#33 for the details.
118:
119: TclObject arg = objv[argIndex];
120: boolean isListType = arg.isListType();
121: boolean isValidIndex = false;
122:
123: if (!isListType) {
124: try {
125: index = Util.getIntForIndex(null, arg, 0);
126: isValidIndex = true;
127: } catch (TclException ex) {
128: }
129: }
130: if (!isListType && isValidIndex) {
131: // arg designates a single index.
132: return TclLindexFlat(interp, list, 1, objv, argIndex);
133: }
134:
135: indices = null;
136: try {
137: indices = TclList.getElements(null, arg);
138: } catch (TclException ex) {
139: }
140:
141: if (indices == null) {
142: // arg designates something that is neither an index nor a
143: // well-formed list. Report the error via TclLindexFlat.
144:
145: return TclLindexFlat(interp, list, 1, objv, argIndex);
146: }
147:
148: // Record the reference to the list that we are maintaining in
149: // the activation record.
150:
151: list.preserve();
152:
153: // arg designates a list, and the code above has parsed it
154: // into indices.
155:
156: for (i = 0; i < indices.length; i++) {
157: // Convert the current list/sublist to a list rep if necessary.
158: try {
159: listLen = TclList.getLength(interp, list);
160: } catch (TclException te) {
161: list.release();
162: throw te;
163: }
164:
165: // Get the index from indices[i]
166:
167: try {
168: index = Util.getIntForIndex(interp, indices[i],
169: listLen - 1);
170: } catch (TclException te) {
171: list.release();
172: throw te;
173: }
174:
175: if (index < 0 || index >= listLen) {
176: // Index is out of range, return empty string result
177: list.release();
178: list = interp.checkCommonString(null);
179: list.preserve();
180: return list;
181: }
182:
183: // Get list element at index.
184:
185: oldList = list;
186: list = TclList.index(interp, oldList, index);
187: list.preserve();
188: oldList.release();
189: }
190:
191: // Return the last object extracted. Its reference count will include
192: // the reference being returned.
193:
194: return list;
195: }
196:
197: /*
198: *----------------------------------------------------------------------
199: *
200: * TclLindexFlat --
201: *
202: * This procedure handles the 'lindex' command, given that the
203: * arguments to the command are known to be a flat list.
204: *
205: * Results:
206: * Returns a standard Tcl result.
207: *
208: * Side effects:
209: * None.
210: *
211: * This procedure is called from either tclExecute.c or
212: * Tcl_LindexObjCmd whenever either is presented with
213: * objc == 2 or objc >= 4. It is also called from TclLindexList
214: * for the objc==3 case once it is determined that objv[2] cannot
215: * be parsed as a list.
216: *
217: *----------------------------------------------------------------------
218: */
219:
220: static TclObject TclLindexFlat(Interp interp, // Tcl interpreter
221: TclObject list, // Tcl object representing the list
222: int indexCount, // Count of indices
223: TclObject[] indexArray,
224: // Array of Tcl objects
225: // representing the indices in the
226: // list
227: int indexArrayOffset) // Offset from start of indexArray
228: throws TclException {
229: int i; // Current list index
230: int listLen; // Length of the current list being
231: // processed
232: int index; // Parsed version of the current element
233: // of indexArray
234: TclObject oldList; // Temporary to hold list so that
235: // its ref count can be decremented.
236:
237: // Record the reference to the 'list' object that we are
238: // maintaining in the activation record.
239:
240: list.preserve();
241:
242: final int endIndex = indexArrayOffset + indexCount;
243: for (i = indexArrayOffset; i < endIndex; i++) {
244: // Convert the current list/sublist to a list rep if necessary.
245: try {
246: listLen = TclList.getLength(interp, list);
247: } catch (TclException te) {
248: list.release();
249: throw te;
250: }
251:
252: // Get the index from objv[i]
253:
254: try {
255: index = Util.getIntForIndex(interp, indexArray[i],
256: listLen - 1);
257: } catch (TclException te) {
258: list.release();
259: throw te;
260: }
261:
262: if (index < 0 || index >= listLen) {
263: // Index is out of range, return empty string result
264: list.release();
265: list = interp.checkCommonString(null);
266: list.preserve();
267: return list;
268: }
269:
270: // Make sure list still refers to a list object.
271: // It might have been converted to something else above
272: // if objv[1] overlaps with one of the other parameters.
273:
274: try {
275: listLen = TclList.getLength(interp, list);
276: } catch (TclException te) {
277: list.release();
278: throw te;
279: }
280:
281: // Get list element at index.
282:
283: oldList = list;
284: list = TclList.index(interp, oldList, index);
285: list.preserve();
286: oldList.release();
287: }
288:
289: return list;
290: }
291:
292: } // end LindexCmd class
|