001: /*
002: * JavaForCmd.java --
003: *
004: * Implements the built-in "java::for" command.
005: *
006: * Copyright (c) 2006 by Moses DeJong
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: JavaForCmd.java,v 1.1 2006/04/13 20:07:04 mdejong Exp $
013: *
014: */
015:
016: package tcl.lang;
017:
018: import java.util.Collection;
019: import java.util.Iterator;
020:
021: /**
022: * This class implements the built-in "java::for" command.
023: * This command is semantically equivalent to the enhanced
024: * Java for statement in JDK 1.5 and newer. It is used
025: * to iterate over Collections and can also be used for arrays.
026: */
027:
028: public class JavaForCmd implements Command {
029: public void cmdProc(Interp interp, TclObject[] objv)
030: throws TclException {
031: if (objv.length != 4) {
032: throw new TclNumArgsException(interp, 1, objv,
033: "type_var collection script");
034: }
035:
036: TclObject type_var = objv[1];
037: TclObject collectionObj = objv[2];
038: TclObject script = objv[3];
039:
040: // Extract {type varname} for iteration var
041:
042: Class type = null;
043: String typename;
044: String varname;
045:
046: if (TclList.getLength(interp, type_var) != 2) {
047: throw new TclException(interp, "invalid type_var \""
048: + type_var + "\"");
049: }
050: typename = TclList.index(interp, type_var, 0).toString();
051: varname = TclList.index(interp, type_var, 1).toString();
052: if (typename.length() == 0 || varname.length() == 0) {
053: throw new TclException(interp, "invalid type_var \""
054: + type_var + "\"");
055: }
056:
057: // Make sure Var is a simple scalar name. It can't be
058: // an array name or a scoped variable name.
059:
060: if (isArrayVarname(varname) || varname.indexOf("::") != -1) {
061: throw new TclException(interp, "invalid type_var \""
062: + type_var + "\", varname must be simple scalar");
063: }
064:
065: // Get Class object that corresponds to typename.
066: // The void type is not allowed.
067:
068: if (typename.equals("void") || typename.equals("Void")
069: || typename.equals("java.lang.Void")) {
070: throw new TclException(interp, "void type is invalid");
071: }
072:
073: type = JavaInvoke.getClassByName(interp, typename);
074:
075: // Get the passes in collection object and check
076: // its type against the passed in type.
077:
078: Object collection_obj = ReflectObject
079: .get(interp, collectionObj);
080: Class collection_class = ReflectObject.getClass(interp,
081: collectionObj);
082:
083: // Determine if the type name given is a primitive type
084: // or an object type. A primitive array pass as the collection
085: // object must match the primitive type exactly. A
086: // primitive array does not implement the Collection
087: // interface, but the Java construct supports the usage.
088:
089: final int ARRAY_OBJECT = 1; // Any Object in an array
090: final int ARRAY_STRING = 2;
091: final int ARRAY_INT = 3;
092: final int ARRAY_BOOLEAN = 4;
093: final int ARRAY_LONG = 5;
094: final int ARRAY_FLOAT = 6;
095: final int ARRAY_DOUBLE = 7;
096: final int ARRAY_BYTE = 8;
097: final int ARRAY_SHORT = 9;
098: final int ARRAY_CHAR = 10;
099: final int COLLECTION = 11; // Any Object in a Collection
100:
101: int typeId;
102:
103: int[] intArray = null;
104: int intIndexValue;
105:
106: boolean[] booleanArray = null;
107: boolean booleanIndexValue;
108:
109: long[] longArray = null;
110: long longIndexValue;
111:
112: float[] floatArray = null;
113: float floatIndexValue;
114:
115: double[] doubleArray = null;
116: double doubleIndexValue;
117:
118: byte[] byteArray = null;
119: byte byteIndexValue;
120:
121: short[] shortArray = null;
122: short shortIndexValue;
123:
124: char[] charArray = null;
125: char charIndexValue;
126:
127: String[] stringArray = null;
128: String stringIndexValue;
129:
130: Object[] objArray = null;
131: //Object objIndexValue;
132:
133: int arrayLength = 0;
134:
135: if (!collection_class.isArray()) {
136: // If argument is not an array, then it has to be a Collection
137: typeId = COLLECTION;
138: } else if (type == Integer.TYPE) {
139: typeId = ARRAY_INT;
140:
141: if (!(collection_obj instanceof int[])) {
142: throw new TclException(interp,
143: "expected collection object to be array of int");
144: } else {
145: intArray = (int[]) collection_obj;
146: arrayLength = intArray.length;
147: }
148: } else if (type == Boolean.TYPE) {
149: typeId = ARRAY_BOOLEAN;
150:
151: if (!(collection_obj instanceof boolean[])) {
152: throw new TclException(interp,
153: "expected collection object to be array of boolean");
154: } else {
155: booleanArray = (boolean[]) collection_obj;
156: arrayLength = booleanArray.length;
157: }
158: } else if (type == Long.TYPE) {
159: typeId = ARRAY_LONG;
160:
161: if (!(collection_obj instanceof long[])) {
162: throw new TclException(interp,
163: "expected collection object to be array of long");
164: } else {
165: longArray = (long[]) collection_obj;
166: arrayLength = longArray.length;
167: }
168: } else if (type == Float.TYPE) {
169: typeId = ARRAY_FLOAT;
170:
171: if (!(collection_obj instanceof float[])) {
172: throw new TclException(interp,
173: "expected collection object to be array of float");
174: } else {
175: floatArray = (float[]) collection_obj;
176: arrayLength = floatArray.length;
177: }
178: } else if (type == Double.TYPE) {
179: typeId = ARRAY_DOUBLE;
180:
181: if (!(collection_obj instanceof double[])) {
182: throw new TclException(interp,
183: "expected collection object to be array of double");
184: } else {
185: doubleArray = (double[]) collection_obj;
186: arrayLength = doubleArray.length;
187: }
188: } else if (type == Byte.TYPE) {
189: typeId = ARRAY_BYTE;
190:
191: if (!(collection_obj instanceof byte[])) {
192: throw new TclException(interp,
193: "expected collection object to be array of byte");
194: } else {
195: byteArray = (byte[]) collection_obj;
196: arrayLength = byteArray.length;
197: }
198: } else if (type == Short.TYPE) {
199: typeId = ARRAY_SHORT;
200:
201: if (!(collection_obj instanceof short[])) {
202: throw new TclException(interp,
203: "expected collection object to be array of short");
204: } else {
205: shortArray = (short[]) collection_obj;
206: arrayLength = shortArray.length;
207: }
208: } else if (type == Character.TYPE) {
209: typeId = ARRAY_CHAR;
210:
211: if (!(collection_obj instanceof char[])) {
212: throw new TclException(interp,
213: "expected collection object to be array of char");
214: } else {
215: charArray = (char[]) collection_obj;
216: arrayLength = charArray.length;
217: }
218: } else if (type == String.class) {
219: typeId = ARRAY_STRING;
220:
221: if (!(collection_obj instanceof String[])) {
222: throw new TclException(interp,
223: "expected collection object to be array of String");
224: } else {
225: stringArray = (String[]) collection_obj;
226: arrayLength = stringArray.length;
227: }
228: } else {
229: typeId = ARRAY_OBJECT;
230:
231: if (!(collection_obj instanceof Object[])) {
232: throw new TclException(interp,
233: "expected collection object to be array of Object");
234: } else {
235: objArray = (Object[]) collection_obj;
236: arrayLength = objArray.length;
237: }
238: }
239:
240: // For a collection object that is not an
241: // array of a primitive type, check that
242: // the object implements the Collection
243: // interface.
244:
245: Collection collection = null;
246: if (typeId == COLLECTION) {
247: if (!(collection_obj instanceof Collection)) {
248: throw new TclException(
249: interp,
250: "passed collection argument "
251: + "of type "
252: + JavaInfoCmd
253: .getNameFromClass(collection_class)
254: + " which does not implement Collection interface");
255: }
256: collection = (Collection) collection_obj;
257: }
258:
259: // Loop over elements in the collection and eval script
260:
261: boolean done;
262: Iterator iterator = null;
263: Object elem;
264: Class elem_class;
265: int i = 0;
266:
267: // loop init
268:
269: switch (typeId) {
270: case COLLECTION: {
271: iterator = collection.iterator();
272: break;
273: }
274: case ARRAY_OBJECT:
275: case ARRAY_STRING:
276: case ARRAY_INT:
277: case ARRAY_BOOLEAN:
278: case ARRAY_LONG:
279: case ARRAY_FLOAT:
280: case ARRAY_DOUBLE:
281: case ARRAY_BYTE:
282: case ARRAY_SHORT:
283: case ARRAY_CHAR: {
284: // No-op
285: break;
286: }
287: default: {
288: throw new TclRuntimeError("unmatched typeId " + typeId);
289: }
290: }
291:
292: while (true) {
293:
294: // loop done condition
295:
296: switch (typeId) {
297: case COLLECTION: {
298: done = !iterator.hasNext();
299: break;
300: }
301: case ARRAY_OBJECT:
302: case ARRAY_STRING:
303: case ARRAY_INT:
304: case ARRAY_BOOLEAN:
305: case ARRAY_LONG:
306: case ARRAY_FLOAT:
307: case ARRAY_DOUBLE:
308: case ARRAY_BYTE:
309: case ARRAY_SHORT:
310: case ARRAY_CHAR: {
311: done = !(i < arrayLength);
312: break;
313: }
314: default: {
315: throw new TclRuntimeError("unmatched typeId " + typeId);
316: }
317: }
318: if (done) {
319: break; // out of while loop
320: }
321:
322: // More elements to process in collection or array,
323: // get the next element, set the interp var, and
324: // evaluate the loop body.
325:
326: TclObject wrapper;
327:
328: switch (typeId) {
329: case COLLECTION:
330: case ARRAY_OBJECT: {
331: // Get Object from array or collection and make sure
332: // it is assignable to type.
333:
334: if (typeId == COLLECTION) {
335: elem = iterator.next();
336: } else {
337: elem = objArray[i];
338: }
339: elem_class = elem.getClass();
340:
341: // Check that the element Object can be cast
342: // up to the type of the iteration var.
343: // In Java code, a failed upcast would
344: // generate a ClassCastException. Here we
345: // check at runtime. This logic also
346: // supports assigning a wrapper type
347: // like Integer to an int iteration var.
348:
349: if (type.isPrimitive()
350: && (((type == Integer.TYPE) && (elem_class == Integer.class))
351: || ((type == Boolean.TYPE) && (elem_class == Boolean.class))
352: || ((type == Long.TYPE) && (elem_class == Long.class))
353: || ((type == Float.TYPE) && (elem_class == Float.class))
354: || ((type == Double.TYPE) && (elem_class == Double.class))
355: || ((type == Byte.TYPE) && (elem_class == Byte.class))
356: || ((type == Short.TYPE) && (elem_class == Short.class)) || ((type == Character.TYPE) && (elem_class == Character.class)))) {
357: // No-op
358: } else if (!JavaInvoke.isAssignable(type, elem_class)) {
359: throw new TclException(
360: interp,
361: ((typeId == COLLECTION) ? "collection"
362: : "array")
363: + " element of type "
364: + JavaInfoCmd
365: .getNameFromClass(elem_class)
366: + " could not be assigned to iteration variable of type "
367: + JavaInfoCmd
368: .getNameFromClass(type));
369: }
370:
371: if (type.isPrimitive() || type == String.class) {
372: wrapper = JavaInvoke.convertJavaObject(interp,
373: type, elem);
374: } else {
375: // Don't convert to Tcl primitive type if the iteration
376: // var is an object type like Integer.
377: wrapper = ReflectObject.newInstance(interp, type,
378: elem);
379: }
380: break;
381: }
382: case ARRAY_STRING: {
383: stringIndexValue = stringArray[i];
384: wrapper = TclString.newInstance(stringIndexValue);
385: break;
386: }
387: case ARRAY_INT: {
388: intIndexValue = intArray[i];
389: wrapper = TclInteger.newInstance(intIndexValue);
390: break;
391: }
392: case ARRAY_BOOLEAN: {
393: booleanIndexValue = booleanArray[i];
394: wrapper = TclInteger.newInstance((booleanIndexValue ? 1
395: : 0));
396: break;
397: }
398: case ARRAY_LONG: {
399: longIndexValue = longArray[i];
400: // Can't represent long as TclInteger
401: wrapper = TclString.newInstance(String
402: .valueOf(longIndexValue));
403: break;
404: }
405: case ARRAY_FLOAT: {
406: floatIndexValue = floatArray[i];
407: wrapper = TclDouble
408: .newInstance((double) floatIndexValue);
409: break;
410: }
411: case ARRAY_DOUBLE: {
412: doubleIndexValue = doubleArray[i];
413: wrapper = TclDouble.newInstance(doubleIndexValue);
414: break;
415: }
416: case ARRAY_BYTE: {
417: byteIndexValue = byteArray[i];
418: wrapper = TclInteger.newInstance((int) byteIndexValue);
419: break;
420: }
421: case ARRAY_SHORT: {
422: shortIndexValue = shortArray[i];
423: wrapper = TclInteger.newInstance((int) shortIndexValue);
424: break;
425: }
426: case ARRAY_CHAR: {
427: charIndexValue = charArray[i];
428: wrapper = TclString.newInstance(charIndexValue);
429: break;
430: }
431: default: {
432: throw new TclRuntimeError("unmatched typeId " + typeId);
433: }
434: }
435:
436: // Update array index
437:
438: i++;
439:
440: // Assign iteration variable
441:
442: interp.setVar(varname, wrapper, 0);
443:
444: // Eval the script argument, handle break and continue
445:
446: try {
447: interp.eval(script, 0);
448: } catch (TclException e) {
449: int ccode = e.getCompletionCode();
450: if (ccode == TCL.BREAK) {
451: break; // Out of while loop
452: } else if (ccode == TCL.CONTINUE) {
453: continue; // To next while loop
454: } else {
455: throw e;
456: }
457: }
458:
459: } // end while (true) loop
460:
461: // Reset interp results after loop, this matches the
462: // implementation of Tcl while and for loop.
463:
464: interp.resetResult();
465: return;
466: }
467:
468: static final boolean isArrayVarname(String varName) {
469: final int lastInd = varName.length() - 1;
470: if (varName.charAt(lastInd) == ')') {
471: if (varName.indexOf('(') != -1) {
472: return true;
473: }
474: }
475: return false;
476: }
477:
478: } // end of JavaForCmd class
|