0001: /*
0002: * ReflectObject.java --
0003: *
0004: * Implements the Tcl internal representation of Java
0005: * reflection object.
0006: *
0007: * See the file "license.terms" for information on usage and
0008: * redistribution of this file, and for a DISCLAIMER OF ALL
0009: * WARRANTIES.
0010: *
0011: * RCS: @(#) $Id: ReflectObject.java,v 1.19 2006/04/13 07:36:50 mdejong Exp $
0012: *
0013: */
0014:
0015: package tcl.lang;
0016:
0017: import tcl.lang.reflect.PkgInvoker;
0018:
0019: import java.lang.reflect.*;
0020: import java.util.*;
0021: import java.beans.*;
0022:
0023: /**
0024: * A ReflectObject is used to create and access arbitrary Java objects
0025: * using the Java Reflection API. It wraps around a Java object (i.e.,
0026: * an instance of any Java class) and expose it to Tcl scripts. The
0027: * object is registered inside the interpreter and is given a string
0028: * name. Tcl scripts can manipulate this object as long as the the
0029: * reference count of the object is greater than zero.
0030: */
0031:
0032: public class ReflectObject implements InternalRep, CommandWithDispose {
0033:
0034: // The java.lang.Object wrapped by the ReflectObject representation.
0035:
0036: Object javaObj;
0037: Class javaClass;
0038:
0039: // The interpreter in which the java.lang.Object is registered in.
0040: // ReflectObject's are not shared among interpreters for safety
0041: // reasons.
0042:
0043: Interp ownerInterp;
0044:
0045: // The reference ID of this object. (same as instance command name)
0046:
0047: String refID;
0048:
0049: // This variables records how many TclObject's are using
0050: // this ReflectObject internal rep. In this example:
0051: //
0052: // set x [new java.lang.Integer 1]
0053: // set y [format %s $x]
0054: // java::info methods $y
0055: //
0056: // The two objects $x and $y share the same ReflectObject instance.
0057: // useCount is 2 when the java::info command has just translated the
0058: // string $y into a ReflectObject.
0059: //
0060: // useCount is initially 1. It will be more than 1 only when the
0061: // script tries to refer to the object using its string form, or when
0062: // the same object is returned by the Reflection API more than once.
0063: //
0064: // This variable is called useCount rather than refCount to avoid
0065: // confusion with TclObject.refCount.
0066:
0067: private int useCount;
0068:
0069: // This variable marks whether the object is still considered "valid"
0070: // in Tcl scripts. An object is no longer valid if its object command
0071: // has been explicitly deleted from the interpreter.
0072:
0073: private boolean isValid;
0074:
0075: // Stores the bindings of this ReflectObject. This member variable is used
0076: // in the BeanEventMgr class.
0077:
0078: Hashtable bindings;
0079:
0080: // the string representation of the null reflect object
0081:
0082: private static final String NULL_REP = "java0x0";
0083:
0084: /*
0085:
0086: // this really should be final but there is a bug in Sun's javac which
0087: // incorrectly flags this as a "final not initialized" error
0088: //private static final ReflectObject NULL_OBJECT;
0089:
0090: private static ReflectObject NULL_OBJECT;
0091:
0092:
0093: // Allocate single object used to represent the untyped null java
0094: // Object. A null object is not registered (hence it can't be deleted).
0095: static {
0096: NULL_OBJECT = makeNullObject(null, null);
0097: }
0098:
0099: */
0100:
0101: protected static final String NOCONVERT = "-noconvert";
0102:
0103: protected static final String CMD_PREFIX = "java0x";
0104:
0105: // set to true to see extra output
0106:
0107: private static final boolean debug = false;
0108:
0109: // set to true to see dump the relfect table
0110: // we adding or removing an object from the table
0111:
0112: private static final boolean dump = false;
0113:
0114: // Private helper for creating reflected null objects
0115:
0116: private static ReflectObject makeNullObject(Interp i, Class c) {
0117: ReflectObject ro = new ReflectObject();
0118:
0119: ro.ownerInterp = i;
0120:
0121: ro.refID = NULL_REP;
0122: ro.useCount = 1;
0123: ro.isValid = true;
0124:
0125: ro.javaObj = null;
0126: ro.javaClass = c;
0127:
0128: return ro;
0129: }
0130:
0131: // Return the string used to hash this Java object into the reflect table
0132: // or the conflict table (in case of a duplicate hash in reflect table.
0133: // For example: java.lang.Object.546464 -> ReflectObject
0134:
0135: private static String getHashString(Class cl, Object obj) {
0136: StringBuffer buff = new StringBuffer();
0137: buff.append(JavaInfoCmd.getNameFromClass(cl));
0138: buff.append('.');
0139:
0140: // A bad hash would suck in terms of performance but it should not
0141: // generate any errors. Use '1' for an extreme test of this case.
0142: //buff.append('1');
0143: buff.append(System.identityHashCode(obj));
0144:
0145: return buff.toString();
0146: }
0147:
0148: // Private helper used to add a reflect object to the reflect table
0149:
0150: private static void addToReflectTable(ReflectObject roRep) {
0151: Interp interp = roRep.ownerInterp;
0152: Class cl = roRep.javaClass;
0153: Object obj = roRep.javaObj;
0154: String id = roRep.refID;
0155:
0156: String hash = getHashString(cl, obj);
0157: ReflectObject found = (ReflectObject) interp.reflectObjTable
0158: .get(hash);
0159:
0160: if (found == null) {
0161: // There was no mapping for this hash value, add one now.
0162:
0163: if (debug) {
0164: System.out.println("new reflect table entry for " + id
0165: + " with hash \"" + hash + "\"");
0166: }
0167:
0168: interp.reflectObjTable.put(hash, roRep);
0169: } else {
0170: // If there is already an entry for this hash value, it means
0171: // that there are two different objects of the same class that
0172: // have the same hash value. In this case, add the ReflectObject
0173: // to the conflict table for this hash value.
0174: // java.lang.Object.546464 -> {ReflectObject ReflectObject ReflectObject}
0175:
0176: if (debug) {
0177: System.out
0178: .println("hash conflict in reflect table for "
0179: + id + " with hash \"" + hash + "\"");
0180: }
0181:
0182: ArrayList conflicts = (ArrayList) interp.reflectConflictTable
0183: .get(hash);
0184:
0185: if (conflicts == null) {
0186: conflicts = new ArrayList();
0187: interp.reflectConflictTable.put(hash, conflicts);
0188: }
0189:
0190: if (debug) {
0191: if (conflicts.contains(roRep)) {
0192: throw new TclRuntimeError(
0193: "the conflict table already contains the ReflectObject");
0194: }
0195: }
0196:
0197: conflicts.add(roRep);
0198: }
0199: }
0200:
0201: // Private helper used to remove a reflected object from the reflect table.
0202:
0203: private static void removeFromReflectTable(ReflectObject roRep) {
0204: Interp interp = roRep.ownerInterp;
0205: Class cl = roRep.javaClass;
0206: Object obj = roRep.javaObj;
0207: String id = roRep.refID;
0208:
0209: String hash = getHashString(cl, obj);
0210: ReflectObject found = (ReflectObject) interp.reflectObjTable
0211: .get(hash);
0212:
0213: // This should never happen
0214: if (found == null) {
0215:
0216: throw new TclRuntimeError(
0217: "reflect table returned null for " + id
0218: + " with hash \"" + hash + "\"");
0219: } else {
0220:
0221: // In the first case, the ReflectObject we hashed to is the same as the
0222: // one we passed in, we can just remove it from the reflect table.
0223: // Be careful to also check the conflict table, if there is 1 entry
0224: // in the conflict table then toast the conflict table and remap the
0225: // reflect object in the conflict table. Otherwise, grab the first
0226: // value out of the conflict table and hash that in the reflect table.
0227:
0228: if (found == roRep) {
0229: interp.reflectObjTable.remove(hash);
0230:
0231: if (debug) {
0232: System.out.println("removing reflect table entry "
0233: + hash);
0234: }
0235:
0236: ArrayList conflicts = (ArrayList) interp.reflectConflictTable
0237: .get(hash);
0238:
0239: if (conflicts != null) {
0240: Object first = conflicts.remove(0);
0241:
0242: if (conflicts.isEmpty()) {
0243: interp.reflectConflictTable.remove(hash);
0244: }
0245:
0246: interp.reflectObjTable.put(hash, first);
0247:
0248: if (debug) {
0249: System.out
0250: .println("replaced reflect table entry from conflict "
0251: + hash);
0252: }
0253: }
0254: } else {
0255:
0256: // In the second case, the ReflectObject we hashed to did not match
0257: // the entry in the reflect table. This means it must be in the
0258: // conflict table so remove it from there. Be sure to remove the
0259: // conflict table mapping if we are removing the last conflict!
0260:
0261: ArrayList conflicts = (ArrayList) interp.reflectConflictTable
0262: .get(hash);
0263:
0264: // This should never happen!
0265:
0266: if (conflicts == null) {
0267: throw new TclRuntimeError(
0268: "conflict table mapped to null for " + id
0269: + " with hash \"" + hash + "\"");
0270: }
0271:
0272: if (debug) {
0273: System.out
0274: .println("removing conflict table entry for "
0275: + id
0276: + " with hash \""
0277: + hash
0278: + "\"");
0279: }
0280:
0281: // FIXME: double check that this uses == compare
0282: // This remove should never fail!
0283:
0284: int index = conflicts.indexOf(roRep);
0285: if (index == -1) {
0286: throw new TclRuntimeError(
0287: "no entry in conflict table for " + id
0288: + " with hash \"" + hash + "\"");
0289: }
0290: conflicts.remove(index);
0291:
0292: if (conflicts.isEmpty()) {
0293: interp.reflectConflictTable.remove(hash);
0294: }
0295: }
0296: }
0297: }
0298:
0299: // Find in ConflictTable will search the reflect hash conflict table
0300: // for a given {Class Object} pair. If the pair exists its ReflectObject
0301: // will be returned. If not, null will be returned. In the case where
0302: // we want to add a new object that conflicts, the table will not
0303: // exists yet so we can't find a match.
0304:
0305: private static ReflectObject findInConflictTable(Interp interp,
0306: Object obj, String hash) {
0307: ArrayList conflicts = (ArrayList) interp.reflectConflictTable
0308: .get(hash);
0309:
0310: if (conflicts == null) {
0311: return null;
0312: }
0313:
0314: for (ListIterator iter = conflicts.listIterator(); iter
0315: .hasNext();) {
0316: ReflectObject found = (ReflectObject) iter.next();
0317: if (found.javaObj == obj) {
0318: if (debug) {
0319: System.out
0320: .println("found conflict table entry for hash "
0321: + hash);
0322: }
0323:
0324: return found;
0325: }
0326: }
0327:
0328: return null;
0329: }
0330:
0331: // Find in ReflectTable will search the reflect table for a given
0332: // {Class Object} pair. If the pair exists its ReflectObject
0333: // will be returned. If not, null will be returned.
0334:
0335: private static ReflectObject findInReflectTable(Interp interp,
0336: Class cl, Object obj) {
0337: String hash = getHashString(cl, obj);
0338: ReflectObject found = (ReflectObject) interp.reflectObjTable
0339: .get(hash);
0340:
0341: // If there is no mapping in the reflect table for this object, return null
0342:
0343: if (found == null) {
0344: if (debug) {
0345: System.out
0346: .println("could not find reflect object for hash \""
0347: + hash + "\"");
0348: }
0349:
0350: return null;
0351: } else {
0352: // If we find a mapping in the reflect table there are two cases
0353: // we need to worry about. If the object we mapped to is the same
0354: // object as the one we are have, then just return it. In the
0355: // case where the object we map to is not the same, we need
0356: // to look in the reflect table because it could be in there.
0357:
0358: if (found.javaObj == obj) {
0359: if (debug) {
0360: System.out
0361: .println("found reflect table match for id "
0362: + found.refID
0363: + " with hash \""
0364: + hash + "\"");
0365: }
0366:
0367: return found;
0368: } else {
0369: return findInConflictTable(interp, obj, hash);
0370: }
0371: }
0372: }
0373:
0374: // This method is only used for debugging, it will dump the contents of the
0375: // reflect table in a human readable form. The dump is to stdout.
0376:
0377: // dump the table from a Tcl/Java shell like this
0378: // set i [java::getinterp]
0379: // java::call tcl.lang.ReflectObject dump $i
0380:
0381: public static void dump(Interp interp) {
0382: try {
0383: System.out
0384: .println("BEGIN DUMP -------------------------------");
0385: System.out.println("interp.reflectObjCount = "
0386: + interp.reflectObjCount);
0387: System.out.println("interp.reflectObjTable.size() = "
0388: + interp.reflectObjTable.size());
0389: System.out.println("interp.reflectConflictTable.size() = "
0390: + interp.reflectConflictTable.size());
0391:
0392: // Loop over the entries in the reflectObjTable and dump them out.
0393:
0394: for (Iterator iter = interp.reflectObjTable.entrySet()
0395: .iterator(); iter.hasNext();) {
0396: Map.Entry entry = (Map.Entry) iter.next();
0397:
0398: System.out.println();
0399: String hash = (String) entry.getKey();
0400: ReflectObject roRep = (ReflectObject) entry.getValue();
0401:
0402: if (roRep == null) {
0403: throw new RuntimeException("Reflect table entry \""
0404: + hash + "\" hashed to null");
0405: }
0406:
0407: // do sanity check
0408: if (roRep.ownerInterp != interp) {
0409: throw new RuntimeException(
0410: "roRep.ownerInterp not the same as current interp");
0411: }
0412:
0413: // Check to see if the command is in the Tcl command table.
0414: if (interp.getCommand(roRep.refID) == null) {
0415: System.out
0416: .println("could not find command named \""
0417: + roRep.refID + "\"");
0418: }
0419:
0420: String hash2 = getHashString(roRep.javaClass,
0421: roRep.javaObj);
0422:
0423: if (!hash.equals(hash2)) {
0424: throw new RuntimeException("hash \"" + hash
0425: + "\" is not equal to calculated"
0426: + " hash \"" + hash2);
0427: }
0428:
0429: System.out
0430: .println("hash \""
0431: + hash
0432: + "\" corresponds to ReflectObject with "
0433: + "refID \""
0434: + roRep.refID
0435: + "\" useCount = \""
0436: + roRep.useCount
0437: + "\" isValid = \""
0438: + roRep.isValid
0439: + "\""
0440: + " javaClass = \""
0441: + JavaInfoCmd
0442: .getNameFromClass(roRep.javaClass)
0443: + "\""
0444: + " System.identityHashCode(javaObj) = \""
0445: + System
0446: .identityHashCode(roRep.javaObj)
0447: + "\"");
0448:
0449: ArrayList conflicts = (ArrayList) interp.reflectConflictTable
0450: .get(hash);
0451:
0452: if (conflicts != null) {
0453: System.out.println("Found conflict table for hash "
0454: + hash);
0455:
0456: for (ListIterator iter2 = conflicts.listIterator(); iter
0457: .hasNext();) {
0458: ReflectObject found = (ReflectObject) iter2
0459: .next();
0460:
0461: System.out
0462: .println("hash conflict for \""
0463: + hash
0464: + "\" corresponds to ReflectObject with "
0465: + "refID \"" + found.refID
0466: + "\"");
0467: }
0468: }
0469: }
0470: } catch (Throwable e) {
0471: e.printStackTrace(System.out);
0472: }
0473: }
0474:
0475: /*
0476: *----------------------------------------------------------------------
0477: *
0478: * makeReflectObject --
0479: *
0480: * Wraps an Java Object in a ReflectObject. If the same Java
0481: * Object has already been wrapped in a ReflectObject, return
0482: * that ReflectObject. Otherwise, create a new ReflectObject to
0483: * wrap the Java Object.
0484: *
0485: * Results:
0486: * None.
0487: *
0488: * Side effects:
0489: * The object is unregistered (and thus no longer accessible from
0490: * Tcl scripts) if no other if no other TclObjects are
0491: * still using this internal rep.
0492: *
0493: *----------------------------------------------------------------------
0494: */
0495:
0496: private static ReflectObject makeReflectObject(Interp interp,
0497: Class cl, Object obj) throws TclException // if a null class with a non null object is passed in
0498: {
0499: //final boolean debug = false;
0500:
0501: if (cl != null && !PkgInvoker.isAccessible(cl)) {
0502: JavaInvoke.notAccessibleError(interp, cl);
0503: }
0504:
0505: if (obj == null) {
0506: // this is the null reflect object case
0507:
0508: if (debug) {
0509: System.out.println("null object");
0510: }
0511:
0512: if (debug && (cl != null)) {
0513: System.out.println("non null class with null object");
0514: System.out.println("non null class was " + cl);
0515: }
0516:
0517: // null objects are not added to the reflect table like other instances
0518:
0519: return makeNullObject(interp, cl);
0520: }
0521:
0522: if (cl == null) {
0523: // we have no way to deal with a non null object that has a
0524: // null class reference type, we must give up in this case
0525:
0526: throw new TclException(interp,
0527: "non null reflect object with null class is not valid");
0528: }
0529:
0530: // apply builtin type conversion rules (so int becomes java.lang.Integer)
0531:
0532: if (cl == Integer.TYPE)
0533: cl = Integer.class;
0534: else if (cl == Boolean.TYPE)
0535: cl = Boolean.class;
0536: else if (cl == Long.TYPE)
0537: cl = Long.class;
0538: else if (cl == Float.TYPE)
0539: cl = Float.class;
0540: else if (cl == Double.TYPE)
0541: cl = Double.class;
0542: else if (cl == Byte.TYPE)
0543: cl = Byte.class;
0544: else if (cl == Short.TYPE)
0545: cl = Short.class;
0546: else if (cl == Character.TYPE)
0547: cl = Character.class;
0548: else if (cl == Void.TYPE)
0549: throw new TclException(interp,
0550: "void object type can not be reflected");
0551:
0552: if (debug) {
0553: System.out.println("object will be reflected as "
0554: + JavaInfoCmd.getNameFromClass(cl));
0555: }
0556:
0557: // Try to find this {Class Object} pair in the reflect table.
0558:
0559: ReflectObject roRep = findInReflectTable(interp, cl, obj);
0560:
0561: if (roRep != null) {
0562: // If it is already in the table just increment the use count and return it
0563:
0564: roRep.useCount++;
0565:
0566: if (debug) {
0567: System.out.println("incr useCount of found object "
0568: + roRep.refID + " to " + roRep.useCount);
0569: }
0570:
0571: return roRep;
0572: } else {
0573: if (cl.isArray()) {
0574: roRep = new ArrayObject();
0575: } else {
0576: roRep = new ReflectObject();
0577: }
0578:
0579: roRep.ownerInterp = interp;
0580: roRep.javaObj = obj;
0581: roRep.javaClass = cl;
0582:
0583: // make sure the object can be represented by the given Class
0584: Class obj_class = roRep.javaObj.getClass();
0585:
0586: if (!roRep.javaClass.isAssignableFrom(obj_class)) {
0587: throw new TclException(interp, "object of type "
0588: + JavaInfoCmd.getNameFromClass(obj_class)
0589: + " can not be referenced as type "
0590: + JavaInfoCmd.getNameFromClass(roRep.javaClass));
0591: }
0592:
0593: if (dump) {
0594: System.out.println("PRE REGISTER DUMP");
0595: dump(interp);
0596: }
0597:
0598: // Register the object in the interp.
0599:
0600: interp.reflectObjCount++; // incr id, the first id used will be 1
0601: roRep.refID = CMD_PREFIX
0602: + Long.toHexString(interp.reflectObjCount);
0603:
0604: interp.createCommand(roRep.refID, roRep);
0605: addToReflectTable(roRep);
0606:
0607: if (debug) {
0608: System.out.println("reflect object " + roRep.refID
0609: + " of type "
0610: + JavaInfoCmd.getNameFromClass(roRep.javaClass)
0611: + " registered");
0612: }
0613:
0614: roRep.useCount = 1;
0615: roRep.isValid = true;
0616:
0617: if (dump) {
0618: System.out.println("POST REGISTER DUMP");
0619: dump(interp);
0620: }
0621:
0622: return roRep;
0623: }
0624: }
0625:
0626: /*
0627: *----------------------------------------------------------------------
0628: *
0629: * dispose --
0630: *
0631: * Called when a TclObject no longers uses this internal rep. We
0632: * unregister the java.lang.Object if no other TclObjects are
0633: * still using this internal rep.
0634: *
0635: * Results:
0636: * None.
0637: *
0638: * Side effects:
0639: * The object is unregistered (and thus no longer accessible from
0640: * Tcl scripts) if no other if no other TclObjects are
0641: * still using this internal rep.
0642: *
0643: *----------------------------------------------------------------------
0644: */
0645:
0646: public void dispose() {
0647: if (debug) {
0648: System.out.println("dispose called for reflect object "
0649: + refID);
0650: }
0651:
0652: useCount--;
0653: if ((useCount == 0) && (refID != NULL_REP)) {
0654: // No TclObject is using this internal rep anymore. Free it.
0655:
0656: if (debug) {
0657: System.out.println("reflect object " + refID
0658: + " is no longer being used");
0659: }
0660:
0661: if (dump) {
0662: System.out.println("PRE DELETE DUMP");
0663: dump(ownerInterp);
0664: }
0665:
0666: // Don't delete command if interp was already deleted
0667: if (isValid)
0668: ownerInterp.deleteCommand(refID);
0669: removeFromReflectTable(this );
0670:
0671: ownerInterp = null;
0672: javaObj = null;
0673: javaClass = null;
0674: bindings = null;
0675: refID = NULL_REP;
0676: }
0677: }
0678:
0679: /*
0680: *----------------------------------------------------------------------
0681: *
0682: * duplicate --
0683: *
0684: * Get a copy of this ReflectObject for copy-on-write
0685: * operations. We just increment its useCount and return the same
0686: * ReflectObject because ReflectObject's cannot be modified, so
0687: * they don't need copy-on-write protections.
0688: *
0689: * Results:
0690: * The same internal rep.
0691: *
0692: * Side effects:
0693: * None.
0694: *
0695: *----------------------------------------------------------------------
0696: */
0697:
0698: public InternalRep duplicate() {
0699: useCount++;
0700:
0701: if (debug) {
0702: System.out.println("duplicate(): incr useCount of " + refID
0703: + " to " + useCount);
0704: }
0705:
0706: return this ;
0707: }
0708:
0709: /*
0710: *----------------------------------------------------------------------
0711: *
0712: * setReflectObjectFromAny --
0713: *
0714: * Called to convert an TclObject's internal rep to ReflectObject.
0715: *
0716: * Results:
0717: * None.
0718: *
0719: * Side effects:
0720: * When successful, the internal representation of tobj is
0721: * changed to ReflectObject, if it is not already so.
0722: *
0723: *----------------------------------------------------------------------
0724: */
0725:
0726: private static void setReflectObjectFromAny(Interp interp, // Current interpreter. Must be non-null.
0727: TclObject tobj) // The TclObject to convert.
0728: throws TclException // If the object's internal rep is not
0729: // already ReflectObject, and the string rep
0730: // is not the name of a java.lang.Object
0731: // registered in the given interpreter.
0732: // Error message is left inside interp.
0733: {
0734: InternalRep rep = tobj.getInternalRep();
0735: ReflectObject roRep;
0736:
0737: if (rep instanceof ReflectObject) {
0738: roRep = (ReflectObject) rep;
0739: if (roRep.isValid && (roRep.ownerInterp == interp)) {
0740: return;
0741: }
0742: }
0743:
0744: String s = tobj.toString();
0745: if (s.startsWith(CMD_PREFIX)) {
0746: if (s.equals(NULL_REP)) {
0747: tobj.setInternalRep(makeReflectObject(interp, null,
0748: null));
0749: return;
0750: } else {
0751: Command cmd = interp.getCommand(s);
0752: if ((cmd != null) && (cmd instanceof ReflectObject)
0753: && ((ReflectObject) cmd).isValid) {
0754: roRep = (ReflectObject) cmd;
0755: roRep.useCount++;
0756:
0757: if (debug) {
0758: System.out
0759: .println("setReflectObjectFromAny(): incr useCount of "
0760: + roRep.refID
0761: + " to "
0762: + roRep.useCount);
0763: }
0764:
0765: tobj.setInternalRep(roRep);
0766: return;
0767: }
0768: }
0769: }
0770:
0771: throw new TclException(interp, "unknown java object \"" + tobj
0772: + "\"");
0773: }
0774:
0775: /*
0776: *----------------------------------------------------------------------
0777: *
0778: * newInstance --
0779: *
0780: * Creates a new instance of a TclObject that wraps a
0781: * java.lang.Object.
0782: *
0783: * Results:
0784: * The newly created TclObject.
0785: *
0786: * Side effects:
0787: * The java.lang.Object will be registered in the interpreter.
0788: *
0789: *----------------------------------------------------------------------
0790: */
0791:
0792: public static TclObject newInstance(Interp interp, // Current interpreter.
0793: Class cl, // class of the reflect instance
0794: Object obj) // java.lang.Object to wrap.
0795: throws TclException {
0796: return new TclObject(makeReflectObject(interp, cl, obj));
0797: }
0798:
0799: /*
0800: *----------------------------------------------------------------------
0801: *
0802: * get --
0803: *
0804: * Returns a java.lang.Object represented by tobj. tobj must have a
0805: * ReflectObject internal rep, or its string rep must be one of the
0806: * currently registered objects.
0807: *
0808: * Results:
0809: * The Java object represented by tobj.
0810: *
0811: * Side effects:
0812: * When successful, the internal representation of tobj is
0813: * changed to ReflectObject, if it is not already so.
0814: *
0815: *----------------------------------------------------------------------
0816: */
0817:
0818: public static Object get(Interp interp, // Current interpreter. Must be non-null.
0819: TclObject tobj) // The TclObject to query.
0820: throws TclException // If the internal rep of tobj cannot
0821: // be converted to a ReflectObject.
0822: // Error message is left inside interp.
0823: {
0824: setReflectObjectFromAny(interp, tobj);
0825: ReflectObject rep = (ReflectObject) tobj.getInternalRep();
0826: return rep.javaObj;
0827: }
0828:
0829: /*
0830: *----------------------------------------------------------------------
0831: *
0832: * getClass --
0833: *
0834: * Returns a java.lang.Class object that is the ref type of this
0835: * reflect object. This is not always the same class as is returned
0836: * by a call to ((Object) o).getClass().
0837: *
0838: * Results:
0839: * The Java class object used to reference tobj.
0840: *
0841: * Side effects:
0842: * When successful, the internal representation of tobj is
0843: * changed to ReflectObject, if it is not already so.
0844: *
0845: *----------------------------------------------------------------------
0846: */
0847:
0848: public static Class getClass(Interp interp, // Current interpreter. Must be non-null.
0849: TclObject tobj) // The TclObject to query.
0850: throws TclException // If the internal rep of tobj cannot
0851: // be converted to a ReflectObject.
0852: // Error message is left inside interp.
0853: {
0854: setReflectObjectFromAny(interp, tobj);
0855: ReflectObject rep = (ReflectObject) tobj.getInternalRep();
0856: return rep.javaClass;
0857: }
0858:
0859: /*
0860: *----------------------------------------------------------------------
0861: *
0862: * getReflectObject --
0863: *
0864: * Returns the InternalRep of a the ReflectObject represented by
0865: * tobj. Only the java:: commands should call this
0866: * method. (java::bind, java::call, etc).
0867: *
0868: * Results:
0869: * The Java object represented by tobj.
0870: *
0871: * Side effects:
0872: * When successful, the internal representation of tobj is
0873: * changed to ReflectObject, if it is not already so.
0874: *
0875: *----------------------------------------------------------------------
0876: */
0877:
0878: static ReflectObject getReflectObject(Interp interp, // Current interpreter. Must be non-null
0879: TclObject tobj) // The TclObject to query.
0880: throws TclException // If the internal rep of tobj cannot
0881: // be converted to a ReflectObject.
0882: // Error message is left inside interp.
0883: {
0884: setReflectObjectFromAny(interp, tobj);
0885: return (ReflectObject) tobj.getInternalRep();
0886: }
0887:
0888: /*
0889: *----------------------------------------------------------------------
0890: *
0891: * cmdProc --
0892: *
0893: * This cmdProc implements the Tcl command used to invoke methods
0894: * of the java.lang.Object stored in this ReflectObject internal
0895: * rep. For example, this method is called to process the "$v"
0896: * command at the second line of this script:
0897: *
0898: * set v [java::new java.util.Vector]
0899: * $v addElement "foo"
0900: *
0901: * Results:
0902: * None.
0903: *
0904: * Side effects:
0905: * If the given method returns a value, it is converted into a
0906: * TclObject and stored as the result of the interpreter.
0907: *
0908: *----------------------------------------------------------------------
0909: */
0910:
0911: public void cmdProc(Interp interp, // Current interpreter.
0912: TclObject[] argv) // Argument list.
0913: throws TclException // Standard Tcl exception;
0914: {
0915: boolean convert;
0916: int sigIdx;
0917:
0918: if (!isValid) {
0919: throw new TclException(interp,
0920: "reflected object is no longer valid");
0921: }
0922:
0923: if (argv.length < 2) {
0924: throw new TclNumArgsException(interp, 1, argv,
0925: "?-noconvert? signature ?arg arg ...?");
0926: }
0927:
0928: String arg1 = argv[1].toString();
0929: if ((arg1.length() >= 2) && (NOCONVERT.startsWith(arg1))) {
0930: convert = false;
0931: sigIdx = 2;
0932: } else {
0933: convert = true;
0934: sigIdx = 1;
0935: }
0936:
0937: if (argv.length < sigIdx + 1) {
0938: throw new TclNumArgsException(interp, 1, argv,
0939: "?-noconvert? signature ?arg arg ...?");
0940: }
0941:
0942: int startIdx = sigIdx + 1;
0943: int count = argv.length - startIdx;
0944:
0945: TclObject result = JavaInvoke.callMethod(interp, argv[0],
0946: argv[sigIdx], argv, startIdx, count, convert);
0947:
0948: if (result == null)
0949: interp.resetResult();
0950: else
0951: interp.setResult(result);
0952: }
0953:
0954: /*
0955: *----------------------------------------------------------------------
0956: *
0957: * disposeCmd --
0958: *
0959: * This method is called when the object command has been deleted
0960: * from an interpreter. It marks the ReflectObject no longer
0961: * accessible from Tcl scripts.
0962: *
0963: * Results:
0964: * None.
0965: *
0966: * Side effects:
0967: * The ReflectObject is no longer accessible from Tcl scripts.
0968: *
0969: *----------------------------------------------------------------------
0970: */
0971:
0972: public void disposeCmd() {
0973: if (debug) {
0974: System.out.println("ReflectObject instance " + refID
0975: + " -> disposedCmd()");
0976: }
0977:
0978: isValid = false;
0979: }
0980:
0981: /*
0982: *----------------------------------------------------------------------
0983: *
0984: * toString --
0985: *
0986: * Called to query the string representation of the Tcl
0987: * object. This method is called only by TclObject.toString()
0988: * when TclObject.stringRep is null.
0989: *
0990: * Results:
0991: * Returns the string representation of this ReflectObject.
0992: *
0993: * Side effects:
0994: * None.
0995: *
0996: *----------------------------------------------------------------------
0997: */
0998:
0999: public String toString() {
1000: return refID;
1001: }
1002:
1003: } // end ReflectObject
|