001: /*
002: * Java.java
003: *
004: * Copyright (C) 2002-2004 Peter Graves
005: * $Id: Java.java,v 1.44 2004/08/10 22:28:13 asimon Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.lisp;
023:
024: import java.lang.reflect.Constructor;
025: import java.lang.reflect.InvocationTargetException;
026: import java.lang.reflect.Method;
027: import java.lang.reflect.Field;
028: import java.lang.reflect.Modifier;
029: import java.lang.reflect.Array;
030:
031: public final class Java extends Lisp {
032: // ### jclass
033: private static final Primitive1 JCLASS = new Primitive1("jclass",
034: PACKAGE_JAVA, true, "name") {
035: public LispObject execute(LispObject arg)
036: throws ConditionThrowable {
037: String className = arg.getStringValue();
038: try {
039: return new JavaObject(classForName(className));
040: } catch (ClassNotFoundException e) {
041: signal(new LispError("class not found: " + className));
042: } catch (Throwable t) {
043: signal(new LispError(getMessage(t)));
044: }
045: // Not reached.
046: return NIL;
047: }
048: };
049:
050: // ### jfield - retrieve or modify a field in a Java class or instance.
051: /*
052: * Supported argument patterns:
053: *
054: * Case 1: class-ref field-name:
055: * to retrieve the value of a static field.
056: *
057: * Case 2: class-ref field-name instance-ref:
058: * to retrieve the value of a class field of the instance.
059: *
060: * Case 3: class-ref field-name primitive-value:
061: * to store primitive-value in a static field.
062: *
063: * Case 4: class-ref field-name instance-ref value:
064: * to store value in a class field of the instance.
065: *
066: * Case 5: class-ref field-name nil value:
067: * to store value in a static field (when value may be
068: * confused with an instance-ref).
069: *
070: * Case 6: field-name instance:
071: * to retrieve the value of a field of the instance. The
072: * class is derived from the instance.
073: *
074: * Case 7: field-name instance value:
075: * to store value in a field of the instance. The class is
076: * derived from the instance.
077: */
078: private static final Primitive JFIELD = new Primitive("jfield",
079: PACKAGE_JAVA, true,
080: "class-ref-or-field field-or-instance &optional instance value") {
081: public LispObject execute(LispObject[] args)
082: throws ConditionThrowable {
083: return makeLispObject((JFIELD_RAW.execute(args))
084: .javaInstance());
085: }
086: };
087:
088: // ### jfield-raw - retrieve or modify a field in a Java class or instance.
089: private static final Primitive JFIELD_RAW = new Primitive(
090: "jfield-raw", PACKAGE_JAVA, true,
091: "class-ref-or-field field-or-instance &optional instance value") {
092: public LispObject execute(LispObject[] args)
093: throws ConditionThrowable {
094: if (args.length < 2 || args.length > 4)
095: signal(new WrongNumberOfArgumentsException(this ));
096: String fieldName = null;
097: Class c;
098: Field f;
099: Class fieldType;
100: Object instance = null;
101: try {
102: if (args[1] instanceof AbstractString) {
103: // Cases 1-5.
104: fieldName = args[1].getStringValue();
105: c = forClassRef(args[0]);
106: } else {
107: // Cases 6 and 7.
108: fieldName = args[0].getStringValue();
109: instance = JavaObject.getObject(args[1]);
110: c = instance.getClass();
111: }
112: f = c.getField(fieldName);
113: fieldType = f.getType();
114: switch (args.length) {
115: case 2:
116: // Cases 1 and 6.
117: break;
118: case 3:
119: // Cases 2,3, and 7.
120: if (instance == null) {
121: // Cases 2 and 3.
122: if (args[2] instanceof JavaObject) {
123: // Case 2.
124: instance = JavaObject.getObject(args[2]);
125: break;
126: } else {
127: // Case 3.
128: f
129: .set(null, args[2]
130: .javaInstance(fieldType));
131: return args[2];
132: }
133: } else {
134: // Case 7.
135: f
136: .set(instance, args[2]
137: .javaInstance(fieldType));
138: return args[2];
139: }
140: case 4:
141: // Cases 4 and 5.
142: if (args[2] != NIL) {
143: // Case 4.
144: instance = JavaObject.getObject(args[2]);
145: }
146: f.set(instance, args[3].javaInstance(fieldType));
147: return args[3];
148: }
149: return new JavaObject(f.get(instance));
150: } catch (ClassNotFoundException e) {
151: signal(new LispError("class not found: "
152: + e.getMessage()));
153: } catch (NoSuchFieldException e) {
154: signal(new LispError("no such field"));
155: } catch (SecurityException e) {
156: signal(new LispError("inaccessible field"));
157: } catch (IllegalAccessException e) {
158: signal(new LispError("illegal access"));
159: } catch (IllegalArgumentException e) {
160: signal(new LispError("illegal argument"));
161: } catch (Throwable t) {
162: signal(new LispError(getMessage(t)));
163: }
164: // Not reached.
165: return NIL;
166: }
167: };
168:
169: // ### jconstructor
170: // jconstructor class-ref &rest parameter-class-refs
171: private static final Primitive JCONSTRUCTOR = new Primitive(
172: "jconstructor", PACKAGE_JAVA, true,
173: "class-ref &rest parameter-class-refs") {
174: public LispObject execute(LispObject[] args)
175: throws ConditionThrowable {
176: if (args.length < 1)
177: signal(new WrongNumberOfArgumentsException(this ));
178: try {
179: final Class c = forClassRef(args[0]);
180: int argCount = 0;
181: if (args.length == 2 && args[1] instanceof Fixnum) {
182: argCount = Fixnum.getValue(args[1]);
183: } else {
184: Class[] parameterTypes = new Class[args.length - 1];
185: for (int i = 1; i < args.length; i++) {
186: parameterTypes[i - 1] = forClassRef(args[i]);
187: }
188: return new JavaObject(c
189: .getConstructor(parameterTypes));
190: }
191: // Parameter types not explicitly specified.
192: Constructor[] constructors = c.getConstructors();
193: for (int i = 0; i < constructors.length; i++) {
194: Constructor constructor = constructors[i];
195: if (constructor.getParameterTypes().length == argCount)
196: return new JavaObject(constructor);
197: }
198: throw new NoSuchMethodException();
199: } catch (ClassNotFoundException e) {
200: signal(new LispError("class not found: "
201: + e.getMessage()));
202: } catch (NoSuchMethodException e) {
203: signal(new LispError("no such constructor"));
204: } catch (ConditionThrowable e) {
205: throw e;
206: } catch (Throwable t) {
207: signal(new LispError(getMessage(t)));
208: }
209: // Not reached.
210: return NIL;
211: }
212: };
213:
214: // ### jmethod
215: // jmethod class-ref name &rest parameter-class-refs
216: private static final Primitive JMETHOD = new Primitive("jmethod",
217: PACKAGE_JAVA, true,
218: "class-ref name &rest parameter-class-refs") {
219: public LispObject execute(LispObject[] args)
220: throws ConditionThrowable {
221: if (args.length < 2)
222: signal(new WrongNumberOfArgumentsException(this ));
223: String methodName = args[1].getStringValue();
224: try {
225: final Class c = forClassRef(args[0]);
226: int argCount = 0;
227: if (args.length == 3 && args[2] instanceof Fixnum) {
228: argCount = Fixnum.getValue(args[2]);
229: } else {
230: Class[] parameterTypes = new Class[args.length - 2];
231: for (int i = 2; i < args.length; i++) {
232: parameterTypes[i - 2] = forClassRef(args[i]);
233: }
234: return new JavaObject(c.getMethod(methodName,
235: parameterTypes));
236: }
237: // Parameter types not explicitly specified.
238: Method[] methods = c.getMethods();
239: for (int i = 0; i < methods.length; i++) {
240: Method method = methods[i];
241: if (method.getName().equals(methodName)
242: && method.getParameterTypes().length == argCount)
243: return new JavaObject(method);
244: }
245: throw new NoSuchMethodException();
246: } catch (ClassNotFoundException e) {
247: signal(new LispError("class not found: "
248: + e.getMessage()));
249: } catch (NoSuchMethodException e) {
250: signal(new LispError("no such method: " + methodName));
251: } catch (ConditionThrowable e) {
252: throw e;
253: } catch (Throwable t) {
254: signal(new LispError(getMessage(t)));
255: }
256: // Not reached.
257: return NIL;
258: }
259: };
260:
261: // ### jstatic
262: // jstatic method class &rest args
263: private static final Primitive JSTATIC = new Primitive("jstatic",
264: PACKAGE_JAVA, true, "method class &rest args") {
265: public LispObject execute(LispObject[] args)
266: throws ConditionThrowable {
267: return makeLispObject((JSTATIC_RAW.execute(args))
268: .javaInstance());
269: }
270: };
271:
272: // ### jstatic-raw
273: // jstatic-raw method class &rest args
274: private static final Primitive JSTATIC_RAW = new Primitive(
275: "jstatic-raw", PACKAGE_JAVA, true,
276: "method class &rest args") {
277: public LispObject execute(LispObject[] args)
278: throws ConditionThrowable {
279: if (args.length < 2)
280: signal(new WrongNumberOfArgumentsException(this ));
281: try {
282: Method m = null;
283: LispObject methodRef = args[0];
284: if (methodRef instanceof JavaObject) {
285: Object obj = ((JavaObject) methodRef).getObject();
286: if (obj instanceof Method)
287: m = (Method) obj;
288: } else if (methodRef instanceof AbstractString) {
289: Class c = forClassRef(args[1]);
290: if (c != null) {
291: String methodName = methodRef.getStringValue();
292: Method[] methods = c.getMethods();
293: int argCount = args.length - 2;
294: for (int i = 0; i < methods.length; i++) {
295: Method method = methods[i];
296: if (!Modifier.isStatic(method
297: .getModifiers())
298: || method.getParameterTypes().length != argCount)
299: continue;
300: if (method.getName().equals(methodName)) {
301: m = method;
302: break;
303: }
304: }
305: if (m == null)
306: signal(new LispError("no such method"));
307: }
308: } else
309: signal(new TypeError("wrong type: " + methodRef));
310: Object[] methodArgs = new Object[args.length - 2];
311: Class[] argTypes = m.getParameterTypes();
312: for (int i = 2; i < args.length; i++) {
313: LispObject arg = args[i];
314: if (arg == NIL)
315: methodArgs[i - 2] = null;
316: else
317: methodArgs[i - 2] = arg
318: .javaInstance(argTypes[i - 2]);
319: }
320: Object result = m.invoke(null, methodArgs);
321: return new JavaObject(result);
322: } catch (Throwable t) {
323: signal(new LispError(getMessage(t)));
324: }
325: // Not reached.
326: return NIL;
327: }
328: };
329:
330: // ### jnew
331: // jnew constructor &rest args
332: private static final Primitive JNEW = new Primitive("jnew",
333: PACKAGE_JAVA, true, "constructor &rest args") {
334: public LispObject execute(LispObject[] args)
335: throws ConditionThrowable {
336: if (args.length < 1)
337: signal(new WrongNumberOfArgumentsException(this ));
338: LispObject classRef = args[0];
339: try {
340: Constructor constructor = (Constructor) JavaObject
341: .getObject(classRef);
342: Class[] argTypes = constructor.getParameterTypes();
343: Object[] initargs = new Object[args.length - 1];
344: for (int i = 1; i < args.length; i++) {
345: LispObject arg = args[i];
346: if (arg == NIL)
347: initargs[i - 1] = null;
348: else {
349: initargs[i - 1] = arg
350: .javaInstance(argTypes[i - 1]);
351: }
352: }
353: return new JavaObject(constructor.newInstance(initargs));
354: } catch (Throwable t) {
355: signal(new LispError(getMessage(t)));
356: }
357: // Not reached.
358: return NIL;
359: }
360: };
361:
362: // ### jnew-array
363: // jnew-array element-type &rest dimensions
364: private static final Primitive JNEW_ARRAY = new Primitive(
365: "jnew-array", PACKAGE_JAVA, true,
366: "element-type &rest dimensions") {
367: public LispObject execute(LispObject[] args)
368: throws ConditionThrowable {
369: if (args.length < 2)
370: signal(new WrongNumberOfArgumentsException(this ));
371: try {
372: Class c = forClassRef(args[0]);
373: int[] dimensions = new int[args.length - 1];
374: for (int i = 1; i < args.length; i++)
375: dimensions[i - 1] = ((Integer) args[i]
376: .javaInstance()).intValue();
377: return new JavaObject(Array.newInstance(c, dimensions));
378: } catch (ClassNotFoundException e) {
379: signal(new LispError("class not found: "
380: + e.getMessage()));
381: } catch (Throwable t) {
382: signal(new LispError(getMessage(t)));
383: }
384: // Not reached.
385: return NIL;
386: }
387: };
388:
389: // ### jarray-ref
390: // jarray-ref java-array &rest indices
391: private static final Primitive JARRAY_REF = new Primitive(
392: "jarray-ref", PACKAGE_JAVA, true,
393: "java-array &rest indices") {
394: public LispObject execute(LispObject[] args)
395: throws ConditionThrowable {
396: return makeLispObject((JARRAY_REF_RAW.execute(args))
397: .javaInstance());
398: }
399: };
400:
401: // ### jarray-ref-raw
402: // jarray-ref-raw java-array &rest indices
403: private static final Primitive JARRAY_REF_RAW = new Primitive(
404: "jarray-ref-raw", PACKAGE_JAVA, true,
405: "java-array &rest indices") {
406: public LispObject execute(LispObject[] args)
407: throws ConditionThrowable {
408: if (args.length < 2)
409: signal(new WrongNumberOfArgumentsException(this ));
410: try {
411: Object a = args[0].javaInstance();
412: for (int i = 1; i < args.length - 1; i++)
413: a = Array.get(a, ((Integer) args[i].javaInstance())
414: .intValue());
415: return new JavaObject(Array
416: .get(a, ((Integer) args[args.length - 1]
417: .javaInstance()).intValue()));
418: } catch (Throwable t) {
419: signal(new LispError(getMessage(t)));
420: }
421: // Not reached.
422: return NIL;
423: }
424: };
425:
426: // ### jarray-set
427: // jarray-set java-array new-value &rest indices
428: private static final Primitive JARRAY_SET = new Primitive(
429: "jarray-set", PACKAGE_JAVA, true,
430: "java-array new-value &rest indices") {
431: public LispObject execute(LispObject[] args)
432: throws ConditionThrowable {
433: if (args.length < 3)
434: signal(new WrongNumberOfArgumentsException(this ));
435: try {
436: Object a = args[0].javaInstance();
437: LispObject v = args[1];
438: for (int i = 2; i < args.length - 1; i++)
439: a = Array.get(a, ((Integer) args[i].javaInstance())
440: .intValue());
441: Array.set(a, ((Integer) args[args.length - 1]
442: .javaInstance()).intValue(), v.javaInstance());
443: return v;
444: } catch (Throwable t) {
445: signal(new LispError(getMessage(t)));
446: }
447: // Not reached.
448: return NIL;
449: }
450: };
451:
452: // ### jcall
453: // jcall method instance &rest args
454: private static final Primitive JCALL = new Primitive("jcall",
455: PACKAGE_JAVA, true, "method instance &rest args") {
456: public LispObject execute(LispObject[] args)
457: throws ConditionThrowable {
458: return makeLispObject((JCALL_RAW.execute(args))
459: .javaInstance());
460: }
461: };
462:
463: // ### jcall-raw
464: // jcall-raw method instance &rest args
465: private static final Primitive JCALL_RAW = new Primitive(
466: "jcall-raw", PACKAGE_JAVA, true,
467: "method instance &rest args") {
468: public LispObject execute(LispObject[] args)
469: throws ConditionThrowable {
470: if (args.length < 2)
471: signal(new WrongNumberOfArgumentsException(this ));
472: try {
473: Method method = (Method) JavaObject.getObject(args[0]);
474: Class[] argTypes = method.getParameterTypes();
475: Object instance;
476: if (args[1] instanceof AbstractString)
477: instance = args[1].getStringValue();
478: else
479: instance = JavaObject.getObject(args[1]);
480: Object[] methodArgs = new Object[args.length - 2];
481: for (int i = 2; i < args.length; i++) {
482: LispObject arg = args[i];
483: if (arg == NIL)
484: methodArgs[i - 2] = null;
485: else
486: methodArgs[i - 2] = arg
487: .javaInstance(argTypes[i - 2]);
488: }
489: Object result = method.invoke(instance, methodArgs);
490: return new JavaObject(result);
491: } catch (Throwable t) {
492: signal(new LispError(getMessage(t)));
493: }
494: // Not reached.
495: return NIL;
496: }
497: };
498:
499: // ### make-immediate-object
500: // make-immediate-object object &optional type
501: private static final Primitive MAKE_IMMEDIATE_OBJECT = new Primitive(
502: "make-immediate-object", PACKAGE_JAVA, true,
503: "object &optional type") {
504: public LispObject execute(LispObject[] args)
505: throws ConditionThrowable {
506: if (args.length < 1)
507: signal(new WrongNumberOfArgumentsException(this ));
508: LispObject object = args[0];
509: try {
510: if (args.length > 1) {
511: LispObject type = args[1];
512: if (type == Keyword.BOOLEAN) {
513: if (object == NIL)
514: return new JavaObject(Boolean.FALSE);
515: else
516: return new JavaObject(Boolean.TRUE);
517: }
518: if (type == Keyword.REF) {
519: if (object == NIL)
520: return new JavaObject(null);
521: else
522: throw new Error();
523: }
524: // other special cases come here
525: }
526: return new JavaObject(object.javaInstance());
527: } catch (Throwable t) {
528: signal(new LispError(
529: "MAKE-IMMEDIATE-OBJECT: not implemented"));
530: }
531: // Not reached.
532: return NIL;
533: }
534: };
535:
536: private static final Primitive1 JAVA_OBJECT_P = new Primitive1(
537: "java-object-p", PACKAGE_JAVA, true, "object") {
538: public LispObject execute(LispObject arg)
539: throws ConditionThrowable {
540: return (arg instanceof JavaObject) ? T : NIL;
541: }
542: };
543:
544: // ### jobject-lisp-value
545: // jobject-lisp-value java-object
546: private static final Primitive1 JOBJECT_LISP_VALUE = new Primitive1(
547: "jobject-lisp-value", PACKAGE_JAVA, true, "java-object") {
548: public LispObject execute(LispObject arg)
549: throws ConditionThrowable {
550: return makeLispObject(arg.javaInstance());
551: }
552: };
553:
554: private static Class classForName(String className)
555: throws ClassNotFoundException {
556: try {
557: return Class.forName(className);
558: } catch (ClassNotFoundException e) {
559: return Class.forName(className, true, JavaClassLoader
560: .getPersistentInstance());
561: }
562: }
563:
564: // Supports Java primitive types too.
565: private static Class forClassRef(LispObject classRef)
566: throws ClassNotFoundException, ConditionThrowable {
567: if (classRef instanceof AbstractString) {
568: String className = classRef.getStringValue();
569: if (className.equals("boolean"))
570: return Boolean.TYPE;
571: if (className.equals("byte"))
572: return Byte.TYPE;
573: if (className.equals("char"))
574: return Character.TYPE;
575: if (className.equals("short"))
576: return Short.TYPE;
577: if (className.equals("int"))
578: return Integer.TYPE;
579: if (className.equals("long"))
580: return Long.TYPE;
581: if (className.equals("float"))
582: return Float.TYPE;
583: if (className.equals("double"))
584: return Double.TYPE;
585: // Not a primitive Java type.
586: return classForName(className);
587: } else
588: try {
589: return (Class) JavaObject.getObject(classRef);
590: } catch (ClassCastException e) {
591: signal(new TypeError(classRef, "Java class"));
592: } catch (ConditionThrowable e) {
593: throw new ClassNotFoundException(e.getMessage());
594: }
595: // Not reached.
596: return null;
597: }
598:
599: private static final LispObject makeLispObject(Object obj)
600: throws ConditionThrowable {
601: if (obj == null)
602: return NIL;
603: if (obj instanceof Boolean)
604: return ((Boolean) obj).booleanValue() ? T : NIL;
605: if (obj instanceof Integer)
606: return new Fixnum(((Integer) obj).intValue());
607: if (obj instanceof Long)
608: return new Bignum(((Long) obj).longValue());
609: if (obj instanceof Double || obj instanceof Float)
610: return new LispFloat(((Number) obj).doubleValue());
611: if (obj instanceof String)
612: return new SimpleString((String) obj);
613: if (obj instanceof Character)
614: return LispCharacter.getInstance(((Character) obj)
615: .charValue());
616: if (obj instanceof Object[]) {
617: Object[] array = (Object[]) obj;
618: SimpleVector v = new SimpleVector(array.length);
619: for (int i = array.length; i-- > 0;)
620: v.setRowMajor(i, new JavaObject(array[i]));
621: return v;
622: }
623: if (obj instanceof LispObject)
624: return (LispObject) obj;
625: return new JavaObject(obj);
626: }
627:
628: private static final String getMessage(Throwable t) {
629: if (t instanceof InvocationTargetException) {
630: try {
631: Method method = Throwable.class.getMethod("getCause",
632: new Class[0]);
633: if (method != null) {
634: Throwable cause = (Throwable) method.invoke(t,
635: new Object[0]);
636: if (cause != null)
637: t = cause;
638: }
639: } catch (NoSuchMethodException e) {
640: Debug.trace(e);
641: } catch (Exception e) {
642: Debug.trace(e);
643: }
644: }
645: String message = t.getMessage();
646: if (message == null || message.length() == 0)
647: message = t.getClass().getName();
648: return message;
649: }
650: }
|