001: /*
002: * JavaInfoCmd.java
003: *
004: * This file contains the Jacl implementation of the built-in java::info
005: * command.
006: *
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: JavaInfoCmd.java,v 1.7 2006/04/13 07:36:50 mdejong Exp $
014: */
015:
016: package tcl.lang;
017:
018: import tcl.lang.reflect.PkgInvoker;
019: import java.util.*;
020: import java.beans.*;
021: import java.lang.reflect.*;
022:
023: /**
024: * This class implements the built-in "java::info" command in Tcl.
025: */
026:
027: class JavaInfoCmd implements Command {
028:
029: static final private String validCmds[] = { "class", "baseclass",
030: "dimensions", "events", "fields", "methods",
031: "constructors", "properties", "superclass" };
032:
033: static final private int CLASS = 0;
034: static final private int BASECLASS = 1;
035: static final private int DIMENSIONS = 2;
036: static final private int EVENTS = 3;
037: static final private int FIELDS = 4;
038: static final private int METHODS = 5;
039: static final private int CONSTRUCTORS = 6;
040: static final private int PROPERTIES = 7;
041: static final private int SUPERCLASS = 8;
042:
043: static final private String propOpts[] = { "-type" };
044: static final private String methOpts[] = { "-type", "-static" };
045:
046: static final int TYPE_OPT = 0;
047: static final int STATIC_OPT = 1;
048:
049: /*
050: *-----------------------------------------------------------------------------
051: *
052: * CmdProc --
053: *
054: * This procedure is invoked to process the "java::info" command.
055: * See the user documentation for details on what it does.
056: *
057: * Results:
058: * None.
059: *
060: * Side effects:
061: * See the user documentation.
062: *
063: *-----------------------------------------------------------------------------
064: */
065:
066: public void cmdProc(Interp interp, // Current interpreter for info query.
067: TclObject argv[]) // Argument list.
068: throws TclException // Exceptions thrown as a result of bad
069: // user input.
070: {
071: int lastArg = argv.length - 1;
072: boolean statOpt = false;
073: boolean typeOpt = false;
074: TclObject resultListObj;
075: Class c;
076:
077: if (argv.length < 2) {
078: throw new TclNumArgsException(interp, 1, argv,
079: "option ?arg arg ...?");
080: }
081:
082: int opt = TclIndex.get(interp, argv[1], validCmds, "option", 0);
083: switch (opt) {
084: case BASECLASS:
085: if (argv.length != 3) {
086: throw new TclNumArgsException(interp, 2, argv,
087: "objOrClass");
088: }
089: c = getClassFromObj(interp, argv[2]);
090: if (c != null) {
091: interp.setResult(getBaseNameFromClass(c));
092: }
093: return;
094: case CLASS:
095: if (argv.length != 3) {
096: throw new TclNumArgsException(interp, 2, argv,
097: "javaObj");
098: }
099: c = ReflectObject.getClass(interp, argv[2]);
100: if (c != null) {
101: interp.setResult(getNameFromClass(c));
102: }
103: return;
104: case DIMENSIONS:
105: if (argv.length != 3) {
106: throw new TclNumArgsException(interp, 2, argv,
107: "objOrClass");
108: }
109: c = getClassFromObj(interp, argv[2]);
110: if (c == null) {
111: interp.setResult(0);
112: } else {
113: interp.setResult(getNumDimsFromClass(c));
114: }
115: return;
116: case EVENTS:
117: if (argv.length != 3) {
118: throw new TclNumArgsException(interp, 2, argv,
119: "javaObj");
120: }
121: c = getClassFromObj(interp, argv[2]);
122: if (c == null) {
123: interp.resetResult();
124: return;
125: }
126: if (!PkgInvoker.isAccessible(c)) {
127: JavaInvoke.notAccessibleError(interp, c);
128: }
129: lookup: {
130: BeanInfo beanInfo;
131:
132: try {
133: beanInfo = Introspector.getBeanInfo(c);
134: } catch (IntrospectionException e) {
135: break lookup;
136: }
137: EventSetDescriptor events[] = beanInfo
138: .getEventSetDescriptors();
139:
140: if (events == null) {
141: break lookup;
142: }
143:
144: TclObject list = TclList.newInstance();
145: for (int i = 0; i < events.length; i++) {
146: TclList.append(interp, list, TclString
147: .newInstance(getNameFromClass(events[i]
148: .getListenerType())));
149: }
150: interp.setResult(list);
151: return;
152: }
153:
154: // The objOrClass doesn't support BeanInfo or it has no events.
155:
156: interp.resetResult();
157: return;
158: case FIELDS:
159: if ((lastArg < 2) || (lastArg > 4)) {
160: throw new TclNumArgsException(interp, 2, argv,
161: "?-type? ?-static? objOrClass");
162: }
163: for (int i = 2; i < lastArg; i++) {
164: opt = TclIndex.get(interp, argv[i], methOpts, "option",
165: 0);
166: switch (opt) {
167: case STATIC_OPT:
168: statOpt = true;
169: break;
170: case TYPE_OPT:
171: typeOpt = true;
172: break;
173: }
174: }
175: c = getClassFromObj(interp, argv[lastArg]);
176: if (c != null) {
177: if (!PkgInvoker.isAccessible(c)) {
178: JavaInvoke.notAccessibleError(interp, c);
179: }
180: resultListObj = getFieldInfoList(interp, c, statOpt,
181: typeOpt);
182: interp.setResult(resultListObj);
183: }
184: return;
185: case METHODS:
186: if ((lastArg < 2) || (lastArg > 4)) {
187: throw new TclNumArgsException(interp, 2, argv,
188: "?-type? ?-static? objOrClass");
189: }
190: for (int i = 2; i < lastArg; i++) {
191: opt = TclIndex.get(interp, argv[i], methOpts, "option",
192: 0);
193: switch (opt) {
194: case STATIC_OPT:
195: statOpt = true;
196: break;
197: case TYPE_OPT:
198: typeOpt = true;
199: break;
200: }
201: }
202: c = getClassFromObj(interp, argv[lastArg]);
203: if (c != null) {
204: if (!PkgInvoker.isAccessible(c)) {
205: JavaInvoke.notAccessibleError(interp, c);
206: }
207: resultListObj = getMethodInfoList(interp, c, statOpt,
208: typeOpt);
209: interp.setResult(resultListObj);
210: }
211: return;
212: case CONSTRUCTORS:
213: if (argv.length != 3) {
214: throw new TclNumArgsException(interp, 2, argv,
215: "objOrClass");
216: }
217: c = getClassFromObj(interp, argv[lastArg]);
218: if (c != null) {
219: if (!PkgInvoker.isAccessible(c)) {
220: JavaInvoke.notAccessibleError(interp, c);
221: }
222: resultListObj = getConstructorInfoList(interp, c);
223: interp.setResult(resultListObj);
224: }
225: return;
226: case PROPERTIES:
227: if ((lastArg < 2) || (lastArg > 3)) {
228: throw new TclNumArgsException(interp, 2, argv,
229: "?-type? objOrClass");
230: }
231: if (lastArg == 3) {
232: opt = TclIndex.get(interp, argv[2], propOpts, "option",
233: 0);
234:
235: // Since we just have one valid option, if the above call
236: // returns without an exception, we've got "-type" (or
237: // abreviations).
238:
239: typeOpt = true;
240: }
241: c = getClassFromObj(interp, argv[lastArg]);
242: if (c != null) {
243: if (!PkgInvoker.isAccessible(c)) {
244: JavaInvoke.notAccessibleError(interp, c);
245: }
246: resultListObj = getPropInfoList(interp, c, typeOpt);
247: interp.setResult(resultListObj);
248: }
249: return;
250: case SUPERCLASS:
251: if (argv.length != 3) {
252: throw new TclNumArgsException(interp, 2, argv,
253: "objOrClass");
254: }
255: c = getClassFromObj(interp, argv[2]);
256:
257: interp.resetResult();
258: if (c != null) {
259: c = c.getSuperclass();
260:
261: if (c != null) {
262: interp.setResult(getNameFromClass(c));
263: }
264:
265: }
266: return;
267: }
268: }
269:
270: /*
271: *-----------------------------------------------------------------------------
272: *
273: * getClassFromObj --
274: *
275: * Find the class associated with objOrClass.
276: *
277: * Results:
278: * Returns a Class.
279: *
280: * Side effects:
281: * Throws a Tcl exception if the objOrClass cannot be found.
282: *
283: *-----------------------------------------------------------------------------
284: */
285:
286: private static Class getClassFromObj(Interp interp, // Current interpreter for info query.
287: TclObject objOrClass) // Class or object for which the
288: // associated class is returned.
289: throws TclException // Exceptions thrown as a result of bad
290: // user input.
291: {
292: Class c;
293: try {
294: c = ReflectObject.getClass(interp, objOrClass);
295: } catch (TclException e) {
296: try {
297: c = ClassRep.get(interp, objOrClass);
298: } catch (TclException e2) {
299: throw new TclException(interp,
300: "unknown java class or object \"" + objOrClass
301: + "\"");
302: }
303: }
304: return c;
305: }
306:
307: /*
308: *-----------------------------------------------------------------------------
309: *
310: * gePropInfoList--
311: *
312: * Find the list of properties.
313: *
314: * Results:
315: * Returns a TclObject list of properties.
316: *
317: * Side effects:
318: * None.
319: *
320: *-----------------------------------------------------------------------------
321: */
322:
323: private static TclObject getPropInfoList(Interp interp, // Current interpreter for info query.
324: Class c, // The class for which we return the
325: // properties.
326: boolean typeOpt) // Include prop-type info in result.
327: throws TclException // Exceptions thrown as a result of bad
328: // user input.
329: {
330: BeanInfo beaninfo;
331: try {
332: beaninfo = Introspector.getBeanInfo(c);
333: } catch (IntrospectionException e) {
334: throw new TclException(interp, e.toString());
335: }
336:
337: PropertyDescriptor propDesc[] = null;
338: propDesc = beaninfo.getPropertyDescriptors();
339:
340: TclObject resultListObj = TclList.newInstance();
341: TclObject elementObj, pairObj;
342:
343: for (int i = 0; i < propDesc.length; i++) {
344: // If the -type option was specified, create a list containing
345: // the field's type and name.
346:
347: pairObj = TclList.newInstance();
348:
349: if (typeOpt) {
350: // The result of getPropertyType() may be "null" if this is an
351: // indexed property that does not support non-indexed access.
352: // For now, if the result is null, just don't add anything to the
353: // result. This is as yet UNTESTED because I couldn't produce a
354: // case in which null was returned.
355:
356: elementObj = TclString
357: .newInstance(getNameFromClass(propDesc[i]
358: .getPropertyType()));
359: if (elementObj != null) {
360: TclList.append(interp, pairObj, elementObj);
361: }
362: }
363: elementObj = TclString.newInstance(propDesc[i].getName());
364: TclList.append(interp, pairObj, elementObj);
365:
366: TclList.append(interp, resultListObj, pairObj);
367: }
368: return resultListObj;
369: }
370:
371: /*
372: *-----------------------------------------------------------------------------
373: *
374: * geFieldInfoList--
375: *
376: * Find the list of fields.
377: *
378: * Results:
379: * Returns a TclObject list of field signatures.
380: *
381: * Side effects:
382: * None.
383: *
384: *-----------------------------------------------------------------------------
385: */
386:
387: private static TclObject getFieldInfoList(Interp interp, // Current interpreter for info query.
388: Class c, // The class for which we return the
389: // fields.
390: boolean statOpt, // Return only/no static field info.
391: boolean typeOpt) // Include feild-type info in result.
392: throws TclException // Exceptions thrown as a result of bad
393: // user input.
394: {
395: // Get the array of fields associated with that class.
396:
397: Field[] fieldArray = FieldSig.getAccessibleFields(c);
398:
399: // Check whether each field is static. Based on -static option,
400: // ignore the field or add it to the result list.
401:
402: TclObject resultListObj = TclList.newInstance();
403: TclObject elementObj, sigObj, pairObj;
404: Class declClass;
405:
406: for (int f = 0; f < fieldArray.length; ++f) {
407: boolean isStatic = ((fieldArray[f].getModifiers() & Modifier.STATIC) > 0);
408: if (isStatic == statOpt) {
409: // If the declaring class is the same as c, and the same field
410: // is also declared in c, then the signature is the name of the
411: // field. Otherwise, the signature is a pair containing the field
412: // name and the declaring class name.
413:
414: sigObj = TclList.newInstance();
415:
416: String fieldName = fieldArray[f].getName();
417: elementObj = TclString.newInstance(fieldName);
418: TclList.append(interp, sigObj, elementObj);
419:
420: declClass = fieldArray[f].getDeclaringClass();
421: if (!declClass.equals(c)) {
422: for (int i = 0; i < fieldArray.length; ++i) {
423: if (i == f) {
424: continue;
425: }
426: if (!fieldName.equals(fieldArray[i].getName())) {
427: continue;
428: }
429: Class tmpClass = fieldArray[i]
430: .getDeclaringClass();
431: if (declClass.isAssignableFrom(tmpClass)) {
432: elementObj = TclString
433: .newInstance(getNameFromClass(declClass));
434: TclList.append(interp, sigObj, elementObj);
435: break;
436: }
437: }
438: }
439: if (typeOpt) {
440: // If -type was used, create a pair with the property type and
441: // signature. Append the pair to the result list.
442:
443: pairObj = TclList.newInstance();
444:
445: elementObj = TclString
446: .newInstance(getNameFromClass(fieldArray[f]
447: .getType()));
448: TclList.append(interp, pairObj, elementObj);
449: TclList.append(interp, pairObj, sigObj);
450: TclList.append(interp, resultListObj, pairObj);
451: } else {
452: // Append the signature object to the result list.
453:
454: TclList.append(interp, resultListObj, sigObj);
455: }
456: }
457: }
458: return resultListObj;
459: }
460:
461: /*
462: *-----------------------------------------------------------------------------
463: *
464: * getMethodInfoList--
465: *
466: * Find the list of static or instance methods.
467: *
468: * Results:
469: * Returns a TclObject list of method signatures.
470: *
471: * Side effects:
472: * None.
473: *
474: *-----------------------------------------------------------------------------
475: */
476:
477: private static TclObject getMethodInfoList(Interp interp, // Current interpreter for info query.
478: Class c, // The class for which we return the
479: // methods.
480: boolean statOpt, // Return only/no static method info.
481: boolean typeOpt) // Include return-type info in result.
482: throws TclException // Exceptions thrown as a result of bad
483: // user input.
484: {
485: // Get the array of accessible static methods associated with the class,
486: // otherwise get all the accessible non-static methods in the class,
487: // its superclasses, and interfaces.
488:
489: Method[] methodArray;
490:
491: if (statOpt) {
492: methodArray = FuncSig.getAccessibleStaticMethods(c);
493: } else {
494: methodArray = FuncSig.getAccessibleInstanceMethods(c);
495: }
496:
497: TclObject resultListObj = TclList.newInstance();
498: TclObject elementObj, sigObj;
499:
500: for (int m = 0; m < methodArray.length; ++m) {
501: if (true) { // FIXME: left in to keep diff simple
502: // Create the signature.
503:
504: sigObj = TclList.newInstance();
505:
506: elementObj = TclString.newInstance(methodArray[m]
507: .getName());
508: TclList.append(interp, sigObj, elementObj);
509:
510: Class[] paramArray = methodArray[m].getParameterTypes();
511: for (int p = 0; p < paramArray.length; ++p) {
512: elementObj = TclString
513: .newInstance(getNameFromClass(paramArray[p]));
514: TclList.append(interp, sigObj, elementObj);
515: }
516:
517: if (typeOpt) {
518: // If -type was used, create a sublist with the
519: // method type, signature and exception types.
520: // Append the sublist the result list.
521:
522: TclObject sublist = TclList.newInstance();
523: TclObject exceptions = TclList.newInstance();
524:
525: Class ex[] = methodArray[m].getExceptionTypes();
526: for (int i = 0; i < ex.length; i++) {
527: TclList.append(interp, exceptions, TclString
528: .newInstance(getNameFromClass(ex[i])));
529: }
530:
531: TclList
532: .append(
533: interp,
534: sublist,
535: TclString
536: .newInstance(getNameFromClass(methodArray[m]
537: .getReturnType())));
538: TclList.append(interp, sublist, sigObj);
539: TclList.append(interp, sublist, exceptions);
540:
541: TclList.append(interp, resultListObj, sublist);
542: } else {
543: // Append the signature object to the result list.
544:
545: TclList.append(interp, resultListObj, sigObj);
546: }
547: }
548: }
549: return resultListObj;
550: }
551:
552: /*
553: *-----------------------------------------------------------------------------
554: *
555: * geConstructorInfoList--
556: *
557: * Find the list of constructors' signatures.
558: *
559: * Results:
560: * Returns a TclObject list of constructor names.
561: *
562: * Side effects:
563: * None.
564: *
565: *-----------------------------------------------------------------------------
566: */
567:
568: private static TclObject getConstructorInfoList(Interp interp, // Current interpreter for info query.
569: Class c) // The class for which we return the
570: // constructors.
571: throws TclException // Exceptions thrown as a result of
572: // bad user input.
573: {
574: // Get the array of constructors associated with that class.
575:
576: Constructor[] constructorArray = FuncSig
577: .getAccessibleConstructors(c);
578:
579: TclObject resultListObj = TclList.newInstance();
580: TclObject elementObj, sigObj;
581:
582: for (int m = 0; m < constructorArray.length; ++m) {
583: // Create signature and append it to the result list.
584:
585: sigObj = TclList.newInstance();
586:
587: elementObj = TclString.newInstance(constructorArray[m]
588: .getName());
589: TclList.append(interp, sigObj, elementObj);
590:
591: Class[] paramArray = constructorArray[m]
592: .getParameterTypes();
593: for (int p = 0; p < paramArray.length; ++p) {
594: elementObj = TclString
595: .newInstance(getNameFromClass(paramArray[p]));
596: TclList.append(interp, sigObj, elementObj);
597: }
598: TclList.append(interp, resultListObj, sigObj);
599: }
600: return resultListObj;
601: }
602:
603: /*
604: *-----------------------------------------------------------------------------
605: *
606: * getNumDimsFromClass --
607: *
608: * Return the number of dimension (# of nested arrays) for a type
609: *
610: * Results:
611: * Returns a non-negative integer.
612: *
613: * Side effects:
614: * None.
615: *
616: *-----------------------------------------------------------------------------
617: */
618:
619: static int getNumDimsFromClass(Class type) // The class for which we return the name.
620: {
621: int dim;
622: for (dim = 0; type.isArray(); dim++) {
623: type = type.getComponentType();
624: }
625: return dim;
626: }
627:
628: /*
629: *-----------------------------------------------------------------------------
630: *
631: * getNameFromClass --
632: *
633: * Return the name of the class associated with "type". If "type" is an
634: * array, for each dimension, append "[]" to the name of he base class.
635: *
636: * Results:
637: * Returns a class name.
638: *
639: * Side effects:
640: * None.
641: *
642: *-----------------------------------------------------------------------------
643: */
644:
645: static String getNameFromClass(Class type) // The class for which we return the name.
646: {
647: StringBuffer name = new StringBuffer();
648:
649: while (type.isArray()) {
650: name.append("[]");
651: type = type.getComponentType();
652: }
653: String className = type.getName().replace('$', '.'); // For inner classes
654: name.insert(0, className);
655: return name.toString();
656: }
657:
658: /*
659: *-----------------------------------------------------------------------------
660: *
661: * getBaseNameFromClass --
662: *
663: * Return the name of the base class associated with "type".
664: *
665: * Results:
666: * Returns a base class name.
667: *
668: * Side effects:
669: * None.
670: *
671: *-----------------------------------------------------------------------------
672: */
673:
674: private static String getBaseNameFromClass(Class type) // The class for which we return the name.
675: {
676: while (type.isArray()) {
677: type = type.getComponentType();
678: }
679: return type.getName().toString().replace('$', '.'); // For inner classes
680: }
681:
682: } //end JavaInfoCmd
|