001: /*
002: * Java.java
003: *
004: * Copyright (C) 2002-2003 Peter Graves
005: * $Id: Java.java,v 1.9 2003/11/15 11:03:32 beedlem 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:
030: public final class Java extends Module {
031: // ### jclass
032: private static final Primitive1 JCLASS = new Primitive1("jclass",
033: PACKAGE_JAVA) {
034: public LispObject execute(LispObject arg)
035: throws ConditionThrowable {
036: try {
037: return new JavaObject(Class.forName(LispString
038: .getValue(arg)));
039: } catch (ClassNotFoundException e) {
040: throw new ConditionThrowable(new LispError(
041: "class not found: " + arg));
042: } catch (Throwable t) {
043: throw new ConditionThrowable(new LispError(
044: getMessage(t)));
045: }
046: }
047: };
048:
049: // ### jfield - retrieve or modify a field in a Java class or instance.
050: /*
051: * Supported argument patterns:
052: *
053: * Case 1: class-ref field-name:
054: * to retrieve the value of a static field.
055: *
056: * Case 2: class-ref field-name instance-ref:
057: * to retrieve the value of a class field of the instance.
058: *
059: * Case 3: class-ref field-name primitive-value:
060: * to store primitive-value in a static field.
061: *
062: * Case 4: class-ref field-name instance-ref value:
063: * to store value in a class field of the instance.
064: *
065: * Case 5: class-ref field-name nil value:
066: * to store value in a static field (when value may be
067: * confused with an instance-ref).
068: *
069: * Case 6: field-name instance:
070: * to retrieve the value of a field of the instance. The
071: * class is derived from the instance.
072: *
073: * Case 7: field-name instance value:
074: * to store value in a field of the instance. The class is
075: * derived from the instance.
076: */
077: private static final Primitive JFIELD = new Primitive("jfield",
078: PACKAGE_JAVA) {
079: public LispObject execute(LispObject[] args)
080: throws ConditionThrowable {
081: if (args.length < 2 || args.length > 4)
082: throw new ConditionThrowable(
083: new WrongNumberOfArgumentsException(this ));
084: String fieldName = null;
085: Class c;
086: Field f;
087: Object instance = null;
088: try {
089: if (args[1] instanceof LispString) {
090: // Cases 1-5.
091: fieldName = LispString.getValue(args[1]);
092: c = forClassRef(args[0]);
093: } else {
094: // Cases 6 and 7.
095: fieldName = LispString.getValue(args[0]);
096: instance = JavaObject.getObject(args[1]);
097: c = instance.getClass();
098: }
099: f = c.getField(fieldName);
100:
101: switch (args.length) {
102: case 2:
103: // Cases 1 and 6.
104: break;
105: case 3:
106: // Cases 2,3, and 7.
107: if (instance == null) {
108: // Cases 2 and 3.
109: if (args[2] instanceof JavaObject) {
110: // Case 2.
111: instance = JavaObject.getObject(args[2]);
112: break;
113: } else {
114: // Case 3.
115: f.set(null, args[2].javaInstance());
116: return args[2];
117: }
118: } else {
119: // Case 7.
120: f.set(instance, JavaObject.getObject(args[2]));
121: return args[2];
122: }
123: case 4:
124: // Cases 4 and 5.
125: if (args[2] != NIL) {
126: // Case 4.
127: instance = JavaObject.getObject(args[2]);
128: }
129: f.set(instance, JavaObject.getObject(args[3]));
130: return args[3];
131: }
132: return makeLispObject(f.get(instance));
133: } catch (ClassNotFoundException e) {
134: throw new ConditionThrowable(new LispError(
135: "class not found: " + e.getMessage()));
136: } catch (NoSuchFieldException e) {
137: throw new ConditionThrowable(new LispError(
138: "no such field"));
139: } catch (SecurityException e) {
140: throw new ConditionThrowable(new LispError(
141: "inaccessible field"));
142: } catch (IllegalAccessException e) {
143: throw new ConditionThrowable(new LispError(
144: "illegal access"));
145: } catch (IllegalArgumentException e) {
146: throw new ConditionThrowable(new LispError(
147: "illegal argument"));
148: } catch (Throwable t) {
149: throw new ConditionThrowable(new LispError(
150: getMessage(t)));
151: }
152: }
153: };
154:
155: // ### jconstructor
156: // jconstructor class-ref &rest parameter-class-refs
157: private static final Primitive JCONSTRUCTOR = new Primitive(
158: "jconstructor", PACKAGE_JAVA) {
159: public LispObject execute(LispObject[] args)
160: throws ConditionThrowable {
161: if (args.length < 1)
162: throw new ConditionThrowable(
163: new WrongNumberOfArgumentsException(this ));
164: try {
165: final Class c = forClassRef(args[0]);
166: int argCount = 0;
167: if (args.length == 2 && args[1] instanceof Fixnum) {
168: argCount = Fixnum.getValue(args[1]);
169: } else {
170: Class[] parameterTypes = new Class[args.length - 1];
171: for (int i = 1; i < args.length; i++) {
172: parameterTypes[i - 1] = forClassRef(args[i]);
173: }
174: return new JavaObject(c
175: .getConstructor(parameterTypes));
176: }
177: // Parameter types not explicitly specified.
178: Constructor[] constructors = c.getConstructors();
179: for (int i = 0; i < constructors.length; i++) {
180: Constructor constructor = constructors[i];
181: if (constructor.getParameterTypes().length == argCount)
182: return new JavaObject(constructor);
183: }
184: throw new NoSuchMethodException();
185: } catch (ClassNotFoundException e) {
186: throw new ConditionThrowable(new LispError(
187: "class not found: " + e.getMessage()));
188: } catch (NoSuchMethodException e) {
189: throw new ConditionThrowable(new LispError(
190: "no such constructor"));
191: } catch (ConditionThrowable e) {
192: throw e;
193: } catch (Throwable t) {
194: throw new ConditionThrowable(new LispError(
195: getMessage(t)));
196: }
197: }
198: };
199:
200: // ### jmethod
201: // jmethod class-ref name &rest parameter-class-refs
202: private static final Primitive JMETHOD = new Primitive("jmethod",
203: PACKAGE_JAVA) {
204: public LispObject execute(LispObject[] args)
205: throws ConditionThrowable {
206: if (args.length < 2)
207: throw new ConditionThrowable(
208: new WrongNumberOfArgumentsException(this ));
209: String methodName = LispString.getValue(args[1]);
210: try {
211: final Class c = forClassRef(args[0]);
212: int argCount = 0;
213: if (args.length == 3 && args[2] instanceof Fixnum) {
214: argCount = Fixnum.getValue(args[2]);
215: } else {
216: Class[] parameterTypes = new Class[args.length - 2];
217: for (int i = 2; i < args.length; i++) {
218: parameterTypes[i - 2] = forClassRef(args[i]);
219: }
220: return new JavaObject(c.getMethod(methodName,
221: parameterTypes));
222: }
223: // Parameter types not explicitly specified.
224: Method[] methods = c.getMethods();
225: for (int i = 0; i < methods.length; i++) {
226: Method method = methods[i];
227: if (method.getName().equals(methodName)
228: && method.getParameterTypes().length == argCount)
229: return new JavaObject(method);
230: }
231: throw new NoSuchMethodException();
232: } catch (ClassNotFoundException e) {
233: throw new ConditionThrowable(new LispError(
234: "class not found: " + e.getMessage()));
235: } catch (NoSuchMethodException e) {
236: throw new ConditionThrowable(new LispError(
237: "no such method: " + methodName));
238: } catch (ConditionThrowable e) {
239: throw e;
240: } catch (Throwable t) {
241: throw new ConditionThrowable(new LispError(
242: getMessage(t)));
243: }
244: }
245: };
246:
247: // ### jstatic
248: // jstatic method class &rest args
249: private static final Primitive JSTATIC = new Primitive("jstatic",
250: PACKAGE_JAVA) {
251: public LispObject execute(LispObject[] args)
252: throws ConditionThrowable {
253: if (args.length < 2)
254: throw new ConditionThrowable(
255: new WrongNumberOfArgumentsException(this ));
256: try {
257: Method m = null;
258: LispObject methodRef = args[0];
259: if (methodRef instanceof JavaObject) {
260: Object obj = ((JavaObject) methodRef).getObject();
261: if (obj instanceof Method)
262: m = (Method) obj;
263: } else if (methodRef instanceof LispString) {
264: Class c = forClassRef(args[1]);
265: if (c != null) {
266: String methodName = LispString
267: .getValue(methodRef);
268: Method[] methods = c.getMethods();
269: for (int i = 0; i < methods.length; i++) {
270: Method method = methods[i];
271: if (!Modifier.isStatic(method
272: .getModifiers()))
273: continue;
274: if (method.getName().equals(methodName)) {
275: m = method;
276: break;
277: }
278: }
279: if (m == null)
280: throw new ConditionThrowable(new LispError(
281: "no such method"));
282: }
283: } else
284: throw new ConditionThrowable(new TypeError(
285: "wrong type: " + methodRef));
286: Object[] methodArgs = new Object[args.length - 2];
287: for (int i = 2; i < args.length; i++) {
288: methodArgs[i - 2] = args[i].javaInstance();
289: }
290: Object result = m.invoke(null, methodArgs);
291: return makeLispObject(result);
292: } catch (Throwable t) {
293: throw new ConditionThrowable(new LispError(
294: getMessage(t)));
295: }
296: }
297: };
298:
299: // ### jnew
300: // jnew constructor &rest args
301: private static final Primitive JNEW = new Primitive("jnew",
302: PACKAGE_JAVA) {
303: public LispObject execute(LispObject[] args)
304: throws ConditionThrowable {
305: if (args.length < 1)
306: throw new ConditionThrowable(
307: new WrongNumberOfArgumentsException(this ));
308: LispObject classRef = args[0];
309: try {
310: Constructor constructor = (Constructor) JavaObject
311: .getObject(classRef);
312: Object[] initargs = new Object[args.length - 1];
313: for (int i = 1; i < args.length; i++) {
314: initargs[i - 1] = args[i].javaInstance();
315: }
316: return new JavaObject(constructor.newInstance(initargs));
317: } catch (Throwable t) {
318: throw new ConditionThrowable(new LispError(
319: getMessage(t)));
320: }
321: }
322: };
323:
324: // ### jcall
325: // jcall method instance &rest args
326: private static final Primitive JCALL = new Primitive("jcall",
327: PACKAGE_JAVA) {
328: public LispObject execute(LispObject[] args)
329: throws ConditionThrowable {
330: if (args.length < 2)
331: throw new ConditionThrowable(
332: new WrongNumberOfArgumentsException(this ));
333: try {
334: Method method = (Method) JavaObject.getObject(args[0]);
335: Object instance;
336: if (args[1] instanceof LispString)
337: instance = LispString.getValue(args[1]);
338: else
339: instance = JavaObject.getObject(args[1]);
340: Object[] methodArgs = new Object[args.length - 2];
341: for (int i = 2; i < args.length; i++) {
342: methodArgs[i - 2] = args[i].javaInstance();
343: }
344: Object result = method.invoke(instance, methodArgs);
345: return makeLispObject(result);
346: } catch (Throwable t) {
347: throw new ConditionThrowable(new LispError(
348: getMessage(t)));
349: }
350: }
351: };
352:
353: // Supports Java primitive types too.
354: private static Class forClassRef(LispObject classRef)
355: throws ClassNotFoundException, ConditionThrowable {
356: if (classRef instanceof LispString) {
357: String className = LispString.getValue(classRef);
358: if (className.equals("boolean"))
359: return Boolean.TYPE;
360: if (className.equals("byte"))
361: return Byte.TYPE;
362: if (className.equals("char"))
363: return Character.TYPE;
364: if (className.equals("short"))
365: return Short.TYPE;
366: if (className.equals("int"))
367: return Integer.TYPE;
368: if (className.equals("long"))
369: return Long.TYPE;
370: if (className.equals("float"))
371: return Float.TYPE;
372: if (className.equals("double"))
373: return Double.TYPE;
374: // Not a primitive Java type.
375: return Class.forName(className);
376: } else
377: try {
378: return (Class) JavaObject.getObject(classRef);
379: } catch (ClassCastException e) {
380: throw new ConditionThrowable(new TypeError(classRef,
381: "Java class"));
382: } catch (ConditionThrowable e) {
383: throw new ClassNotFoundException(e.getMessage());
384: }
385: }
386:
387: private static final LispObject makeLispObject(Object obj)
388: throws ConditionThrowable {
389: if (obj == null)
390: return NIL;
391: if (obj instanceof Boolean)
392: return ((Boolean) obj).booleanValue() ? T : NIL;
393: if (obj instanceof Integer)
394: return new Fixnum(((Integer) obj).intValue());
395: if (obj instanceof Long)
396: return new Bignum(((Long) obj).longValue());
397: if (obj instanceof Double || obj instanceof Float)
398: return new LispFloat(((Number) obj).doubleValue());
399: if (obj instanceof String)
400: return new LispString((String) obj);
401: if (obj instanceof Character)
402: return LispCharacter.getInstance(((Character) obj)
403: .charValue());
404: if (obj instanceof Object[]) {
405: Object[] array = (Object[]) obj;
406: Vector v = new Vector(array.length);
407: for (int i = array.length; i-- > 0;)
408: v.set(i, new JavaObject(array[i]));
409: return v;
410: }
411: return new JavaObject(obj);
412: }
413:
414: private static final String getMessage(Throwable t) {
415: if (t instanceof InvocationTargetException) {
416: try {
417: Method method = Throwable.class.getMethod("getCause",
418: new Class[0]);
419: if (method != null) {
420: Throwable cause = (Throwable) method.invoke(t,
421: new Object[0]);
422: if (cause != null)
423: t = cause;
424: }
425: } catch (NoSuchMethodException e) {
426: Debug.trace(e);
427: } catch (Exception e) {
428: Debug.trace(e);
429: }
430: }
431: String message = t.getMessage();
432: if (message == null || message.length() == 0)
433: message = t.getClass().getName();
434: return message;
435: }
436:
437: static {
438: export("JCLASS", PACKAGE_JAVA);
439: export("JFIELD", PACKAGE_JAVA);
440: export("JCONSTRUCTOR", PACKAGE_JAVA);
441: export("JMETHOD", PACKAGE_JAVA);
442: export("JSTATIC", PACKAGE_JAVA);
443: export("JNEW", PACKAGE_JAVA);
444: export("JCALL", PACKAGE_JAVA);
445: }
446: }
|