001: /*
002: * ArrayObject.java --
003: *
004: * This class implements the Array Object Command, which is a special case
005: * of the Object Command.
006: *
007: * See the file "license.terms" for information on usage and
008: * redistribution of this file, and for a DISCLAIMER OF ALL
009: * WARRANTIES.
010: *
011: * RCS: @(#) $Id: ArrayObject.java,v 1.4 2002/12/30 02:30:54 mdejong Exp $
012: *
013: */
014:
015: package tcl.lang;
016:
017: import java.lang.reflect.*;
018: import java.util.*;
019: import java.beans.*;
020:
021: /*
022: * An ArrayObject is used to create and access Java Array objects
023: * using the Java Reflection API. It wraps around a Java Array object (i.e.,
024: * an instance of any Java Array class) and expose it to Tcl scripts.
025: */
026:
027: class ArrayObject extends ReflectObject {
028:
029: static final private String validCmds[] = { "length", "get",
030: "getrange", "set", "setrange" };
031: static final private int OPT_LENGTH = 0;
032: static final private int OPT_GET = 1;
033: static final private int OPT_GETRANGE = 2;
034: static final private int OPT_SET = 3;
035: static final private int OPT_SETRANGE = 4;
036:
037: /*
038: *-----------------------------------------------------------------------------
039: *
040: * cmdProc --
041: *
042: * This cmdProc implements the Tcl command used to invoke methods
043: * of the java.lang.Object stored in this ArrayObject internal
044: * rep. For example, this method is called to process the "$v"
045: * command at the second line of this script:
046: *
047: * set v [java::new java.util.Vector]
048: * $v addElement "foo"
049: *
050: * Results:
051: * None.
052: *
053: * Side effects:
054: * May set the value of array elements or default to the super class
055: * implementation of cmdProc.
056: *
057: *-----------------------------------------------------------------------------
058: */
059:
060: public void cmdProc(Interp interp, // Current interpreter.
061: TclObject argv[]) // Argument list.
062: throws TclException // If wrong number of args, a
063: // standard Tcl exception;
064: // If an exception is encountered
065: // during the invokation of the method,
066: // the Exception object will be stored
067: // in the errorCode of the interp.
068: {
069: boolean convert;
070: int optionIdx, numArgs;
071: Object subArrayObj;
072: Class subArrayClass;
073: int option, index, numDims, count;
074: TclObject indexListObj;
075:
076: if (argv.length < 2) {
077: throw new TclNumArgsException(interp, 1, argv,
078: "?-noconvert? option ?arg arg ...?");
079: }
080:
081: String arg1 = argv[1].toString();
082: if ((arg1.length() >= 2) && (NOCONVERT.startsWith(arg1))) {
083: // numArgs is the number of optional arguments after the sub-command.
084:
085: convert = false;
086: optionIdx = 2;
087: numArgs = argv.length - 3;
088: } else {
089: convert = true;
090: optionIdx = 1;
091: numArgs = argv.length - 2;
092: }
093:
094: if (numArgs < 0) {
095: throw new TclNumArgsException(interp, 1, argv,
096: "?-noconvert? option ?arg arg ...?");
097: }
098:
099: // If the <option> argument to the array object command is one of the
100: // array sub-commands, then proceed to the switch statement below.
101: // Otherwise, the array object behaves as a reflect object by calling
102: // ReflectObject.cmdProc, and <option> will be treated as a method
103: // of the array object.
104: //
105: // We can be sure that there is no conflect between the array
106: // sub-commands and the method of the array object. This is
107: // because the array object is an instance of java.lang.Object,
108: // which has only the following methods (as of JDK 1.1): getClass,
109: // hashCode, equals, wait, toString, notify, notifyAll
110:
111: try {
112: option = TclIndex.get(interp, argv[optionIdx], validCmds,
113: "option", TCL.EXACT);
114: } catch (TclException e) {
115: try {
116: int startIdx = optionIdx + 1;
117:
118: FuncSig.get(interp, javaClass, argv[optionIdx], argv,
119: startIdx, argv.length - startIdx, false);
120: } catch (TclException e1) {
121: throw new TclException(interp, "bad option \""
122: + argv[optionIdx]
123: + "\": must be length, get, getrange, "
124: + "set, setrange, or a valid method signature");
125: }
126:
127: super .cmdProc(interp, argv);
128: return;
129: }
130:
131: switch (option) {
132: case OPT_LENGTH:
133: if (numArgs != 0) {
134: throw new TclNumArgsException(interp, optionIdx + 1,
135: argv, "");
136: }
137: if (convert == false) {
138: throw new TclException(interp,
139: "-noconvert flag not allowed for the \"length\" sub-command");
140: }
141: interp.setResult(Array.getLength(javaObj));
142: return;
143:
144: case OPT_GET:
145: if (numArgs != 1) {
146: throw new TclNumArgsException(interp, optionIdx + 1,
147: argv, "indexList");
148: }
149: indexListObj = argv[optionIdx + 1];
150: numDims = TclList.getLength(interp, indexListObj);
151: if (numDims == 0) {
152: subArrayObj = javaObj;
153: subArrayClass = dereferenceClassDims(interp, javaClass,
154: 1);
155: index = 0;
156: } else {
157: // Dereference all but the last dimension specified. Set
158: // the interpreter result to the index'th element of the
159: // "subArrayObj".
160:
161: subArrayObj = dereferenceArrayDims(interp, javaObj,
162: numDims, indexListObj);
163: subArrayClass = dereferenceClassDims(interp, javaClass,
164: numDims);
165: index = TclInteger.get(interp, TclList.index(interp,
166: indexListObj, numDims - 1));
167: }
168:
169: // Set the interpreter result to a TclObject containing the index'th
170: // element of "subArrayObj".
171:
172: interp.setResult(getArrayElt(interp, subArrayObj,
173: subArrayClass, index, convert));
174: return;
175:
176: case OPT_SET:
177: if (numArgs != 2) {
178: throw new TclNumArgsException(interp, optionIdx + 1,
179: argv, "indexList value");
180: }
181: if (convert == false) {
182: throw new TclException(interp,
183: "-noconvert flag not allowed for the \"set\" sub-command");
184: }
185:
186: indexListObj = argv[optionIdx + 1];
187: numDims = TclList.getLength(interp, indexListObj);
188: if (numDims == 0) {
189: subArrayObj = javaObj;
190: subArrayClass = dereferenceClassDims(interp, javaClass,
191: 1);
192: index = 0;
193: } else {
194: // Dereference all but the last dimension specified. Set
195: // the value of index'th element of the "subArrayObj" to
196: // the value in argv[optionIdx + 2].
197:
198: subArrayObj = dereferenceArrayDims(interp, javaObj,
199: numDims, indexListObj);
200: subArrayClass = dereferenceClassDims(interp, javaClass,
201: numDims);
202: index = TclInteger.get(interp, TclList.index(interp,
203: indexListObj, numDims - 1));
204: }
205:
206: // Set the value of the index'th element of "subArrayObj" to the value
207: // in the TclObject argv[optionIdx + 2].
208:
209: setArrayElt(interp, subArrayObj, subArrayClass, index,
210: argv[optionIdx + 2]);
211: interp.resetResult();
212: return;
213:
214: case OPT_GETRANGE:
215: if (numArgs > 2) {
216: throw new TclNumArgsException(interp, optionIdx + 1,
217: argv, "?indexList ?count??");
218: }
219:
220: // If an index list is specified, dereference all but the last
221: // dimension specified. If the index list is empty, getrange
222: // behaves as an identity function and returns argv[0].
223:
224: subArrayObj = javaObj;
225: subArrayClass = dereferenceClassDims(interp, javaClass, 1);
226: index = 0;
227:
228: if (numArgs > 0) {
229: indexListObj = argv[optionIdx + 1];
230: numDims = TclList.getLength(interp, indexListObj);
231: if (numDims > 0) {
232: subArrayObj = dereferenceArrayDims(interp, javaObj,
233: numDims, indexListObj);
234: subArrayClass = dereferenceClassDims(interp,
235: javaClass, numDims);
236: index = TclInteger.get(interp, TclList.index(
237: interp, indexListObj, numDims - 1));
238: }
239: }
240:
241: // The variable "count" represents the number of elements to
242: // return. The default is the array size less the first index
243: // (the remaining elements of the array). If a count is
244: // specified and is smaller than the default, the default is
245: // overridden.
246:
247: count = Array.getLength(subArrayObj) - index;
248: if (numArgs > 1) {
249: count = Math.min(count, TclInteger.get(interp,
250: argv[optionIdx + 2]));
251: }
252:
253: // Set the interpreter result to a TclList containing "count" elements
254: // of the "subArrayObj", starting with the index'th element.
255:
256: interp.setResult(getArrayElts(interp, subArrayObj,
257: subArrayClass, index, count, convert));
258: return;
259:
260: case OPT_SETRANGE:
261: if ((numArgs < 1) || (numArgs > 3)) {
262: throw new TclNumArgsException(interp, optionIdx + 1,
263: argv, "?indexList ?count?? valueList");
264: }
265: if (convert == false) {
266: throw new TclException(interp,
267: "-noconvert flag not allowed for the \"setrange\" sub-command");
268: }
269:
270: TclObject tclValueListObj = argv[argv.length - 1];
271:
272: // If an index list is specified, dereference all but the last
273: // dimension specified. If the index list is empty, setrange
274: // initialized the array object as it would in the java::new command.
275:
276: subArrayObj = javaObj;
277: subArrayClass = dereferenceClassDims(interp, javaClass, 1);
278: index = 0;
279:
280: if (numArgs > 1) {
281: indexListObj = argv[optionIdx + 1];
282: numDims = TclList.getLength(interp, indexListObj);
283: if (numDims > 0) {
284: subArrayObj = dereferenceArrayDims(interp, javaObj,
285: numDims, indexListObj);
286: subArrayClass = dereferenceClassDims(interp,
287: javaClass, numDims);
288: index = TclInteger.get(interp, TclList.index(
289: interp, indexListObj, numDims - 1));
290: }
291: }
292:
293: // "count" represents the number of elements to set. The
294: // default is the minimum of the valueList size and array size
295: // less the first index (the remaining elements of the array).
296: // If a count is specified and is smaller than the default,
297: // the default is overridden.
298:
299: count = Math.min(
300: TclList.getLength(interp, tclValueListObj), Array
301: .getLength(subArrayObj)
302: - index);
303:
304: if (numArgs > 2) {
305: count = Math.min(count, TclInteger.get(interp,
306: argv[optionIdx + 2]));
307: }
308:
309: // Set the value of "count" elements of the "subArrayObj", starting
310: // with the index'th element, to the values in tclValueListObj.
311:
312: setArrayElts(interp, subArrayObj, subArrayClass, index,
313: count, tclValueListObj);
314: interp.resetResult();
315: return;
316: }
317: }
318:
319: /*
320: *-----------------------------------------------------------------------------
321: *
322: * initArray --
323: *
324: * Call Array.newInstance and populate the array.
325: *
326: * Results:
327: * When successful, the object created by the constructor.
328: *
329: * Side effects:
330: * The constructor can cause arbitrary side effects.
331: *
332: *-----------------------------------------------------------------------------
333: */
334:
335: static Object initArray(Interp interp, // Current interpreter.
336: TclObject sizeListObj, // List of dimension sizes.
337: int sizeListLen, // Size of sizeListObj.
338: int dim, // Current dimension.
339: int numDims, // Number of dimensions to allocate.
340: Class cls, // Type of Array.
341: TclObject valueListObj) // List to populate the Array.
342: throws TclException // May encounter TclList elt of wrong
343: // type in sizeListObj or valueListObj.
344: {
345: Class compCls = cls.getComponentType();
346: int valueListLen = 0;
347: if (valueListObj != null) {
348: valueListLen = TclList.getLength(interp, valueListObj);
349: }
350:
351: // Set arrayLength to be the dim'th dimension size in sizeListObj. If
352: // sizeListObj doesn't contain dim elts, the array length is the length
353: // of valueListObj.
354: //
355: // Initialize the "arrayObj" to size "arrayLength".
356:
357: int arrayLength;
358: if (dim < sizeListLen) {
359: arrayLength = TclInteger.get(interp, TclList.index(interp,
360: sizeListObj, dim));
361: } else {
362: arrayLength = valueListLen;
363: }
364: if ((arrayLength == 0) && (dim < (numDims - 1))) {
365: throw new TclException(interp, "cannot initialize a "
366: + numDims
367: + " dimensional array with zero size in dimension "
368: + dim);
369: }
370:
371: Object arrayObj;
372: try {
373: arrayObj = Array.newInstance(compCls, arrayLength);
374: } catch (NegativeArraySizeException ex) {
375: throw new TclException(interp, "negative array size "
376: + arrayLength);
377: }
378:
379: if (compCls.isArray()) {
380: // Initialize each subArray "i" according to the dim+1'st elt in
381: // sizeListObj and i'th elt in valueListObj.
382:
383: int nextDim = dim + 1;
384: for (int i = 0; i < arrayLength; i++) {
385:
386: TclObject subValueListObj = null;
387: if (i < valueListLen) {
388: subValueListObj = TclList.index(interp,
389: valueListObj, i);
390: }
391:
392: Object subArrayObj = initArray(interp, sizeListObj,
393: sizeListLen, nextDim, numDims, compCls,
394: subValueListObj);
395: Array.set(arrayObj, i, subArrayObj);
396: }
397: } else if (valueListLen > 0) {
398: // Set the value of "count" elements of the "subArrayObj", starting
399: // with the 0'th element, to the values in valueListObj.
400:
401: int count = Math.min(arrayLength, valueListLen);
402: setArrayElts(interp, arrayObj, compCls, 0, count,
403: valueListObj);
404: }
405: return arrayObj;
406: }
407:
408: /*
409: *-----------------------------------------------------------------------------
410: *
411: * dereferenceArrayDims --
412: *
413: * Dereference "numDims - 1" dimensions of the array. Return a non-null
414: * pointer to the remaining array.
415: *
416: * Results:
417: * Returns an array cell object.
418: *
419: * Side effects:
420: * None.
421: *
422: *-----------------------------------------------------------------------------
423: */
424:
425: private static Object dereferenceArrayDims(Interp interp, // Current interpreter.
426: Object arrayObj, // Array to dereference. Must be an array.
427:
428: int numDerefDims, // Number of dimensions to dereference.
429: TclObject indexListObj) // Index to dereference in each dim.
430: throws TclException // May encounter bad array index or
431: // dereference a null array value.
432: {
433: // Before derefencing any dimensions, check that the indexList isn't too
434: // large--we want to return an array.
435:
436: int numDims = JavaInfoCmd.getNumDimsFromClass(arrayObj
437: .getClass());
438: if (numDims < numDerefDims) {
439: throw new TclException(interp, "bad indexList \""
440: + indexListObj.toString() + "\": javaObj only has "
441: + numDims + " dimension(s)");
442: }
443:
444: Object subArrayObj = arrayObj;
445: for (int dim = 0; dim < numDerefDims - 1; dim++) {
446:
447: int index = TclInteger.get(interp, TclList.index(interp,
448: indexListObj, dim));
449: try {
450: subArrayObj = Array.get(subArrayObj, index);
451: } catch (ArrayIndexOutOfBoundsException e) {
452: int max = Array.getLength(subArrayObj) - 1;
453: throw new TclException(interp, "array index \"" + index
454: + "\" is out of bounds: must be between 0 and "
455: + max);
456: }
457: if (subArrayObj == null) {
458: throw new TclException(interp,
459: "null value in dimension " + dim
460: + ": can't dereference " + numDims
461: + " dimensions");
462: }
463: }
464: return subArrayObj;
465: }
466:
467: /*
468: *-----------------------------------------------------------------------------
469: *
470: * dereferenceClassDims --
471: *
472: * Dereference class numDims dimensions of the array class type
473: *
474: * Results:
475: * Returns an sub array. ex: class Object[][] -> Object[] -> Object
476: *
477: * Side effects:
478: * None.
479: *
480: *-----------------------------------------------------------------------------
481: */
482:
483: private static Class dereferenceClassDims(Interp interp, // Current interpreter.
484: Class arrayClass, // Array class type.
485:
486: int numDerefDims) // Number of dimensions to dereference.
487: throws TclException // May encounter bad array index or
488: // dereference a null array value.
489: {
490: // Before derefencing class, check that the numDerefDims isn't too large
491:
492: int numDims = JavaInfoCmd.getNumDimsFromClass(arrayClass);
493: if (numDims < numDerefDims) {
494: throw new TclException(interp,
495: "bad class dereference class only has " + numDims
496: + " dimension(s)");
497: }
498:
499: Class subArrayClass = arrayClass;
500: for (int dim = 0; dim < numDerefDims; dim++) {
501: subArrayClass = subArrayClass.getComponentType();
502: }
503: return subArrayClass;
504: }
505:
506: /*
507: *-----------------------------------------------------------------------------
508: *
509: * getArrayElts --
510: *
511: * Return a TclList containing "count" elements of "arrayObj",
512: * starting with the index'th element.
513: *
514: * Results:
515: * Returns a TclListObject.
516: *
517: * Side effects:
518: * None.
519: *
520: *-----------------------------------------------------------------------------
521: */
522:
523: static TclObject getArrayElts(Interp interp, // Current interpreter.
524: Object arrayObj, // Array to dereference. Must be an array.
525: Class arrayClass, // Class object of array to deref
526: int index, // First elt to dereference.
527: int count, // Number of elts to dereference.
528: boolean convert) // Whether the values should be converted
529: // into Tcl objects of the closest types.
530: throws TclException // May encounter bad index.
531: {
532: TclObject resultListObj = TclList.newInstance();
533: try {
534: for (int i = 0; i < count; i++, index++) {
535: TclList.append(interp, resultListObj, getArrayElt(
536: interp, arrayObj, arrayClass, index, convert));
537: }
538: } catch (TclException e) {
539: resultListObj.release();
540: throw e;
541: }
542: return resultListObj;
543: }
544:
545: /*
546: *-----------------------------------------------------------------------------
547: *
548: * getArrayElt --
549: *
550: * Return a TclObject containing an element elements of "arrayObj",
551: * at the index'th element.
552: *
553: * Results:
554: * Returns a TclObject.
555: *
556: * Side effects:
557: * None.
558: *
559: *-----------------------------------------------------------------------------
560: */
561:
562: static TclObject getArrayElt(Interp interp, // Current interpreter.
563: Object arrayObj, // Array to dereference. Must be an array.
564: Class arrayClass, // Class object of array to deref
565: int index, // First elt to dereference.
566: boolean convert) throws TclException // May encounter bad index.
567: {
568: // Set "obj" to the index'th element of the arrayObj. If Array.get()
569: // fails, reset the interp result to cover error message, and set
570: // "obj" to null. Wrap "obj" in a TclObject and append it to the
571: // result list.
572:
573: Object obj;
574: try {
575: obj = Array.get(arrayObj, index);
576: } catch (ArrayIndexOutOfBoundsException e) {
577: int max = Array.getLength(arrayObj) - 1;
578: throw new TclException(interp, "array index \"" + index
579: + "\" is out of bounds: must be between 0 and "
580: + max);
581: }
582:
583: /*
584:
585: System.out.println("object class is " + arrayObj.getClass());
586: System.out.println("object component type is " +
587: arrayObj.getClass().getComponentType());
588:
589: System.out.println("array class is " + arrayClass);
590: System.out.println("array component type is " +
591: arrayClass.getComponentType());
592: System.out.println("obj derived type is " + obj.getClass());
593:
594: */
595:
596: if (convert) {
597: return JavaInvoke
598: .convertJavaObject(interp, arrayClass, obj);
599: } else {
600: return ReflectObject.newInstance(interp, arrayClass, obj);
601: }
602:
603: }
604:
605: /*
606: *-----------------------------------------------------------------------------
607: *
608: * setArrayElts --
609: *
610: * Set the value of "count" elements of the "arrayObj", starting with
611: * the index'th element, to the first "count" elements of "valueList".
612: * Throw a TclException if arrayObj has fewer than "index" elements.
613: * Throw a TclException if an element of "valueList" cannot be converted
614: * to the component type of "arrayObj".
615: *
616: * Results:
617: * None.
618: *
619: * Side effects:
620: * Sets "count" elements of "arrayObj".
621: *
622: *-----------------------------------------------------------------------------
623: */
624:
625: static void setArrayElts(Interp interp, // Current interpreter.
626: Object arrayObj, // Array whose elts to set. Must be an array.
627: Class arrayClass, // Class object of array to deref
628: int index, // First elt to set.
629: int count, // Number of elts to set.
630: TclObject tclValueListObj) // List of values to assign.
631: throws TclException // May encounter bad index,
632: // or wrong type in tclValueListObj.
633: {
634: for (int i = 0; i < count; i++, index++) {
635: setArrayElt(interp, arrayObj, arrayClass, index, TclList
636: .index(interp, tclValueListObj, i));
637: }
638: }
639:
640: /*
641: *-----------------------------------------------------------------------------
642: *
643: * setArrayElt --
644: *
645: * Set the value of "count" elements of the "arrayObj", starting with
646: * the index'th element, to the first "count" elements of "valueList".
647: * Throw a TclException if arrayObj has fewer than "index" elements.
648: * Throw a TclException if an element of "valueList" cannot be converted
649: * to the component type of "arrayObj".
650: *
651: * Results:
652: * None.
653: *
654: * Side effects:
655: * Sets "count" elements of "arrayObj".
656: *
657: *-----------------------------------------------------------------------------
658: */
659:
660: static void setArrayElt(Interp interp, // Current interpreter.
661: Object arrayObj, // Array whose elts to set. Must be an array.
662: Class arrayClass, // Class object of array to deref
663: int index, // First elt to set.
664: TclObject value) // Value to assign.
665: throws TclException // May encounter bad index,
666: // or wrong type in tclValueListObj.
667: {
668: //Class componentType = arrayClass.getComponentType();
669: Class componentType = arrayClass;
670:
671: /*
672:
673: System.out.println("object class is " + arrayObj.getClass());
674: System.out.println("object component type is " +
675: arrayObj.getClass().getComponentType());
676:
677:
678: System.out.println("array class is " + arrayClass);
679: System.out.println("array component type is " +
680: arrayClass.getComponentType());
681:
682: */
683:
684: Object javaValue = JavaInvoke.convertTclObject(interp,
685: componentType, value);
686:
687: // Set the arrayObj[index] to valueObj. If the array has a primitive
688: // component type, the new value is automatically unwrapped by
689: // Array.set().
690:
691: try {
692: Array.set(arrayObj, index, javaValue);
693: } catch (ArrayIndexOutOfBoundsException e) {
694: int max = Array.getLength(arrayObj) - 1;
695: throw new TclException(interp, "array index \"" + index
696: + "\" is out of bounds: must be between 0 and "
697: + max);
698: }
699: }
700:
701: /*
702: *-----------------------------------------------------------------------------
703: *
704: * getBaseName --
705: *
706: * Return the name of the base class of the class with internal-rep
707: * "clsName".
708: *
709: * Results:
710: * Returns a base class name.
711: *
712: * Side effects:
713: * None.
714: *
715: *-----------------------------------------------------------------------------
716: */
717:
718: static String getBaseName(String clsName) // String name of the class.
719: {
720: // If the string is of the form className[][]..., strip out the trailing
721: // []s.
722:
723: if (clsName.endsWith("[]")) {
724: int end = clsName.indexOf('[');
725: return clsName.substring(0, end);
726: }
727:
728: // If the string begins with '[', strip off the leading '['s and convert of
729: // base code to the string it represents.
730:
731: if (clsName.charAt(0) == '[') {
732: if (clsName.endsWith("[")) {
733: return clsName;
734: }
735: String baseName = clsName.substring(1);
736: while (baseName.charAt(0) == '[') {
737: baseName = baseName.substring(1);
738: }
739: if ((baseName.charAt(0) == 'L') && (baseName.endsWith(";"))) {
740: return baseName.substring(1, baseName.length() - 1);
741: } else if (baseName.charAt(0) == 'I') {
742: return "int";
743: } else if (baseName.charAt(0) == 'Z') {
744: return "boolean";
745: } else if (baseName.charAt(0) == 'J') {
746: return "long";
747: } else if (baseName.charAt(0) == 'F') {
748: return "float";
749: } else if (baseName.charAt(0) == 'D') {
750: return "double";
751: } else if (baseName.charAt(0) == 'B') {
752: return "byte";
753: } else if (baseName.charAt(0) == 'S') {
754: return "short";
755: } else if (baseName.charAt(0) == 'C') {
756: return "char";
757: }
758: }
759:
760: // "clsName" is not array class name, so it must be a base class.
761:
762: return clsName;
763: }
764:
765: } // end ArrayObject
|