0001: /*
0002: * JavaInvoke.java --
0003: *
0004: * This class implements the common routines used by the java::*
0005: * commands to access the Java Reflection API.
0006: *
0007: * Copyright (c) 1997 Sun Microsystems, Inc.
0008: *
0009: * See the file "license.terms" for information on usage and
0010: * redistribution of this file, and for a DISCLAIMER OF ALL
0011: * WARRANTIES.
0012: *
0013: * RCS: @(#) $Id: JavaInvoke.java,v 1.25 2006/06/13 06:52:47 mdejong Exp $
0014: *
0015: */
0016:
0017: package tcl.lang;
0018:
0019: import tcl.lang.reflect.*;
0020: import java.lang.reflect.*;
0021: import java.util.*;
0022: import java.beans.*;
0023:
0024: /**
0025: * This class implements the common routines used by the java::*
0026: * commands to create Java objects, call Java methods and access fields
0027: * and properties. It also has auxiliary routines for converting between
0028: * TclObject's and Java Object's.
0029: */
0030:
0031: class JavaInvoke {
0032:
0033: // We need to use empty array Object[0] a lot. We keep a static copy
0034: // and re-use it to avoid garbage collection.
0035:
0036: static private Object EMPTY_ARGS[] = new Object[0];
0037:
0038: /*
0039: *-----------------------------------------------------------------------------
0040: *
0041: * newInstance --
0042: *
0043: * Call the specified constructor.
0044: *
0045: * Results:
0046: * When successful, the object created by the constructor.
0047: *
0048: * Side effects:
0049: * The constructor can cause arbitrary side effects.
0050: *
0051: *-----------------------------------------------------------------------------
0052: */
0053:
0054: static TclObject newInstance(Interp interp, // Current interpreter.
0055: TclObject signature, // Constructor signature.
0056: TclObject[] argv, // Arguments.
0057: int startIdx, // Index of the first argument in argv to
0058: // pass to the constructor.
0059: int count) // Number of arguments to pass to the
0060: // constructor.
0061: throws TclException // Standard Tcl exception.
0062: {
0063: // Some built-in types have wrapper classes that behave in
0064: // unexpected ways. For example, the Boolean constructor
0065: // is overloaded to accept either a boolean primitive or
0066: // a String value. The FuncSig module prefers method signatures
0067: // that accept a String, but the version that accepts a String
0068: // does not match Tcl's number parsing semantics. Fix this problem
0069: // by explicitly invoking the wrapper constructor that accepts
0070: // a primitive type so that the type conversion logic in this
0071: // module is used to pass a Tcl value to a Java primitive argument.
0072:
0073: if (count == 1) {
0074: final String sig = signature.toString();
0075: if (sig.equals("Boolean")
0076: || sig.equals("java.lang.Boolean")) {
0077: signature = TclString
0078: .newInstance("java.lang.Boolean boolean");
0079: } else if (sig.equals("Integer")
0080: || sig.equals("java.lang.Integer")) {
0081: signature = TclString
0082: .newInstance("java.lang.Integer int");
0083: } else if (sig.equals("Byte")
0084: || sig.equals("java.lang.Byte")) {
0085: signature = TclString
0086: .newInstance("java.lang.Byte byte");
0087: } else if (sig.equals("Short")
0088: || sig.equals("java.lang.Short")) {
0089: signature = TclString
0090: .newInstance("java.lang.Short short");
0091: } else if (sig.equals("Character")
0092: || sig.equals("java.lang.Character")) {
0093: signature = TclString
0094: .newInstance("java.lang.Character char");
0095: } else if (sig.equals("Long")
0096: || sig.equals("java.lang.Long")) {
0097: signature = TclString
0098: .newInstance("java.lang.Long long");
0099: } else if (sig.equals("Float")
0100: || sig.equals("java.lang.Float")) {
0101: signature = TclString
0102: .newInstance("java.lang.Float float");
0103: } else if (sig.equals("Double")
0104: || sig.equals("java.lang.Double")) {
0105: signature = TclString
0106: .newInstance("java.lang.Double double");
0107: }
0108: }
0109:
0110: FuncSig sig = FuncSig.get(interp, null, signature, argv,
0111: startIdx, count, false);
0112:
0113: Object javaObj = call(interp, sig.pkgInvoker, signature,
0114: sig.func, null, argv, startIdx, count);
0115:
0116: return ReflectObject
0117: .newInstance(interp, sig.targetCls, javaObj);
0118: }
0119:
0120: /*
0121: *-----------------------------------------------------------------------------
0122: *
0123: * callMethod --
0124: *
0125: * Call the specified instance or static method of the given object.
0126: *
0127: * Results:
0128: * When successful, this method returns the Java object that the
0129: * Java method would have returned. If the Java method has a void
0130: * return type then null is returned.
0131: *
0132: * Side effects:
0133: * The method can cause arbitrary side effects.
0134: *
0135: *-----------------------------------------------------------------------------
0136: */
0137:
0138: static TclObject callMethod(Interp interp, // Current interpreter.
0139: TclObject reflectObj, // The object whose method to invoke.
0140: TclObject signature, // Method signature.
0141: TclObject argv[], // Arguments.
0142: int startIdx, // Index of the first argument in argv[] to
0143: // pass to the method.
0144: int count, // Number of arguments to pass to the
0145: // method.
0146: boolean convert) // Whether the value should be converted
0147: // into Tcl objects of the closest types.
0148: throws TclException {
0149: Object javaObj = ReflectObject.get(interp, reflectObj);
0150: Class javaCl = ReflectObject.getClass(interp, reflectObj);
0151: FuncSig sig = FuncSig.get(interp, javaCl, signature, argv,
0152: startIdx, count, false);
0153: Method method = (Method) sig.func;
0154: Class rtype = method.getReturnType();
0155:
0156: if (!PkgInvoker.isAccessible(rtype)) {
0157: throw new TclException(interp, "Return type \""
0158: + JavaInfoCmd.getNameFromClass(rtype)
0159: + "\" is not accessible");
0160: }
0161:
0162: Object result = call(interp, sig.pkgInvoker, signature, method,
0163: javaObj, argv, startIdx, count);
0164:
0165: if (rtype == Void.TYPE) {
0166: return null;
0167: } else {
0168: return wrap(interp, rtype, result, convert);
0169: }
0170: }
0171:
0172: /*
0173: *-----------------------------------------------------------------------------
0174: *
0175: * callStaticMethod --
0176: *
0177: * Call the specified static method of the given object.
0178: *
0179: * Results:
0180: * When successful, this method returns the Java object that the
0181: * Java method would have returned. If the Java method has a void
0182: * return type then null is returned.
0183: *
0184: * Side effects:
0185: * The method can cause arbitrary side effects.
0186: *
0187: *-----------------------------------------------------------------------------
0188: */
0189:
0190: static TclObject callStaticMethod(Interp interp, // Current interpreter.
0191: TclObject classObj, // Class whose static method to invoke.
0192: TclObject signature, // Method signature.
0193: TclObject argv[], // Arguments.
0194: int startIdx, // Index of the first argument in argv[] to
0195: // pass to the method.
0196: int count, // Number of arguments to pass to the
0197: // method.
0198: boolean convert) // Whether the value should be converted
0199: // into Tcl objects of the closest types.
0200: throws TclException {
0201: Class cls = ClassRep.get(interp, classObj);
0202: FuncSig sig = FuncSig.get(interp, cls, signature, argv,
0203: startIdx, count, true);
0204:
0205: Method method = (Method) sig.func;
0206: Class rtype = method.getReturnType();
0207:
0208: if (!PkgInvoker.isAccessible(rtype)) {
0209: throw new TclException(interp, "Return type \""
0210: + JavaInfoCmd.getNameFromClass(rtype)
0211: + "\" is not accessible");
0212: }
0213:
0214: Object result = call(interp, sig.pkgInvoker, signature, method,
0215: null, argv, startIdx, count);
0216:
0217: if (rtype == Void.TYPE) {
0218: return null;
0219: } else {
0220: return wrap(interp, method.getReturnType(), result, convert);
0221: }
0222: }
0223:
0224: /*
0225: *-----------------------------------------------------------------------------
0226: *
0227: * call --
0228: *
0229: * Call the constructor, instance method, or static method with
0230: * the given parameters. Check the parameter types and perform
0231: * TclObject to JavaObject conversion.
0232: *
0233: * Results:
0234: * The object created by the constructor, or the return value
0235: * of the method call.
0236: *
0237: * Side effects:
0238: * The constructor/method call may have arbitrary side effects.
0239: *
0240: *-----------------------------------------------------------------------------
0241: */
0242:
0243: static Object call(Interp interp, PkgInvoker invoker, // The PkgInvoked used to invoke the
0244: // method or constructor.
0245: TclObject signature, // For formatting error message.
0246: Object func, // The Constructor or Method to call.
0247: Object obj, // The object associated with an instace
0248: // method call. Should be null for
0249: // constructor calls and static method
0250: // calls.
0251: TclObject argv[], // Argument list.
0252: int startIdx, // Index of the first argument in argv[] to
0253: // pass to the method or constructor.
0254: int count) // Number of arguments to pass to the
0255: // method or constructor.
0256: throws TclException // Standard Tcl exception.
0257: {
0258: Class paramTypes[];
0259: Constructor cons = null;
0260: Method method = null;
0261: int i;
0262: boolean isConstructor = (func instanceof Constructor);
0263:
0264: if (isConstructor) {
0265: cons = (Constructor) func;
0266: paramTypes = cons.getParameterTypes();
0267: } else {
0268: method = (Method) func;
0269: paramTypes = method.getParameterTypes();
0270: }
0271:
0272: if (count != paramTypes.length) {
0273: throw new TclException(interp, "wrong # args for calling "
0274: + (isConstructor ? "constructor" : "method")
0275: + " \"" + signature + "\"");
0276: }
0277:
0278: Object args[];
0279:
0280: if (count == 0) {
0281: args = EMPTY_ARGS;
0282: } else {
0283: args = new Object[count];
0284: for (i = 0; i < count; i++) {
0285: args[i] = convertTclObject(interp, paramTypes[i],
0286: argv[i + startIdx]);
0287: }
0288: }
0289:
0290: try {
0291: final boolean debug = false;
0292: Object result;
0293:
0294: if (isConstructor) {
0295: result = invoker.invokeConstructor(cons, args);
0296: } else {
0297: result = invoker.invokeMethod(method, obj, args);
0298: }
0299:
0300: if (debug) {
0301: System.out
0302: .println("result object from invocation is \""
0303: + result + "\"");
0304: }
0305:
0306: return result;
0307: } catch (InstantiationException e) {
0308: throw new TclRuntimeError("unexpected abstract class: "
0309: + e.getMessage());
0310: } catch (IllegalAccessException e) {
0311: throw new TclRuntimeError(
0312: "unexpected inaccessible ctor or method: "
0313: + e.getMessage());
0314: } catch (IllegalArgumentException e) {
0315: throw new TclRuntimeError(
0316: "unexpected IllegalArgumentException: "
0317: + e.getMessage());
0318: } catch (InvocationTargetException e) {
0319: Throwable te = e.getTargetException();
0320: if (te instanceof TclException) {
0321: interp.setResult(te.getMessage());
0322: throw (TclException) te;
0323: } else {
0324: throw new ReflectException(interp, te);
0325: }
0326: }
0327: }
0328:
0329: /*
0330: *-----------------------------------------------------------------------------
0331: *
0332: * getField --
0333: *
0334: * Returns the value of a field in the given object.
0335: *
0336: * Results:
0337: * When successful, returns an array: Object result[2]. result[0]
0338: * is the value of the field; result[1] is the type of the field.
0339: *
0340: * Side effects:
0341: * None.
0342: *
0343: *-----------------------------------------------------------------------------
0344: */
0345:
0346: static final TclObject getField(Interp interp, // Current interpreter.
0347: TclObject classOrObj, // Class or object whose field to get.
0348: TclObject signature, // Signature of the field.
0349: boolean convert) // Whether the value should be converted
0350: // into Tcl objects of the closest types.
0351: throws TclException // Standard Tcl exception.
0352: {
0353: return getsetField(interp, classOrObj, signature, null,
0354: convert, true);
0355: }
0356:
0357: /*
0358: *-----------------------------------------------------------------------------
0359: *
0360: * setField --
0361: *
0362: * Sets the value of a field in the given object.
0363: *
0364: * Results:
0365: * None.
0366: *
0367: * Side effects:
0368: * When successful, the field is set to the given value.
0369: *
0370: *-----------------------------------------------------------------------------
0371: */
0372:
0373: static final void setField(Interp interp, // Current interpreter.
0374: TclObject classOrObj, // Class or object whose field to get.
0375: TclObject signature, // Signature of the field.
0376: TclObject value) // New value for the field.
0377: throws TclException // Standard Tcl exception.
0378: {
0379: getsetField(interp, classOrObj, signature, value, false, false);
0380: }
0381:
0382: /*
0383: *-----------------------------------------------------------------------------
0384: *
0385: * getsetField --
0386: *
0387: * Gets or sets the field in the given object.
0388: *
0389: * Results:
0390: * None.
0391: *
0392: * Side effects:
0393: * When successful, the field is set to the given value if isget
0394: * is false.
0395: *
0396: *-----------------------------------------------------------------------------
0397: */
0398:
0399: static TclObject getsetField(Interp interp, // Current interpreter.
0400: TclObject classOrObj, // Class or object whose field to get.
0401: TclObject signature, // Signature of the field.
0402: TclObject value, // New value for the field.
0403: boolean convert, // Whether the value should be converted
0404: // into Tcl objects of the closest types.
0405: boolean isget) throws TclException // Standard Tcl exception.
0406: {
0407: Class cls = null;
0408: Object obj = null;
0409: boolean isStatic = false;
0410:
0411: try {
0412: obj = ReflectObject.get(interp, classOrObj);
0413: } catch (TclException e) {
0414: try {
0415: cls = ClassRep.get(interp, classOrObj);
0416: } catch (TclException e1) {
0417: throw new TclException(interp,
0418: "unknown class or object \"" + classOrObj
0419: + "\"");
0420: }
0421: isStatic = true;
0422:
0423: if (!PkgInvoker.isAccessible(cls)) {
0424: JavaInvoke.notAccessibleError(interp, cls);
0425: }
0426: }
0427:
0428: if (!isStatic) {
0429: if (obj == null) {
0430: throw new TclException(interp,
0431: "can't access fields in a null object reference");
0432: }
0433: cls = ReflectObject.getClass(interp, classOrObj);
0434: }
0435:
0436: // Check for the special case where the field is named "class"
0437: // which has a special meaning and is enforced by the javac compiler.
0438: // If found, return the java.lang.Class object for the named class.
0439:
0440: if (isStatic && isget && signature.toString().equals("class")) {
0441: return wrap(interp, Class.class, cls, false);
0442: }
0443:
0444: FieldSig sig = FieldSig.get(interp, signature, cls);
0445: Field field = sig.field;
0446: if (isStatic && (!(Modifier.isStatic(field.getModifiers())))) {
0447: throw new TclException(interp,
0448: "can't access an instance field without an object");
0449: }
0450: Class ftype = field.getType();
0451:
0452: if (!PkgInvoker.isAccessible(field.getType())) {
0453: throw new TclException(interp, "Field type \""
0454: + JavaInfoCmd.getNameFromClass(ftype)
0455: + "\" is not accessible");
0456: }
0457:
0458: if (!isget && Modifier.isFinal(field.getModifiers())) {
0459: throw new TclException(interp, "can't set final field \""
0460: + signature + "\"");
0461: }
0462:
0463: try {
0464: if (isget) {
0465: return wrap(interp, ftype, sig.pkgInvoker.getField(
0466: field, obj), convert);
0467: } else {
0468: Object javaValue = convertTclObject(interp, ftype,
0469: value);
0470: sig.pkgInvoker.setField(field, obj, javaValue);
0471: return null;
0472: }
0473: } catch (IllegalArgumentException e) {
0474: throw new TclRuntimeError(
0475: "unexpected IllegalArgumentException: "
0476: + e.getMessage());
0477: } catch (IllegalAccessException e) {
0478: throw new TclRuntimeError(
0479: "unexpected IllegalAccessException: "
0480: + e.getMessage());
0481: }
0482: }
0483:
0484: /*
0485: *-----------------------------------------------------------------------------
0486: *
0487: * getProperty --
0488: *
0489: * Returns the value of a property in the given object.
0490: *
0491: * Results:
0492: * When successful, returns a the value of the property inside
0493: * a TclObject
0494: *
0495: * Side effects:
0496: * None.
0497: *
0498: *-----------------------------------------------------------------------------
0499: */
0500:
0501: static TclObject getProperty(Interp interp, // Current interpreter.
0502: TclObject reflectObj, // The object whose property to query.
0503: TclObject propName, // The name of the property to query.
0504: boolean convert) // Whether the value should be converted
0505: // into Tcl objects of the closest types.
0506: throws TclException // A standard Tcl exception.
0507: {
0508: Object javaObj = ReflectObject.get(interp, reflectObj);
0509: if (javaObj == null) {
0510: throw new TclException(interp,
0511: "can't get property from null object");
0512: }
0513:
0514: Class javaClass = ReflectObject.getClass(interp, reflectObj);
0515: PropertySig sig = PropertySig.get(interp, javaClass, propName);
0516:
0517: Method readMethod = sig.desc.getReadMethod();
0518:
0519: if (readMethod == null) {
0520: throw new TclException(interp,
0521: "can't get write-only property \"" + propName
0522: + "\"");
0523: }
0524:
0525: try {
0526: return wrap(interp, readMethod.getReturnType(),
0527: sig.pkgInvoker.invokeMethod(readMethod, javaObj,
0528: EMPTY_ARGS), convert);
0529: } catch (IllegalAccessException e) {
0530: throw new TclRuntimeError(
0531: "unexpected inaccessible readMethod: "
0532: + e.getMessage());
0533: } catch (IllegalArgumentException e) {
0534: throw new TclRuntimeError(
0535: "unexpected IllegalArgumentException: "
0536: + e.getMessage());
0537: } catch (InvocationTargetException e) {
0538: throw new ReflectException(interp, e);
0539: }
0540:
0541: }
0542:
0543: /*
0544: *-----------------------------------------------------------------------------
0545: *
0546: * setProperty --
0547: *
0548: * Returns the value of a property in the given object.
0549: *
0550: * Results:
0551: * None.
0552: *
0553: * Side effects:
0554: * When successful, the property will have the new value.
0555: *
0556: *-----------------------------------------------------------------------------
0557: */
0558:
0559: static void setProperty(Interp interp, // Current interpreter.
0560: TclObject reflectObj, // The object whose property to query.
0561: TclObject propName, // The name of the property to query.
0562: TclObject value) // Whether the value should be converted
0563: // into Tcl objects of the closest types.
0564: throws TclException // A standard Tcl exception.
0565: {
0566: Object javaObj = ReflectObject.get(interp, reflectObj);
0567: if (javaObj == null) {
0568: throw new TclException(interp,
0569: "can't set property in null object");
0570: }
0571:
0572: Class javaClass = ReflectObject.getClass(interp, reflectObj);
0573: PropertySig sig = PropertySig.get(interp, javaClass, propName);
0574:
0575: Method writeMethod = sig.desc.getWriteMethod();
0576: Class type = sig.desc.getPropertyType();
0577:
0578: if (writeMethod == null) {
0579: throw new TclException(interp,
0580: "can't set read-only property \"" + propName + "\"");
0581: }
0582:
0583: Object args[] = new Object[1];
0584: args[0] = convertTclObject(interp, type, value);
0585:
0586: try {
0587: sig.pkgInvoker.invokeMethod(writeMethod, javaObj, args);
0588: } catch (IllegalAccessException e) {
0589: throw new TclRuntimeError(
0590: "unexpected inaccessible writeMethod: "
0591: + e.getMessage());
0592: } catch (IllegalArgumentException e) {
0593: throw new TclRuntimeError(
0594: "unexpected IllegalArgumentException: "
0595: + e.getMessage());
0596: } catch (InvocationTargetException e) {
0597: throw new ReflectException(interp, e);
0598: }
0599: }
0600:
0601: /*
0602: *-----------------------------------------------------------------------------
0603: *
0604: * getClassByName --
0605: *
0606: * Returns Class object identified by the string name. We allow
0607: * abbreviation of the java.lang.* class if there is no ambiguity:
0608: * e.g., if there is no class whose fully qualified name is "String",
0609: * then "String" means java.lang.String. Inner classes are supported
0610: * both with fully qualified names and imported class names.
0611: *
0612: * Results:
0613: * If successful, The Class object identified by the string name.
0614: *
0615: * Side effects:
0616: * None.
0617: *
0618: *-----------------------------------------------------------------------------
0619: */
0620:
0621: static Class getClassByName(Interp interp, // Interp used by TclClassLoader
0622: String clsName) // String name of the class.
0623: throws TclException // If the class cannot be found or loaded.
0624:
0625: {
0626: Class result = null;
0627: int dimension;
0628:
0629: final boolean debug = false;
0630: if (debug) {
0631: System.out.println("JavaInvoke.getClassByName(\"" + clsName
0632: + "\")");
0633: }
0634:
0635: // If the string is of the form className[][]..., strip out the trailing
0636: // []s and record the dimension of the array.
0637:
0638: StringBuffer prefix_buf = new StringBuffer(64);
0639: StringBuffer suffix_buf = new StringBuffer(64);
0640: StringBuffer clsName_buf = new StringBuffer(clsName);
0641:
0642: String lname;
0643:
0644: int clsName_len;
0645: for (dimension = 0; true; dimension++) {
0646: clsName_len = clsName_buf.length();
0647:
0648: if ((clsName_len > 2)
0649: && (clsName_buf.charAt(clsName_len - 2) == '[')
0650: && (clsName_buf.charAt(clsName_len - 1) == ']')) {
0651:
0652: clsName_buf.setLength(clsName_len - 2);
0653: prefix_buf.append('[');
0654: } else {
0655: break;
0656: }
0657: }
0658:
0659: boolean package_name_exception = false;
0660:
0661: if (true) {
0662: clsName = clsName_buf.toString(); // Use shortened form of name
0663:
0664: // Search for the char '.' in the name. If '.' is in
0665: // the name then we know it is not a builtin type.
0666:
0667: if (clsName.indexOf('.') == -1) {
0668: if (dimension > 0) {
0669: boolean isPrimitive = true;
0670:
0671: if (clsName.equals("int")) {
0672: prefix_buf.append('I');
0673: } else if (clsName.equals("boolean")) {
0674: prefix_buf.append('Z');
0675: } else if (clsName.equals("long")) {
0676: prefix_buf.append('J');
0677: } else if (clsName.equals("float")) {
0678: prefix_buf.append('F');
0679: } else if (clsName.equals("double")) {
0680: prefix_buf.append('D');
0681: } else if (clsName.equals("byte")) {
0682: prefix_buf.append('B');
0683: } else if (clsName.equals("short")) {
0684: prefix_buf.append('S');
0685: } else if (clsName.equals("char")) {
0686: prefix_buf.append('C');
0687: } else {
0688: isPrimitive = false;
0689: }
0690:
0691: if (isPrimitive) {
0692: try {
0693: return Class.forName(prefix_buf.toString());
0694: } catch (ClassNotFoundException e) {
0695: throw new TclRuntimeError(
0696: "unexpected ClassNotFoundException: "
0697: + e.getMessage());
0698: }
0699: }
0700:
0701: // Otherwise, not a primitive array type
0702:
0703: prefix_buf.append('L');
0704: suffix_buf.append(';');
0705: } else {
0706: if (clsName.equals("int")) {
0707: return Integer.TYPE;
0708: } else if (clsName.equals("boolean")) {
0709: return Boolean.TYPE;
0710: } else if (clsName.equals("long")) {
0711: return Long.TYPE;
0712: } else if (clsName.equals("float")) {
0713: return Float.TYPE;
0714: } else if (clsName.equals("double")) {
0715: return Double.TYPE;
0716: } else if (clsName.equals("byte")) {
0717: return Byte.TYPE;
0718: } else if (clsName.equals("short")) {
0719: return Short.TYPE;
0720: } else if (clsName.equals("char")) {
0721: return Character.TYPE;
0722: }
0723: }
0724:
0725: // Use TclClassLoader defined on a per-interp basis.
0726: TclClassLoader tclClassLoader = (TclClassLoader) interp
0727: .getClassLoader();
0728:
0729: try {
0730: lname = prefix_buf + clsName + suffix_buf;
0731:
0732: if (debug) {
0733: System.out.println("attempting load of \""
0734: + lname + "\"");
0735: }
0736:
0737: result = tclClassLoader.loadClass(lname);
0738: } catch (ClassNotFoundException e) {
0739: result = null;
0740: } catch (PackageNameException e) {
0741: // Should not be possible to catch a PackageNameException
0742: // here since the class name above should contain no '.' chars.
0743: throw new TclRuntimeError(
0744: "unexpected PackageNameException :"
0745: + e.getMessage());
0746: }
0747:
0748: if (result == null) {
0749: // If the class loader can not find the class then check with
0750: // the "import" feature to see if the given clsName maps to
0751: // a fully qualified class name.
0752:
0753: boolean inJavaLang = false;
0754: String fullyqualified = JavaImportCmd.getImport(
0755: interp, clsName);
0756:
0757: // If we do not find a fully qualified name in the import table
0758: // then try to fully qualify the class with the java.lang prefix
0759:
0760: if (fullyqualified == null) {
0761: inJavaLang = true;
0762: fullyqualified = "java.lang." + clsName;
0763: }
0764:
0765: // If the class starts with "java." and it can't be
0766: // loaded with the system class loader, then a
0767: // PackageNameException is raised.
0768:
0769: try {
0770: lname = prefix_buf + fullyqualified
0771: + suffix_buf;
0772:
0773: if (debug) {
0774: System.out.println("attempting load of \""
0775: + lname + "\"");
0776: }
0777:
0778: result = tclClassLoader.loadClass(lname);
0779: } catch (ClassNotFoundException e) {
0780: result = null;
0781: } catch (PackageNameException e) {
0782: // If loading a class from java.lang package fails
0783: // and we fully qualified the class name with the
0784: // java.lang prefix, then don't emit a special
0785: // error message related to the package name.
0786:
0787: if (inJavaLang) {
0788: // No-op
0789: } else {
0790: package_name_exception = true;
0791: }
0792: result = null;
0793: }
0794:
0795: if (debug) {
0796: if (result == null) {
0797: System.out.println("load failed");
0798: } else {
0799: System.out.println("load worked");
0800: }
0801: }
0802: }
0803: } else {
0804: // clsName contains a '.' character. It is either a fully
0805: // qualified toplevel class name or an inner class name.
0806: // Note that use of a '$' to indicate an inner class is
0807: // supported only for backwards compatibility and
0808: // works only with a fully qualified class name.
0809:
0810: TclClassLoader tclClassLoader = (TclClassLoader) interp
0811: .getClassLoader();
0812:
0813: if (dimension > 0) {
0814: prefix_buf.append("L");
0815: suffix_buf.append(";");
0816: }
0817:
0818: try {
0819: lname = prefix_buf + clsName + suffix_buf;
0820:
0821: if (debug) {
0822: System.out.println("attempting load of \""
0823: + lname + "\"");
0824: }
0825:
0826: result = tclClassLoader.loadClass(lname);
0827: } catch (ClassNotFoundException e) {
0828: result = null;
0829: } catch (PackageNameException e) {
0830: package_name_exception = true;
0831: result = null;
0832: }
0833:
0834: if (debug) {
0835: if (result == null) {
0836: System.out.println("load failed");
0837: } else {
0838: System.out.println("load worked");
0839: }
0840: }
0841:
0842: if ((result == null) && (clsName.indexOf('$') == -1)) {
0843: // Toplevel class with fully qualified name not found.
0844: // Search for an inner class with this name. This
0845: // search is tricky because inner classes can be
0846: // nested inside other inner classes. Find a containing
0847: // class that exists, then search for an inner class
0848: // relative to the containing class. Old style inner class
0849: // names that contain a literal '$' character are not searched.
0850:
0851: ArrayList parts = new ArrayList(5);
0852: int si = 0;
0853: int clsNameLength = clsName.length();
0854: for (int i = 0; i <= clsNameLength; i++) {
0855: if ((i == clsNameLength)
0856: || (clsName.charAt(i) == '.')) {
0857: parts.add(clsName.substring(si, i));
0858: si = i + 1;
0859: }
0860: }
0861: if (debug) {
0862: System.out.println("clsName parts is " + parts);
0863: }
0864:
0865: // Search for a contanining class, construct inner
0866: // class name if a contanining class was found.
0867:
0868: String toplevel = null;
0869: String inner = null;
0870: boolean load_inner = false;
0871:
0872: for (int i = parts.size() - 1; i > 0; i--) {
0873: StringBuffer sb;
0874:
0875: sb = new StringBuffer(64);
0876: for (int bi = 0; bi < i; bi++) {
0877: sb.append(parts.get(bi));
0878: sb.append('.');
0879: }
0880: if ((sb.length() > 0)
0881: && (sb.charAt(sb.length() - 1) == '.')) {
0882: sb.setLength(sb.length() - 1);
0883: }
0884: toplevel = sb.toString();
0885:
0886: sb = new StringBuffer(64);
0887: for (int ai = i; ai < parts.size(); ai++) {
0888: sb.append(parts.get(ai));
0889: sb.append('$');
0890: }
0891: if ((sb.length() > 0)
0892: && (sb.charAt(sb.length() - 1) == '$')) {
0893: sb.setLength(sb.length() - 1);
0894: }
0895: inner = sb.toString();
0896:
0897: if (debug) {
0898: System.out.println("loop " + i + ":");
0899: System.out.println("toplevel is "
0900: + toplevel);
0901: System.out.println("inner is " + inner);
0902: }
0903:
0904: try {
0905: lname = prefix_buf + toplevel + suffix_buf;
0906:
0907: if (debug) {
0908: System.out
0909: .println("attempting load of \""
0910: + lname + "\"");
0911: }
0912:
0913: result = tclClassLoader.loadClass(lname);
0914: } catch (ClassNotFoundException e) {
0915: // Not an enclosing toplevel class, raise TclException
0916: result = null;
0917: } catch (PackageNameException e) {
0918: package_name_exception = true;
0919: result = null;
0920: }
0921:
0922: if (debug) {
0923: if (result == null) {
0924: System.out.println("load failed");
0925: } else {
0926: System.out.println("load worked");
0927: }
0928: }
0929:
0930: if (result != null) {
0931: // Containing class was loaded, break out of
0932: // this loop and load the inner class by name.
0933:
0934: load_inner = true;
0935: break;
0936: } else if ((toplevel.indexOf('.') == -1)) {
0937: // The toplevel class was not loaded, it could
0938: // be an imported class name. Check the import
0939: // table for this class name. Don't bother
0940: // loading an imported name since the class
0941: // had to exist to be imported in the first place.
0942:
0943: if (debug) {
0944: System.out
0945: .println("checking import table for \""
0946: + toplevel + "\"");
0947: }
0948: String fullyqualified = JavaImportCmd
0949: .getImport(interp, toplevel);
0950: if (debug) {
0951: if (fullyqualified == null) {
0952: System.out
0953: .println("was not imported");
0954: } else {
0955: System.out
0956: .println("was imported as \""
0957: + fullyqualified
0958: + "\"");
0959: }
0960: }
0961:
0962: if (fullyqualified != null) {
0963: load_inner = true;
0964: toplevel = fullyqualified;
0965: break;
0966: } else {
0967: // Not an imported toplevel class. Check to
0968: // see if the class is in the java.lang package.
0969:
0970: fullyqualified = "java.lang."
0971: + toplevel;
0972:
0973: try {
0974: lname = prefix_buf + fullyqualified
0975: + suffix_buf;
0976:
0977: if (debug) {
0978: System.out
0979: .println("attempting load of \""
0980: + lname + "\"");
0981: }
0982:
0983: result = tclClassLoader
0984: .loadClass(lname);
0985: } catch (ClassNotFoundException e) {
0986: result = null;
0987: } catch (PackageNameException e) {
0988: result = null;
0989: }
0990:
0991: if (debug) {
0992: if (result == null) {
0993: System.out
0994: .println("load failed");
0995: } else {
0996: System.out
0997: .println("load worked");
0998: }
0999: }
1000:
1001: if (result != null) {
1002: load_inner = true;
1003: toplevel = fullyqualified;
1004: break;
1005: }
1006: }
1007: }
1008: }
1009:
1010: if (load_inner) {
1011: // If enclosing class exists, attempt to load inner class.
1012:
1013: try {
1014: lname = prefix_buf + toplevel + "$" + inner
1015: + suffix_buf;
1016:
1017: if (debug) {
1018: System.out
1019: .println("attempting load of \""
1020: + lname + "\"");
1021: }
1022:
1023: result = tclClassLoader.loadClass(lname);
1024: } catch (ClassNotFoundException e) {
1025: // Not an inner class, raise TclException
1026: result = null;
1027: } catch (PackageNameException e) {
1028: package_name_exception = true;
1029: result = null;
1030: }
1031:
1032: if (debug) {
1033: if (result == null) {
1034: System.out.println("load failed");
1035: } else {
1036: System.out.println("load worked");
1037: }
1038: }
1039: } // end if (load_inner)
1040: }
1041: }
1042: } // end if (true) block
1043:
1044: if ((result == null) && package_name_exception) {
1045: if (debug) {
1046: System.out
1047: .println("throwing TclException because of PackageNameException");
1048: }
1049:
1050: throw new TclException(interp,
1051: "cannot load new class into java or tcl package");
1052: }
1053:
1054: if (result == null) {
1055: if (debug) {
1056: System.out
1057: .println("throwing unknown class TclException");
1058: }
1059:
1060: throw new TclException(interp, "unknown class \""
1061: + clsName_buf + "\"");
1062: }
1063:
1064: return result;
1065: }
1066:
1067: /*
1068: *----------------------------------------------------------------------
1069: *
1070: * convertJavaObject --
1071: *
1072: * Converts the java.lang.Object into a Tcl object and return
1073: * TclObject that holds the reult. Primitive data types
1074: * are converted into primitive Tcl data types. Otherwise,
1075: * a ReflectObject wrapper is created for the object so that it
1076: * can be later accessed with the Reflection API.
1077: *
1078: * Results:
1079: * The TclObject representation of the Java object.
1080: *
1081: * Side effects:
1082: * None.
1083: *
1084: *----------------------------------------------------------------------
1085: */
1086:
1087: static TclObject convertJavaObject(Interp interp, // Current interpreter.
1088: Class cls, // The class of the Java Object
1089: Object javaObj) // The java.lang.Object to convert to a TclObject.
1090: throws TclException {
1091: if (javaObj == null) {
1092: if (cls == String.class) {
1093: return TclString.newInstance("");
1094: } else {
1095: return ReflectObject.newInstance(interp, cls, javaObj);
1096: }
1097:
1098: } else if ((cls == Integer.TYPE) || (cls == Integer.class)) {
1099: return TclInteger.newInstance(((Integer) javaObj)
1100: .intValue());
1101:
1102: } else if ((cls == Long.TYPE) || (cls == Long.class)) {
1103: // A long can not be represented as a TclInteger
1104: return TclString.newInstance(javaObj.toString());
1105:
1106: } else if ((cls == Short.TYPE) || (cls == Short.class)) {
1107: return TclInteger.newInstance(((Short) javaObj).intValue());
1108:
1109: } else if ((cls == Byte.TYPE) || (cls == Byte.class)) {
1110: return TclInteger.newInstance(((Byte) javaObj).intValue());
1111:
1112: } else if ((cls == Double.TYPE) || (cls == Double.class)) {
1113: return TclDouble.newInstance(((Double) javaObj)
1114: .doubleValue());
1115:
1116: } else if ((cls == Float.TYPE) || (cls == Float.class)) {
1117: return TclDouble.newInstance(((Float) javaObj)
1118: .doubleValue());
1119:
1120: } else if ((cls == Boolean.TYPE) || (cls == Boolean.class)) {
1121: return TclBoolean.newInstance(((Boolean) javaObj)
1122: .booleanValue());
1123:
1124: } else if ((cls == Character.TYPE) || (cls == Character.class)) {
1125: return TclString.newInstance(((Character) javaObj)
1126: .toString());
1127:
1128: } else if (cls == String.class) {
1129: return TclString.newInstance((String) javaObj);
1130:
1131: } else {
1132: return ReflectObject.newInstance(interp, cls, javaObj);
1133: }
1134: }
1135:
1136: /*
1137: *-----------------------------------------------------------------------------
1138: *
1139: * convertTclObject --
1140: *
1141: * Converts a Tcl object to a Java Object of the required type.
1142: *
1143: * Results:
1144: * An Object of the required type.
1145: *
1146: * Side effects:
1147: * None.
1148: *
1149: *-----------------------------------------------------------------------------
1150: */
1151:
1152: static final Object convertTclObject(Interp interp, // Current interpreter.
1153: Class type, // Convert to this type.
1154: TclObject tclObj) // From this Tcl object.
1155: throws TclException // If conversion fails.
1156: {
1157: Object javaObj = null;
1158: Class javaClass = null;
1159: boolean isReflectObj = false;
1160:
1161: try {
1162: javaObj = ReflectObject.get(interp, tclObj);
1163: javaClass = ReflectObject.getClass(interp, tclObj);
1164: isReflectObj = true;
1165: } catch (TclException e) {
1166: interp.resetResult();
1167: }
1168:
1169: if (!isReflectObj) {
1170: // tclObj a Tcl "primitive" value. We try convert it to the
1171: // corresponding primitive value in Java.
1172: //
1173: // To optimize performance, the following "if" statements are
1174: // arranged according to (my guesstimation of) the frequency
1175: // that a certain type is used.
1176:
1177: if (type == String.class) {
1178: return tclObj.toString();
1179:
1180: } else if (type == Object.class) {
1181: return tclObj.toString();
1182:
1183: } else if ((type == Integer.TYPE)
1184: || (type == Integer.class)) {
1185: // If an object is already a TclInteger type, then pass
1186: // the existing value directly. Otherwise, parse the
1187: // number as a Java int and see if it can be represented
1188: // as a Java int. This logic will raise an exception
1189: // when a number can't be represented as a 32bit signed int.
1190: // Tcl's weird number parsing rules will wrap the integer
1191: // around and calling code can't detect an overflow.
1192:
1193: int jint = parseJavaInt(interp, tclObj);
1194: return new Integer(jint);
1195:
1196: } else if ((type == Boolean.TYPE)
1197: || (type == Boolean.class)) {
1198: return new Boolean(TclBoolean.get(interp, tclObj));
1199:
1200: } else if ((type == Long.TYPE) || (type == Long.class)) {
1201: // If an object is already a TclInteger type, then pass
1202: // the existing value directly. Otherwise, parse the
1203: // number as a Java long. Raise a TclException if the
1204: // number is not an integer or is outside the long bounds.
1205:
1206: long jlong = parseJavaLong(interp, tclObj);
1207: return new Long(jlong);
1208:
1209: } else if ((type == Float.TYPE) || (type == Float.class)) {
1210: // Tcl stores floating point numbers as doubles,
1211: // so we just need to check to see if the value
1212: // is outside the float bounds. Invoking a Java
1213: // method should not automatically lose precision.
1214:
1215: double jdouble = TclDouble.get(interp, tclObj);
1216: float jfloat = (float) jdouble;
1217:
1218: if ((jdouble == Double.NaN)
1219: || (jdouble == Double.NEGATIVE_INFINITY)
1220: || (jdouble == Double.POSITIVE_INFINITY)) {
1221: // No-op
1222: } else if ((jdouble != 0.0)
1223: && ((Math.abs(jdouble) > (double) Float.MAX_VALUE) || (Math
1224: .abs(jdouble) < (double) Float.MIN_VALUE))) {
1225: throw new TclException(interp,
1226: "double value too large to represent in a float");
1227: }
1228: return new Float(jfloat);
1229:
1230: } else if ((type == Double.TYPE) || (type == Double.class)) {
1231: return new Double(TclDouble.get(interp, tclObj));
1232:
1233: } else if ((type == Byte.TYPE) || (type == Byte.class)) {
1234: // Parse a Java int, then check valid byte range.
1235:
1236: int jint = parseJavaInt(interp, tclObj);
1237: if ((jint < Byte.MIN_VALUE) || (jint > Byte.MAX_VALUE)) {
1238: throw new TclException(interp,
1239: "integer value too large to represent in a byte");
1240: }
1241: return new Byte((byte) jint);
1242:
1243: } else if ((type == Short.TYPE) || (type == Short.class)) {
1244: // Parse a Java int, then check valid byte range.
1245:
1246: int jint = parseJavaInt(interp, tclObj);
1247: if ((jint < Short.MIN_VALUE)
1248: || (jint > Short.MAX_VALUE)) {
1249: throw new TclException(interp,
1250: "integer value too large to represent in a short");
1251: }
1252: return new Short((short) jint);
1253:
1254: } else if ((type == Character.TYPE)
1255: || (type == Character.class)) {
1256: String str = tclObj.toString();
1257: if (str.length() != 1) {
1258: throw new TclException(interp,
1259: "expected character but got \"" + tclObj
1260: + "\"");
1261: }
1262: return new Character(str.charAt(0));
1263:
1264: } else if (type == TclObject.class) {
1265: // Pass a non ReflectObject TclObject directly to a Java method.
1266: return tclObj;
1267: } else {
1268: throw new TclException(interp, "\"" + tclObj
1269: + "\" is not an object handle of class \""
1270: + JavaInfoCmd.getNameFromClass(type) + "\"");
1271: }
1272: } else {
1273: // The TclObject is a ReflectObject that contains javaObj. We
1274: // check to see if javaObj can be converted to the required
1275: // type. If javaObj is a wrapper for a primitive type then
1276: // we check to see if the object is an instanceof the type.
1277:
1278: if (isAssignable(type, javaClass)) {
1279: return javaObj;
1280: }
1281:
1282: if (type.isPrimitive()) {
1283: if (type == Boolean.TYPE) {
1284: if (javaObj instanceof Boolean) {
1285: return javaObj;
1286: }
1287: } else if (type == Character.TYPE) {
1288: if (javaObj instanceof Character) {
1289: return javaObj;
1290: }
1291: } else if (type == Byte.TYPE) {
1292: if (javaObj instanceof Byte) {
1293: return javaObj;
1294: }
1295: } else if (type == Short.TYPE) {
1296: if (javaObj instanceof Short) {
1297: return javaObj;
1298: }
1299: } else if (type == Integer.TYPE) {
1300: if (javaObj instanceof Integer) {
1301: return javaObj;
1302: }
1303: } else if (type == Long.TYPE) {
1304: if (javaObj instanceof Long) {
1305: return javaObj;
1306: }
1307: } else if (type == Float.TYPE) {
1308: if (javaObj instanceof Float) {
1309: return javaObj;
1310: }
1311: } else if (type == Double.TYPE) {
1312: if (javaObj instanceof Double) {
1313: return javaObj;
1314: }
1315: } else if (type == Void.TYPE) {
1316: // void is not a valid type for conversions
1317: }
1318: }
1319:
1320: // Pass TclObject that contains the ReflectObject directly.
1321: if (type == TclObject.class) {
1322: return tclObj;
1323: }
1324:
1325: throw new TclException(interp, "expected object of type "
1326: + JavaInfoCmd.getNameFromClass(type)
1327: + " but got \""
1328: + tclObj
1329: + "\" ("
1330: + ((javaClass == null) ? "null" : JavaInfoCmd
1331: .getNameFromClass(javaClass)) + ")");
1332: }
1333: }
1334:
1335: /*
1336: *-----------------------------------------------------------------------------
1337: *
1338: * wrap --
1339: *
1340: * Wraps a Java Object into a TclObject according to whether the
1341: * convert flag is set.
1342: *
1343: * Results:
1344: * The TclObject that wraps the Java Object.
1345: *
1346: * Side effects:
1347: * None.
1348: *
1349: *-----------------------------------------------------------------------------
1350: */
1351:
1352: private static final TclObject wrap(Interp interp, // Current interpreter.
1353: Class cls, // The class of the Java Object
1354: Object javaObj, // The Java Object to wrap.
1355: boolean convert) // Whether the value should be converted
1356: // into Tcl objects of the closest types.
1357: throws TclException {
1358: if (convert) {
1359: return convertJavaObject(interp, cls, javaObj);
1360: } else {
1361: return ReflectObject.newInstance(interp, cls, javaObj);
1362: }
1363: }
1364:
1365: /*
1366: *-----------------------------------------------------------------------------
1367: *
1368: * isAssignable --
1369: *
1370: * Return true if the argument object can be assigned to
1371: * convert flag is set.
1372: *
1373: * Results:
1374: * The TclObject that wraps the Java Object.
1375: *
1376: * Side effects:
1377: * None.
1378: *
1379: *-----------------------------------------------------------------------------
1380: */
1381:
1382: static final boolean isAssignable(Class to_cls, // The class we want to assign to
1383: Class from_cls) // The class we want to assign from (can be null)
1384: {
1385: // A primitive type can not be assigned the null value, but it
1386: // can be assigned to any type derived from Object.
1387:
1388: if (from_cls == null) {
1389: if (to_cls.isPrimitive()) {
1390: return false;
1391: } else {
1392: return true;
1393: }
1394: } else {
1395: if ((to_cls == from_cls)
1396: || to_cls.isAssignableFrom(from_cls)) {
1397: return true;
1398: } else {
1399: return false;
1400: }
1401: }
1402: }
1403:
1404: /*
1405: *-----------------------------------------------------------------------------
1406: *
1407: * notAccessibleError --
1408: *
1409: * Raise a specific TclException when a class that is not accessible
1410: * is found.
1411: *
1412: * Results:
1413: * None.
1414: *
1415: * Side effects:
1416: * None.
1417: *
1418: *-----------------------------------------------------------------------------
1419: */
1420:
1421: static void notAccessibleError(Interp interp, Class cls)
1422: throws TclException {
1423: throw new TclException(interp, "Class \""
1424: + JavaInfoCmd.getNameFromClass(cls)
1425: + "\" is not accessible");
1426: }
1427:
1428: /*
1429: *-----------------------------------------------------------------------------
1430: *
1431: * isInnerClass --
1432: *
1433: * Return true is a class is either an inner class or an inner interface.
1434: * This is true only for classes defined inside other classes.
1435: *
1436: * Results:
1437: * None.
1438: *
1439: * Side effects:
1440: * None.
1441: *
1442: *-----------------------------------------------------------------------------
1443: */
1444:
1445: static boolean isInnerClass(Class cls) throws TclException {
1446: String cname = cls.getName();
1447: if (cname.indexOf('$') == -1) {
1448: return false;
1449: } else {
1450: return true;
1451: }
1452: }
1453:
1454: /*
1455: *-----------------------------------------------------------------------------
1456: *
1457: * parseJavaInt --
1458: *
1459: * Parse a Java int type from a TclObject. Unlike the rest of Tcl,
1460: * this method will raise an error when an integer value is not
1461: * in the range Integer.MIN_VALUE to Integer.MAX_VALUE. This is
1462: * the range of a 32bit signed number as defined by Java. Tcl parses
1463: * integers as 32bit unsigned numbers and wraps values outside the
1464: * valid range. This method will catch the case of an integer outside
1465: * of the valid range and raise a TclException so that a bogus value
1466: * is not passed to Java.
1467: *
1468: * Results:
1469: * Returns an int value or raises a TclException to indicate that
1470: * the number can't be parsed as a Java int.
1471: *
1472: * Side effects:
1473: * None.
1474: *
1475: *-----------------------------------------------------------------------------
1476: */
1477:
1478: static int parseJavaInt(Interp interp, TclObject obj)
1479: throws TclException {
1480: // No point in reparsing a "pure" integer.
1481:
1482: if (obj.hasNoStringRep() && obj.isIntType()) {
1483: return TclInteger.get(interp, obj);
1484: }
1485:
1486: String srep = obj.toString();
1487: String s = srep;
1488: int len = s.length();
1489: char c;
1490: int startInd, endInd;
1491: boolean isNegative = false;
1492:
1493: // Trim whitespace off front of string
1494:
1495: int i = 0;
1496: while (i < len
1497: && (((c = s.charAt(i)) == ' ') || Character
1498: .isWhitespace(c))) {
1499: i++;
1500: }
1501: if (i >= len) {
1502: throw new TclException(interp,
1503: "expected integer but got \"" + s + "\"");
1504: }
1505: startInd = i;
1506:
1507: // Trim whitespace off end of string
1508:
1509: endInd = len - 1;
1510: while (endInd > startInd
1511: && (((c = s.charAt(endInd)) == ' ') || Character
1512: .isWhitespace(c))) {
1513: endInd--;
1514: }
1515:
1516: // Check for optional '-' sign, needed for hex and octal parse.
1517:
1518: c = s.charAt(i);
1519: if (c == '-') {
1520: isNegative = true;
1521: i++;
1522: }
1523: if (i >= (endInd + 1)) {
1524: throw new TclException(interp,
1525: "expected integer but got \"" + s + "\"");
1526: }
1527:
1528: // Check for hex or octal string prefix characters
1529:
1530: int radix = Character.MIN_RADIX - 1; // An invalid value
1531:
1532: c = s.charAt(i);
1533: if (c == '0' && len > 1) {
1534: // Either hex or octal
1535: i++;
1536: c = s.charAt(i);
1537:
1538: if (len > 2 && (c == 'x' || c == 'X')) {
1539: // Parse as hex
1540: radix = 16;
1541: i++;
1542: } else {
1543: // Parse as octal
1544: radix = 8;
1545: }
1546:
1547: // Create string that contains a leading negative sign followed
1548: // by the radix letters, leaving out the radix prefix.
1549: // For example, "-0xFF" is parsed as "-FF".
1550:
1551: if (isNegative) {
1552: s = "-" + s.substring(i, endInd + 1);
1553: } else {
1554: s = s.substring(i, endInd + 1);
1555: }
1556: } else {
1557: // Parse as decimal integer
1558:
1559: if ((startInd > 0) || (endInd < (len - 1))) {
1560: s = s.substring(startInd, endInd + 1);
1561: }
1562:
1563: radix = 10;
1564: }
1565:
1566: if (s.length() == 0) {
1567: throw new TclException(interp,
1568: "expected integer but got \"" + srep + "\"");
1569: }
1570:
1571: int ival;
1572: try {
1573: ival = Integer.parseInt(s, radix);
1574: } catch (NumberFormatException nfe) {
1575: // If one of the letters is not a valid radix character, then
1576: // the number is not a valid. Otherwise, the number must be
1577: // an integer value that is outside the valid range.
1578:
1579: for (i = 0; i < s.length(); i++) {
1580: c = s.charAt(i);
1581: if (i == 0 && c == '-') {
1582: continue; // Skip minus sign
1583: }
1584: if (Character.digit(c, radix) == -1) {
1585: throw new TclException(interp,
1586: "expected integer but got \"" + srep + "\"");
1587: }
1588: }
1589:
1590: throw new TclException(interp,
1591: "integer value too large to represent in a int");
1592: }
1593:
1594: return ival;
1595: }
1596:
1597: /*
1598: *-----------------------------------------------------------------------------
1599: *
1600: * parseJavaLong --
1601: *
1602: * Parse a Java long type from a TclObject. Tcl may not support
1603: * 64 bit integers (Jacl does not), so this method needs to be used
1604: * to determine if a string can be parsed into a long and if the
1605: * result is in the range Long.MIN_VALUE to Long.MAX_VALUE.
1606: * This method will catch the case of an long outside of
1607: * the valid range and raise a TclException so that a bogus value
1608: * is not passed to Java.
1609: *
1610: * Results:
1611: * Returns a long value or raises a TclException to indicate that
1612: * the number can't be parsed as a Java long.
1613: *
1614: * Side effects:
1615: * None.
1616: *
1617: *-----------------------------------------------------------------------------
1618: */
1619:
1620: static long parseJavaLong(Interp interp, TclObject obj)
1621: throws TclException {
1622: // No point in reparsing a "pure" integer.
1623:
1624: if (obj.hasNoStringRep() && obj.isIntType()) {
1625: return (long) TclInteger.get(interp, obj);
1626: }
1627:
1628: String srep = obj.toString();
1629: String s = srep;
1630: int len = s.length();
1631: char c;
1632: int startInd, endInd;
1633: boolean isNegative = false;
1634:
1635: // Trim whitespace off front of string
1636:
1637: int i = 0;
1638: while (i < len
1639: && (((c = s.charAt(i)) == ' ') || Character
1640: .isWhitespace(c))) {
1641: i++;
1642: }
1643: if (i >= len) {
1644: throw new TclException(interp,
1645: "expected integer but got \"" + s + "\"");
1646: }
1647: startInd = i;
1648:
1649: // Trim whitespace off end of string
1650:
1651: endInd = len - 1;
1652: while (endInd > startInd
1653: && (((c = s.charAt(endInd)) == ' ') || Character
1654: .isWhitespace(c))) {
1655: endInd--;
1656: }
1657:
1658: // Check for optional '-' sign, needed for hex and octal parse.
1659:
1660: c = s.charAt(i);
1661: if (c == '-') {
1662: isNegative = true;
1663: i++;
1664: }
1665: if (i >= (endInd + 1)) {
1666: throw new TclException(interp,
1667: "expected integer but got \"" + s + "\"");
1668: }
1669:
1670: // Check for hex or octal string prefix characters
1671:
1672: int radix = Character.MIN_RADIX - 1; // An invalid value
1673:
1674: c = s.charAt(i);
1675: if (c == '0' && len > 1) {
1676: // Either hex or octal
1677: i++;
1678: c = s.charAt(i);
1679:
1680: if (len > 2 && (c == 'x' || c == 'X')) {
1681: // Parse as hex
1682: radix = 16;
1683: i++;
1684: } else {
1685: // Parse as octal
1686: radix = 8;
1687: }
1688:
1689: // Create string that contains a leading negative sign followed
1690: // by the radix letters, leaving out the radix prefix.
1691: // For example, "-0xFF" is parsed as "-FF".
1692:
1693: if (isNegative) {
1694: s = "-" + s.substring(i, endInd + 1);
1695: } else {
1696: s = s.substring(i, endInd + 1);
1697: }
1698: } else {
1699: // Parse as decimal integer
1700:
1701: if ((startInd > 0) || (endInd < (len - 1))) {
1702: s = s.substring(startInd, endInd + 1);
1703: }
1704:
1705: radix = 10;
1706: }
1707:
1708: if (s.length() == 0) {
1709: throw new TclException(interp,
1710: "expected integer but got \"" + srep + "\"");
1711: }
1712:
1713: long lval;
1714: try {
1715: lval = Long.parseLong(s, radix);
1716: } catch (NumberFormatException nfe) {
1717: // If one of the letters is not a valid radix character, then
1718: // the number is not a valid. Otherwise, the number must be
1719: // an integer value that is outside the valid range.
1720:
1721: for (i = 0; i < s.length(); i++) {
1722: c = s.charAt(i);
1723: if (i == 0 && c == '-') {
1724: continue; // Skip minus sign
1725: }
1726: if (Character.digit(c, radix) == -1) {
1727: throw new TclException(interp,
1728: "expected integer but got \"" + srep + "\"");
1729: }
1730: }
1731:
1732: throw new TclException(interp,
1733: "integer value too large to represent in a long");
1734: }
1735:
1736: return lval;
1737: }
1738:
1739: } // end JavaInvoke
|