source: branches/0.24.x/abcl/src/org/armedbear/lisp/Java.java

Last change on this file was 13113, checked in by ehuelsmann, 15 years ago

Finalize renaming JNULL_REF to JNULL_REF_P.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 48.0 KB
Line 
1/*
2 * Java.java
3 *
4 * Copyright (C) 2002-2006 Peter Graves, Andras Simon
5 * $Id: Java.java 13113 2010-12-29 21:24:01Z ehuelsmann $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import static org.armedbear.lisp.Lisp.*;
37
38import java.beans.BeanInfo;
39import java.beans.IntrospectionException;
40import java.beans.Introspector;
41import java.beans.PropertyDescriptor;
42import java.lang.reflect.Array;
43import java.lang.reflect.Constructor;
44import java.lang.reflect.Field;
45import java.lang.reflect.InvocationTargetException;
46import java.lang.reflect.Method;
47import java.lang.reflect.Modifier;
48import java.util.*;
49
50public final class Java
51{
52    static final Map<Class,Symbol> registeredExceptions =
53       new HashMap<Class,Symbol>();
54
55    private static final LispClass java_exception = LispClass.findClass(Symbol.JAVA_EXCEPTION);
56
57    static boolean isJavaException(LispClass lc)
58    {
59        return lc.subclassp(java_exception);
60    }
61
62    private static final Primitive ENSURE_JAVA_OBJECT = new pf_ensure_java_object();
63    @DocString(name="ensure-java-object", args="obj",
64    doc="Ensures OBJ is wrapped in a JAVA-OBJECT, wrapping it if necessary.")
65    private static final class pf_ensure_java_object extends Primitive
66    {
67        pf_ensure_java_object() 
68        {
69            super("ensure-java-object", PACKAGE_JAVA, true);
70        }
71
72        @Override
73        public LispObject execute(LispObject obj) {
74      return obj instanceof JavaObject ? obj : new JavaObject(obj);
75        }
76    };
77
78    private static final Primitive REGISTER_JAVA_EXCEPTION = new pf_register_java_exception();
79    @DocString(name="register-java-exception", // => T
80    args="exception-name condition-symbol",
81    doc="Registers the Java Throwable named by the symbol EXCEPTION-NAME as the condition " +
82        "designated by CONDITION-SYMBOL.  Returns T if successful, NIL if not.")
83    private static final class pf_register_java_exception extends Primitive
84    {
85        pf_register_java_exception() 
86        {
87            super("register-java-exception", PACKAGE_JAVA, true);
88        }
89
90        @Override
91        public LispObject execute(LispObject className, LispObject symbol)
92
93        {
94            // FIXME Verify that CONDITION-SYMBOL is a symbol that names a condition.
95            // FIXME Signal a continuable error if the exception is already registered.
96            if ((symbol instanceof Symbol) && isJavaException(LispClass.findClass((Symbol) symbol))) {
97                registeredExceptions.put(classForName(className.getStringValue()),
98                                         (Symbol)symbol);
99                return T;
100            }
101            return NIL;
102        }
103    };
104
105    private static final Primitive UNREGISTER_JAVA_EXCEPTION = new pf_unregister_java_exception();
106    @DocString(name="unregister-java-exception", args="exception-name",
107    doc="Unregisters the Java Throwable EXCEPTION-NAME previously registered" +
108        " by REGISTER-JAVA-EXCEPTION.")
109    private static final class pf_unregister_java_exception extends Primitive
110    {
111        pf_unregister_java_exception() 
112        {
113            super("unregister-java-exception", PACKAGE_JAVA, true);
114        }
115
116        @Override
117        public LispObject execute(LispObject className)
118
119        {
120            // FIXME Verify that EXCEPTION-NAME designates a subclass of Throwable.
121            return registeredExceptions.remove(classForName(className.getStringValue())) == null ? NIL : T;
122        }
123    };
124
125    static Symbol getCondition(Class cl)
126    {
127  Class o = classForName("java.lang.Object");
128      for (Class c = cl ; c != o ; c = c.getSuperclass()) {
129            Object object = registeredExceptions.get(c);
130            if (object != null && isJavaException(LispClass.findClass((Symbol) object))) {
131                return (Symbol) object;
132            }
133        }
134        return null;
135    }
136
137    private static final Primitive JCLASS = new pf_jclass();
138    @DocString(name="jclass", args="name-or-class-ref &optional class-loader",
139    doc="Returns a reference to the Java class designated by" +
140        " NAME-OR-CLASS-REF. If the CLASS-LOADER parameter is passed, the" +
141        " class is resolved with respect to the given ClassLoader.")
142    private static final class pf_jclass extends Primitive
143    {
144
145        pf_jclass() 
146        {
147            super(Symbol.JCLASS);
148        }
149
150        @Override
151        public LispObject execute(LispObject arg)
152        {
153      return JavaObject.getInstance(javaClass(arg, JavaClassLoader.getCurrentClassLoader()));
154        }
155
156        @Override
157        public LispObject execute(LispObject className, LispObject classLoader)
158        {
159      ClassLoader loader = (ClassLoader) classLoader.javaInstance(ClassLoader.class);
160      return JavaObject.getInstance(javaClass(className, loader));
161        }
162    };
163
164    static final LispObject jfield(Primitive fun, LispObject[] args, boolean translate)
165
166    {
167        if (args.length < 2 || args.length > 4)
168            error(new WrongNumberOfArgumentsException(fun));
169        String fieldName = null;
170        Class c;
171        Field f;
172        Class fieldType;
173        Object instance = null;
174        try {
175            if (args[1] instanceof AbstractString) {
176                // Cases 1-5.
177                fieldName = args[1].getStringValue();
178                c = javaClass(args[0]);
179            } else {
180                // Cases 6 and 7.
181                fieldName = args[0].getStringValue();
182                instance = JavaObject.getObject(args[1]);
183                c = instance.getClass();
184            }
185            f = c.getField(fieldName);
186            fieldType = f.getType();
187            switch (args.length) {
188                case 2:
189                    // Cases 1 and 6.
190                    break;
191                case 3:
192                    // Cases 2,3, and 7.
193                    if (instance == null) {
194                        // Cases 2 and 3.
195                        if (args[2] instanceof JavaObject) {
196                            // Case 2.
197                            instance = JavaObject.getObject(args[2]);
198                            break;
199                        } else {
200                            // Case 3.
201                            f.set(null,args[2].javaInstance(fieldType));
202                            return args[2];
203                        }
204                    } else {
205                        // Case 7.
206                        f.set(instance,args[2].javaInstance(fieldType));
207                        return args[2];
208                    }
209                case 4:
210                    // Cases 4 and 5.
211                    if (args[2] != NIL) {
212                        // Case 4.
213                        instance = JavaObject.getObject(args[2]);
214                    }
215                    f.set(instance,args[3].javaInstance(fieldType));
216                    return args[3];
217            }
218            return JavaObject.getInstance(f.get(instance), translate, f.getType());
219        }
220        catch (NoSuchFieldException e) {
221            error(new LispError("no such field"));
222        }
223        catch (SecurityException e) {
224            error(new LispError("inaccessible field"));
225        }
226        catch (IllegalAccessException e) {
227            error(new LispError("illegal access"));
228        }
229        catch (IllegalArgumentException e) {
230            error(new LispError("illegal argument"));
231        }
232        catch (Throwable t) { // no code -> no ControlTransfer
233            error(new LispError(getMessage(t)));
234        }
235        // Not reached.
236        return NIL;
237    }
238
239
240    private static final Primitive JFIELD = new pf_jfield();
241    @DocString(name="jfield",
242    args="class-ref-or-field field-or-instance &optional instance value",
243    doc="Retrieves or modifies a field in a Java class or instance.\n\n"+
244        "Supported argument patterns:\n\n"+
245        "   Case 1: class-ref  field-name:\n"+
246        "      Retrieves the value of a static field.\n\n"+
247        "   Case 2: class-ref  field-name  instance-ref:\n"+
248        "      Retrieves the value of a class field of the instance.\n\n"+
249        "   Case 3: class-ref  field-name  primitive-value:\n"+
250        "      Stores a primitive-value in a static field.\n\n"+
251        "   Case 4: class-ref  field-name  instance-ref  value:\n"+
252        "      Stores value in a class field of the instance.\n\n"+
253        "   Case 5: class-ref  field-name  nil  value:\n"+
254        "      Stores value in a static field (when value may be\n"+
255        "      confused with an instance-ref).\n\n"+
256        "   Case 6: field-name  instance:\n"+
257        "      Retrieves the value of a field of the instance. The\n"+
258        "      class is derived from the instance.\n\n"+
259        "   Case 7: field-name  instance  value:\n"+
260        "      Stores value in a field of the instance. The class is\n"+
261        "      derived from the instance.\n\n"
262        )
263    private static final class pf_jfield extends Primitive
264    {
265        pf_jfield() 
266        {
267            super("jfield", PACKAGE_JAVA, true);
268        }
269
270        @Override
271        public LispObject execute(LispObject[] args)
272        {
273            return jfield(this, args, true);
274        }
275    };
276
277    private static final Primitive JFIELD_RAW = new pf_jfield_raw();
278    @DocString(name="jfield",
279    args="class-ref-or-field field-or-instance &optional instance value",
280    doc="Retrieves or modifies a field in a Java class or instance. Does not\n"+
281        "attempt to coerce its value or the result into a Lisp object.\n\n"+
282        "Supported argument patterns:\n\n"+
283        "   Case 1: class-ref  field-name:\n"+
284        "      Retrieves the value of a static field.\n\n"+
285        "   Case 2: class-ref  field-name  instance-ref:\n"+
286        "      Retrieves the value of a class field of the instance.\n\n"+
287        "   Case 3: class-ref  field-name  primitive-value:\n"+
288        "      Stores a primitive-value in a static field.\n\n"+
289        "   Case 4: class-ref  field-name  instance-ref  value:\n"+
290        "      Stores value in a class field of the instance.\n\n"+
291        "   Case 5: class-ref  field-name  nil  value:\n"+
292        "      Stores value in a static field (when value may be\n"+
293        "      confused with an instance-ref).\n\n"+
294        "   Case 6: field-name  instance:\n"+
295        "      Retrieves the value of a field of the instance. The\n"+
296        "      class is derived from the instance.\n\n"+
297        "   Case 7: field-name  instance  value:\n"+
298        "      Stores value in a field of the instance. The class is\n"+
299        "      derived from the instance.\n\n"
300        )
301    private static final class pf_jfield_raw extends Primitive
302    {
303        pf_jfield_raw() 
304        {
305            super("jfield-raw", PACKAGE_JAVA, true);
306        }
307
308        @Override
309        public LispObject execute(LispObject[] args)
310        {
311            return jfield(this, args, false);
312        }
313    };
314
315    private static final Primitive JCONSTRUCTOR = new pf_jconstructor();
316    @DocString(name="jconstructor", args="class-ref &rest parameter-class-refs",
317    doc="Returns a reference to the Java constructor of CLASS-REF with the" +
318        " given PARAMETER-CLASS-REFS.")
319    private static final class pf_jconstructor extends Primitive
320    {
321        pf_jconstructor() 
322        {
323            super("jconstructor", PACKAGE_JAVA, true);
324        }
325
326        @Override
327        public LispObject execute(LispObject[] args)
328        {
329            if (args.length < 1)
330                error(new WrongNumberOfArgumentsException(this));
331            try {
332                final Class<?> c = javaClass(args[0]);
333                int argCount = 0;
334                if (args.length == 2 && args[1] instanceof Fixnum) {
335                    argCount = Fixnum.getValue(args[1]);
336                } else {
337                    Class<?>[] parameterTypes = new Class[args.length-1];
338                    for (int i = 1; i < args.length; i++) {
339                        parameterTypes[i-1] = javaClass(args[i]);
340                    }
341                    return JavaObject.getInstance(c.getConstructor(parameterTypes));
342                }
343                // Parameter types not explicitly specified.
344                Constructor[] constructors = c.getConstructors();
345                for (int i = 0; i < constructors.length; i++) {
346                    Constructor constructor = constructors[i];
347                    if (constructor.getParameterTypes().length == argCount)
348                        return JavaObject.getInstance(constructor);
349                }
350                throw new NoSuchMethodException();
351            }
352            catch (NoSuchMethodException e) {
353                error(new LispError("no such constructor"));
354            }
355            catch (ControlTransfer e) {
356                throw e;
357            }
358            catch (Throwable t) { // ControlTransfer addressed above
359                error(new LispError(getMessage(t)));
360            }
361            // Not reached.
362            return NIL;
363        }
364    };
365
366    private static final Primitive JMETHOD = new pf_jmethod();
367
368    @DocString(name="jmethod", args="class-ref method-name &rest parameter-class-refs",
369    doc="Returns a reference to the Java method METHOD-NAME of CLASS-REF with the" +
370        " given PARAMETER-CLASS-REFS.")
371    private static final class pf_jmethod extends Primitive
372    {
373        pf_jmethod() 
374        {
375            super("jmethod", PACKAGE_JAVA, true);
376        }
377
378        @Override
379        public LispObject execute(LispObject[] args)
380        {
381            if (args.length < 2)
382                error(new WrongNumberOfArgumentsException(this));
383            final Class<?> c = javaClass(args[0]);
384            String methodName = args[1].getStringValue();
385            try {
386                int argCount = 0;
387                if (args.length == 3 && args[2] instanceof Fixnum) {
388                    argCount = ((Fixnum)args[2]).value;
389                } else {
390                    Class<?>[] parameterTypes = new Class[args.length-2];
391                    for (int i = 2; i < args.length; i++)
392                        parameterTypes[i-2] = javaClass(args[i]);
393                    return JavaObject.getInstance(c.getMethod(methodName,
394                                                              parameterTypes));
395                }
396                // Parameter types were not explicitly specified.
397                Method[] methods = c.getMethods();
398                for (int i = 0; i < methods.length; i++) {
399                    Method method = methods[i];
400                    if (method.getName().equals(methodName) &&
401                        method.getParameterTypes().length == argCount)
402                        return JavaObject.getInstance(method);
403                }
404                throw new NoSuchMethodException();
405            }
406            catch (NoSuchMethodException e) {
407                StringBuilder sb = new StringBuilder("No such method: ");
408                sb.append(c.getName());
409                sb.append('.');
410                sb.append(methodName);
411                sb.append('(');
412                for (int i = 2; i < args.length; i++) {
413                    sb.append(args[i].writeToString());
414                    if (i < args.length - 1)
415                        sb.append(',');
416                }
417                sb.append(')');
418                error(new LispError(sb.toString()));
419            }
420            catch (ControlTransfer e) {
421                throw e;
422            }
423            catch (Throwable t) { // ControlTransfer addressed above
424                error(new LispError(getMessage(t)));
425            }
426            // Not reached.
427            return NIL;
428        }
429    };
430
431    static final LispObject jstatic(Primitive fun, LispObject[] args, boolean translate)
432
433    {
434        if (args.length < 2)
435            error(new WrongNumberOfArgumentsException(fun));
436        try {
437            Method m = null;
438            LispObject methodRef = args[0];
439            if (methodRef instanceof JavaObject) {
440                Object obj = ((JavaObject)methodRef).getObject();
441                if (obj instanceof Method)
442                    m = (Method) obj;
443            } else if (methodRef instanceof AbstractString) {
444                Class c = javaClass(args[1]);
445                if (c != null) {
446                    String methodName = methodRef.getStringValue();
447                    Method[] methods = c.getMethods();
448        List<Method> staticMethods = new ArrayList<Method>();
449                    int argCount = args.length - 2;
450        for(Method m1 : methods) {
451      if(Modifier.isStatic(m1.getModifiers())) {
452          staticMethods.add(m1);
453      }
454        }
455        if(staticMethods.size() > 0) {
456      m = findMethod(staticMethods.toArray(new Method[staticMethods.size()]), methodName, args);
457        }
458                    if (m == null)
459                        error(new LispError("no such method"));
460                }
461            } else
462                error(new TypeError("wrong type: " + methodRef));
463            Object[] methodArgs = new Object[args.length-2];
464            Class[] argTypes = m.getParameterTypes();
465            for (int i = 2; i < args.length; i++) {
466                LispObject arg = args[i];
467                if (arg == NIL)
468                    methodArgs[i-2] = null;
469                else
470                    methodArgs[i-2] = arg.javaInstance(argTypes[i-2]);
471            }
472            Object result = m.invoke(null, methodArgs);
473      return JavaObject.getInstance(result, translate, m.getReturnType());
474        }
475        catch (ControlTransfer c) {
476            throw c;
477        }
478        catch (Throwable t) { // ControlTransfer handled above
479            if (t instanceof InvocationTargetException)
480                t = t.getCause();
481            Symbol condition = getCondition(t.getClass());
482            if (condition == null)
483                error(new JavaException(t));
484            else
485                Symbol.SIGNAL.execute(
486                    condition,
487                    Keyword.CAUSE,
488                    JavaObject.getInstance(t),
489                    Keyword.FORMAT_CONTROL,
490                    new SimpleString(getMessage(t)));
491        }
492        // Not reached.
493        return NIL;
494    }
495
496    private static final Primitive JSTATIC = new pf_jstatic();
497    @DocString(name="jstatic", args="method class &rest args",
498    doc="Invokes the static method METHOD on class CLASS with ARGS.")
499    private static final class pf_jstatic extends Primitive
500    {
501        pf_jstatic() 
502        {
503            super("jstatic", PACKAGE_JAVA, true);
504        }
505
506        @Override
507        public LispObject execute(LispObject[] args)
508        {
509            return jstatic(this, args, true);
510        }
511    };
512
513    private static final Primitive JSTATIC_RAW = new pf_jstatic_raw();
514    @DocString(name="jstatic-raw", args="method class &rest args",
515    doc="Invokes the static method METHOD on class CLASS with ARGS. Does not "+
516        "attempt to coerce the arguments or result into a Lisp object.")
517    private static final class pf_jstatic_raw extends Primitive
518    {
519        pf_jstatic_raw() 
520        {
521            super("jstatic-raw", PACKAGE_JAVA, true);
522        }
523
524        @Override
525        public LispObject execute(LispObject[] args)
526        {
527            return jstatic(this, args, false);
528        }
529    };
530
531    private static final Primitive JNEW = new pf_jnew();
532    @DocString(name="jnew", args="constructor &rest args",
533    doc="Invokes the Java constructor CONSTRUCTOR with the arguments ARGS.")
534    private static final class pf_jnew extends Primitive
535    {
536        pf_jnew()
537        {
538            super("jnew", PACKAGE_JAVA, true);
539        }
540
541        @Override
542        public LispObject execute(LispObject[] args)
543        {
544            if (args.length < 1)
545                error(new WrongNumberOfArgumentsException(this));
546            LispObject classRef = args[0];
547            try {
548                Constructor constructor;
549    if(classRef instanceof AbstractString) {
550        constructor = findConstructor(javaClass(classRef), args);
551    } else {
552        Object object = JavaObject.getObject(classRef);
553        if(object instanceof Constructor) {
554      constructor = (Constructor) object;
555        } else if(object instanceof Class<?>) {
556      constructor = findConstructor((Class<?>) object, args);
557        } else {
558      return error(new LispError(classRef.writeToString() + " is neither a Constructor nor a Class"));
559        }
560    }
561                Class[] argTypes = constructor.getParameterTypes();
562                Object[] initargs = new Object[args.length-1];
563                for (int i = 1; i < args.length; i++) {
564                    LispObject arg = args[i];
565                    if (arg == NIL)
566                        initargs[i-1] = null;
567                    else {
568                        initargs[i-1] = arg.javaInstance(argTypes[i-1]);
569                    }
570                }
571                return JavaObject.getInstance(constructor.newInstance(initargs));
572            }
573            catch (ControlTransfer c) {
574                throw c;
575            }
576            catch (Throwable t) { // ControlTransfer handled above
577                if (t instanceof InvocationTargetException)
578                    t = t.getCause();
579                Symbol condition = getCondition(t.getClass());
580                if (condition == null)
581                    error(new JavaException(t));
582                else
583                    Symbol.SIGNAL.execute(
584                        condition,
585                        Keyword.CAUSE,
586                        JavaObject.getInstance(t),
587                        Keyword.FORMAT_CONTROL,
588                        new SimpleString(getMessage(t)));
589            }
590            // Not reached.
591            return NIL;
592        }
593    };
594
595    private static final Primitive JNEW_ARRAY = new pf_jnew_array();
596    @DocString(name="jnew-array", args="element-type &rest dimensions",
597    doc="Creates a new Java array of type ELEMENT-TYPE, with the given" +
598        " DIMENSIONS.")
599    private static final class pf_jnew_array extends Primitive
600    {
601        pf_jnew_array()
602        {
603            super("jnew-array", PACKAGE_JAVA, true);
604        }
605
606        @Override
607        public LispObject execute(LispObject[] args)
608        {
609            if (args.length < 2)
610                error(new WrongNumberOfArgumentsException(this));
611            try {
612                Class c = javaClass(args[0]);
613                int[] dimensions = new int[args.length - 1];
614                for (int i = 1; i < args.length; i++)
615                    dimensions[i-1] = ((Integer)args[i].javaInstance()).intValue();
616                return JavaObject.getInstance(Array.newInstance(c, dimensions));
617            }
618            catch (Throwable t) { // no code -> no ControlTransfer
619                error(new JavaException(t));
620            }
621            // Not reached.
622            return NIL;
623        }
624    };
625
626    static final LispObject jarray_ref(Primitive fun, LispObject[] args, boolean translate)
627
628    {
629        if (args.length < 2)
630            error(new WrongNumberOfArgumentsException(fun));
631        try {
632            Object a = args[0].javaInstance();
633            for (int i = 1; i<args.length - 1; i++)
634                a = Array.get(a, ((Integer)args[i].javaInstance()).intValue());
635            return JavaObject.getInstance(Array.get(a,
636                    ((Integer)args[args.length - 1].javaInstance()).intValue()), translate);
637        }
638        catch (Throwable t) { // no code -> no ControlTransfer
639            Symbol condition = getCondition(t.getClass());
640            if (condition == null)
641                error(new JavaException(t));
642            else
643                Symbol.SIGNAL.execute(
644                    condition,
645                    Keyword.CAUSE,
646                    JavaObject.getInstance(t),
647                    Keyword.FORMAT_CONTROL,
648                    new SimpleString(getMessage(t)));
649        }
650        // Not reached.
651        return NIL;
652    }
653
654    private static final Primitive JARRAY_REF = new pf_jarray_ref();
655    @DocString(name="jarray-ref", args="java-array &rest indices",
656    doc="Dereferences the Java array JAVA-ARRAY using the given INDICIES, " +
657        "coercing the result into a Lisp object, if possible.")
658    private static final class pf_jarray_ref extends Primitive
659    {
660        pf_jarray_ref()
661        {
662            super("jarray-ref", PACKAGE_JAVA, true);
663        }
664
665        @Override
666        public LispObject execute(LispObject[] args)
667        {
668            return jarray_ref(this, args, true);
669        }
670    };
671
672    private static final Primitive JARRAY_REF_RAW = new pf_jarray_ref_raw();
673    @DocString(name="jarray-ref-raw", args="java-array &rest indices",
674    doc="Dereference the Java array JAVA-ARRAY using the given INDICIES. " +
675        "Does not attempt to coerce the result into a Lisp object.")
676    private static final class pf_jarray_ref_raw extends Primitive
677    {
678        pf_jarray_ref_raw() 
679        {
680            super("jarray-ref-raw", PACKAGE_JAVA, true);
681        }
682
683        @Override
684        public LispObject execute(LispObject[] args)
685        {
686            return jarray_ref(this, args, false);
687        }
688    };
689
690    private static final Primitive JARRAY_SET = new pf_jarray_set();
691    @DocString(name="jarray-set", args="java-array new-value &rest indices",
692    doc="Stores NEW-VALUE at the given index in JAVA-ARRAY.")
693    private static final class pf_jarray_set extends Primitive
694    {
695        pf_jarray_set()
696        {
697            super("jarray-set", PACKAGE_JAVA, true);
698        }
699
700        @Override
701        public LispObject execute(LispObject[] args)
702        {
703            if (args.length < 3)
704                error(new WrongNumberOfArgumentsException(this));
705            try {
706                Object a = args[0].javaInstance();
707                LispObject v = args[1];
708                for (int i = 2; i<args.length - 1; i++)
709                    a = Array.get(a, ((Integer)args[i].javaInstance()).intValue());
710                Array.set(a, ((Integer)args[args.length - 1].javaInstance()).intValue(), v.javaInstance());
711                return v;
712            }
713            catch (Throwable t) { // no code -> no ControlTransfer
714                Symbol condition = getCondition(t.getClass());
715                if (condition == null)
716                    error(new JavaException(t));
717                else
718                    Symbol.SIGNAL.execute(
719                        condition,
720                        Keyword.CAUSE,
721                        JavaObject.getInstance(t),
722                        Keyword.FORMAT_CONTROL,
723                        new SimpleString(getMessage(t)));
724            }
725            // Not reached.
726            return NIL;
727        }
728    };
729
730    /**  Calls makeLispObject() to convert the result to an appropriate Lisp type. */
731    private static final Primitive JCALL = new pf_jcall();
732    @DocString(name="jcall", args="method-ref instance &rest args",
733    doc="Invokes the Java method METHOD-REF on INSTANCE with arguments ARGS," +
734        " coercing the result into a Lisp object, if possible.")
735    private static final class pf_jcall extends Primitive
736    {
737        pf_jcall()
738        {
739            super(Symbol.JCALL);
740        }
741
742        @Override
743        public LispObject execute(LispObject[] args)
744        {
745            return jcall(this, args, true);
746        }
747    };
748
749    /**
750     * Does no type conversion. The result of the call is simply wrapped in a
751     *   JavaObject.
752     */
753    private static final Primitive JCALL_RAW = new pf_jcall_raw();
754    @DocString(name="jcall-raw", args="method-ref instance &rest args",
755    doc="Invokes the Java method METHOD-REF on INSTANCE with arguments ARGS." +
756        " Does not attempt to coerce the result into a Lisp object.")
757    private static final class pf_jcall_raw extends Primitive
758    {
759        pf_jcall_raw()
760        {
761            super(Symbol.JCALL_RAW);
762        }
763
764        @Override
765        public LispObject execute(LispObject[] args)
766        {
767            return jcall(this, args, false);
768        }
769    };
770
771    static LispObject jcall(Primitive fun, LispObject[] args, boolean translate)
772
773    {
774        if (args.length < 2)
775            error(new WrongNumberOfArgumentsException(fun));
776        try {
777      final LispObject methodArg = args[0];
778      final LispObject instanceArg = args[1];
779      final Object instance;
780      Class<?> intendedClass = null;
781      if (instanceArg instanceof AbstractString) {
782    instance = instanceArg.getStringValue();
783      } else if (instanceArg instanceof JavaObject) {
784    JavaObject jobj = ((JavaObject)instanceArg);
785    instance = jobj.getObject();
786    intendedClass = jobj.getIntendedClass();
787      } else {
788    instance = instanceArg.javaInstance();
789      }
790      if(instance == null) {
791    throw new NullPointerException(); //Handled below
792      }
793            Method method;
794      Object[] methodArgs;
795            if (methodArg instanceof AbstractString) {
796    methodArgs = translateMethodArguments(args, 2);
797                String methodName = methodArg.getStringValue();
798    if(intendedClass == null) {
799        intendedClass = instance.getClass();
800    }
801                method = findMethod(intendedClass, methodName, methodArgs);
802    Class actualClass = null;
803    if(method == null) {       
804        actualClass = instance.getClass();
805        if(intendedClass != actualClass &&
806           Modifier.isPublic(actualClass.getModifiers())) {
807      method = findMethod(actualClass, methodName, methodArgs);
808        }
809    }
810    if (method == null) {
811        String classes = intendedClass.getName();
812        if(actualClass != null && actualClass != intendedClass) {
813      classes += " or " + actualClass.getName();
814        }
815        throw new NoSuchMethodException("No applicable method named " + methodName + " found in " + classes);
816    }
817
818            } else
819                method = (Method) JavaObject.getObject(methodArg);
820            Class<?>[] argTypes = (Class<?>[])method.getParameterTypes();
821      if(argTypes.length != args.length - 2) {
822    return error(new WrongNumberOfArgumentsException("Wrong number of arguments for " + method + ": expected " + argTypes.length + ", got " + (args.length - 2)));
823      }
824            methodArgs = new Object[argTypes.length];
825            for (int i = 2; i < args.length; i++) {
826                LispObject arg = args[i];
827                if (arg == NIL)
828                    methodArgs[i-2] = null;
829                else
830                    methodArgs[i-2] = arg.javaInstance(argTypes[i-2]);
831            }
832            return JavaObject.getInstance(method.invoke(instance, methodArgs),
833                                          translate,
834            method.getReturnType());
835        }
836        catch (ControlTransfer t) {
837            throw t;
838        }
839        catch (Throwable t) { // ControlTransfer handled above
840            if (t instanceof InvocationTargetException)
841                t = t.getCause();
842            Symbol condition = getCondition(t.getClass());
843            if (condition == null)
844                error(new JavaException(t));
845            else
846                Symbol.SIGNAL.execute(
847                    condition,
848                    Keyword.CAUSE,
849                    JavaObject.getInstance(t),
850                    Keyword.FORMAT_CONTROL,
851                    new SimpleString(getMessage(t)));
852        }
853        // Not reached.
854        return null;
855    }
856
857    private static Object[] translateMethodArguments(LispObject[] args) {
858  return translateMethodArguments(args, 0);
859    }
860
861    private static Object[] translateMethodArguments(LispObject[] args, int offs) {
862  int argCount = args.length - offs;
863        Object[] javaArgs = new Object[argCount];
864        for (int i = 0; i < argCount; ++i) {
865            Object x = args[i + offs];
866            if (x == NIL) {
867                javaArgs[i] = null;
868            } else {
869                javaArgs[i] = ((LispObject) x).javaInstance();
870            }
871        }
872  return javaArgs;
873    }
874
875    private static Method findMethod(Method[] methods, String methodName, Object[] javaArgs) {
876  int argCount = javaArgs.length;
877        Method result = null;
878        for (int i = methods.length; i-- > 0;) {
879            Method method = methods[i];
880            if (!method.getName().equals(methodName)) {
881                continue;
882            }
883            if (method.getParameterTypes().length != argCount) {
884                continue;
885            }
886            Class<?>[] methodTypes = (Class<?>[]) method.getParameterTypes();
887            if (!isApplicableMethod(methodTypes, javaArgs)) {
888                continue;
889            }
890            if (result == null || isMoreSpecialized(methodTypes, result.getParameterTypes())) {
891                result = method;
892            }
893        }
894        return result;
895    }
896
897    private static Method findMethod(Class<?> c, String methodName, Object[] javaArgs) {
898        Method[] methods = c.getMethods();
899  return findMethod(methods, methodName, javaArgs);
900    }
901
902    private static Method findMethod(Class<?> c, String methodName, LispObject[] args) {
903        Object[] javaArgs = translateMethodArguments(args, 2);
904  return findMethod(c, methodName, javaArgs);
905    }
906
907    private static Method findMethod(Method[] methods, String methodName, LispObject[] args) {
908        Object[] javaArgs = translateMethodArguments(args, 2);
909  return findMethod(methods, methodName, javaArgs);
910    }
911
912    static Constructor findConstructor(Class<?> c, LispObject[] args) throws NoSuchMethodException {
913  int argCount = args.length - 1;
914        Object[] javaArgs = translateMethodArguments(args, 1);
915        Constructor[] ctors = c.getConstructors();
916        Constructor result = null;
917        for (int i = ctors.length; i-- > 0;) {
918            Constructor ctor = ctors[i];
919            if (ctor.getParameterTypes().length != argCount) {
920                continue;
921            }
922            Class<?>[] methodTypes = (Class<?>[]) ctor.getParameterTypes();
923            if (!isApplicableMethod(methodTypes, javaArgs)) {
924                continue;
925            }
926            if (result == null || isMoreSpecialized(methodTypes, result.getParameterTypes())) {
927                result = ctor;
928            }
929        }
930        if (result == null) {
931      StringBuilder sb = new StringBuilder(c.getSimpleName());
932      sb.append('(');
933      boolean first = true;
934      for(Object o : javaArgs) {
935    if(first) {
936        first = false;
937    } else {
938        sb.append(", ");
939    }
940    if(o != null) {
941        sb.append(o.getClass().getName());
942    } else {
943        sb.append("<null>");
944    }
945      }
946      sb.append(')');
947            throw new NoSuchMethodException(sb.toString());
948        }
949        return result;
950    }
951
952    private static boolean isApplicableMethod(Class<?>[] methodTypes,
953            Object[] args) {
954        for (int i = 0; i < methodTypes.length; ++i) {
955            Class<?> methodType = methodTypes[i];
956            Object arg = args[i];
957            if (methodType.isPrimitive()) {
958                Class<?> x = getBoxedClass(methodType);
959                if (!x.isInstance(arg)) {
960                    return false;
961                }
962            } else if (arg != null && !methodType.isInstance(arg)) {
963                return false;
964            }
965        }
966        return true;
967    }
968
969    private static boolean isMoreSpecialized(Class<?>[] xtypes, Class<?>[] ytypes) {
970        for (int i = 0; i < xtypes.length; ++i) {
971            Class<?> xtype = xtypes[i];
972            if (xtype.isPrimitive()) {
973                xtype = getBoxedClass(xtype);
974            }
975            Class<?> ytype = ytypes[i];
976            if (ytype.isPrimitive()) {
977                ytype = getBoxedClass(ytype);
978            }
979            if (xtype.equals(ytype)) {
980                continue;
981            }
982            if (ytype.isAssignableFrom(xtype)) {
983                return true;
984            }
985        }
986        return false;
987    }
988
989    public static Class<?> maybeBoxClass(Class<?> clazz) {
990  if(clazz.isPrimitive()) {
991      return getBoxedClass(clazz);
992  } else {
993      return clazz;
994  }
995    }
996   
997    private static Class<?> getBoxedClass(Class<?> clazz) {
998        if (clazz.equals(int.class)) {
999            return Integer.class;
1000        } else if (clazz.equals(boolean.class)) {
1001            return Boolean.class;
1002        } else if (clazz.equals(byte.class)) {
1003            return Byte.class;
1004        } else if (clazz.equals(char.class)) {
1005            return Character.class;
1006        } else if (clazz.equals(long.class)) {
1007            return Long.class;
1008        } else if (clazz.equals(float.class)) {
1009            return Float.class;
1010        } else if (clazz.equals(double.class)) {
1011            return Double.class;
1012        } else if (clazz.equals(short.class)) {
1013            return Short.class;
1014        } else { // if (methodType.equals(void.class))
1015            return Void.class;
1016        }
1017    }
1018
1019    private static final Primitive MAKE_IMMEDIATE_OBJECT = new pf_make_immediate_object();
1020    @DocString(name="make-immediate-object", args="object &optional type",
1021    doc="Attempts to coerce a given Lisp object into a java-object of the\n"+
1022        "given type.  If type is not provided, works as jobject-lisp-value.\n"+
1023        "Currently, type may be :BOOLEAN, treating the object as a truth value,\n"+
1024        "or :REF, which returns Java null if NIL is provided.")
1025    private static final class pf_make_immediate_object extends Primitive
1026    {
1027        pf_make_immediate_object()
1028        {
1029            super("make-immediate-object", PACKAGE_JAVA, true);
1030        }
1031
1032        @Override
1033        public LispObject execute(LispObject[] args)
1034        {
1035            if (args.length < 1)
1036                error(new WrongNumberOfArgumentsException(this));
1037            LispObject object = args[0];
1038            if (args.length > 1) {
1039                LispObject type = args[1];
1040                if (type == Keyword.BOOLEAN) {
1041                    if (object == NIL)
1042                        return JavaObject.getInstance(Boolean.FALSE);
1043                    else
1044                        return JavaObject.getInstance(Boolean.TRUE);
1045                }
1046                if (type == Keyword.REF) {
1047                    if (object == NIL)
1048                        return JavaObject.getInstance(null);
1049                    else
1050                        error(new LispError("MAKE-IMMEDIATE-OBJECT: not implemented"));
1051                }
1052                // other special cases come here
1053            }
1054            return JavaObject.getInstance(object.javaInstance());
1055        }
1056    };
1057
1058    private static final Primitive JNULL_REF_P = new pf_jnull_ref_p();
1059    @DocString(name="jnull-ref-p", args="object",
1060    doc="Returns a non-NIL value when the JAVA-OBJECT `object` is `null`,\n"
1061            + "or signals a TYPE-ERROR condition if the object isn't of\n"
1062            + "the right type.")
1063    private static final class pf_jnull_ref_p extends Primitive
1064    {
1065        pf_jnull_ref_p()
1066        {
1067            super("jnull-ref-p", PACKAGE_JAVA, true);
1068        }
1069
1070        @Override
1071        public LispObject execute(LispObject ref)
1072        {
1073            if (ref instanceof JavaObject)
1074            {
1075                JavaObject jref = (JavaObject)ref;
1076                return (jref.javaInstance() == null) ? T : NIL;
1077            } else
1078                return Lisp.type_error(ref, Symbol.JAVA_OBJECT);
1079        }
1080    };
1081
1082
1083    private static final Primitive JAVA_OBJECT_P = new pf_java_object_p();
1084    @DocString(name="java-object-p", args="object",
1085    doc="Returns T if OBJECT is a JAVA-OBJECT.")
1086    private static final class pf_java_object_p extends Primitive
1087    {
1088        pf_java_object_p() 
1089        {
1090            super("java-object-p", PACKAGE_JAVA, true);
1091        }
1092
1093        @Override
1094        public LispObject execute(LispObject arg)
1095        {
1096            return (arg instanceof JavaObject) ? T : NIL;
1097        }
1098    };
1099
1100    private static final Primitive JOBJECT_LISP_VALUE = new pf_jobject_lisp_value();
1101    @DocString(name="jobject-lisp-value", args="java-object",
1102    doc="Attempts to coerce JAVA-OBJECT into a Lisp object.")
1103    private static final class pf_jobject_lisp_value extends Primitive
1104    {
1105        pf_jobject_lisp_value()
1106        {
1107            super("jobject-lisp-value", PACKAGE_JAVA, true, "java-object");
1108        }
1109
1110        @Override
1111        public LispObject execute(LispObject arg)
1112        {
1113            return JavaObject.getInstance(arg.javaInstance(), true);
1114        }
1115    };
1116
1117    private static final Primitive JCOERCE = new pf_jcoerce();
1118    @DocString(name="jcoerce", args="object intended-class",
1119    doc="Attempts to coerce OBJECT into a JavaObject of class INTENDED-CLASS." +
1120        "  Raises a TYPE-ERROR if no conversion is possible.")
1121    private static final class pf_jcoerce extends Primitive
1122    {
1123        pf_jcoerce()
1124        {
1125            super("jcoerce", PACKAGE_JAVA, true);
1126        }
1127
1128        @Override
1129        public LispObject execute(LispObject javaObject, LispObject intendedClass)
1130        {
1131      Object o = javaObject.javaInstance();
1132      Class<?> c = javaClass(intendedClass);
1133      try {
1134    return JavaObject.getInstance(o, c);
1135      } catch(ClassCastException e) {
1136    return error(new TypeError(javaObject, new SimpleString(c.getName())));
1137      }
1138        }
1139    };
1140
1141    private static final Primitive JGET_PROPERTY_VALUE = new pf__jget_property_value();
1142    @DocString(name="%jget-propety-value", args="java-object property-name",
1143    doc="Gets a JavaBeans property on JAVA-OBJECT.\n" +
1144        "SYSTEM-INTERNAL: Use jproperty-value instead.")
1145    private static final class pf__jget_property_value extends Primitive
1146    {
1147        pf__jget_property_value() 
1148        {
1149      super("%jget-property-value", PACKAGE_JAVA, true,
1150                  "java-object property-name");
1151        }
1152     
1153        @Override
1154        public LispObject execute(LispObject javaObject, LispObject propertyName) {
1155      try {
1156        Object obj = javaObject.javaInstance();
1157        PropertyDescriptor pd = getPropertyDescriptor(obj, propertyName);
1158        Object value = pd.getReadMethod().invoke(obj);
1159        if(value instanceof LispObject) {
1160            return (LispObject) value;
1161        } else if(value != null) {
1162            return JavaObject.getInstance(value, true);
1163        } else {
1164            return NIL;
1165        }
1166      } catch (Exception e) {
1167                return error(new JavaException(e));
1168      }
1169        }
1170    };
1171   
1172    private static final Primitive JSET_PROPERTY_VALUE = new pf__jset_property_value();
1173    @DocString(name="%jset-propety-value", args="java-object property-name value",
1174    doc="Sets a JavaBean property on JAVA-OBJECT.\n" +
1175        "SYSTEM-INTERNAL: Use (setf jproperty-value) instead.")
1176    private static final class pf__jset_property_value extends Primitive
1177    {
1178        pf__jset_property_value()
1179        {
1180      super("%jset-property-value", PACKAGE_JAVA, true,
1181                  "java-object property-name value");
1182        }
1183     
1184        @Override
1185        public LispObject execute(LispObject javaObject, LispObject propertyName, LispObject value) {
1186      Object obj = null;
1187      try {
1188    obj = javaObject.javaInstance();
1189    PropertyDescriptor pd = getPropertyDescriptor(obj, propertyName);
1190    Object jValue;
1191    //TODO maybe we should do this in javaInstance(Class)
1192    if(value instanceof JavaObject) {
1193        jValue = value.javaInstance();
1194    } else {
1195        if(Boolean.TYPE.equals(pd.getPropertyType()) ||
1196           Boolean.class.equals(pd.getPropertyType())) {
1197      jValue = value != NIL;
1198        } else {
1199      jValue = value != NIL ? value.javaInstance() : null;
1200        }
1201    }
1202    pd.getWriteMethod().invoke(obj, jValue);
1203    return value;
1204      } catch (Exception e) {
1205            return error(new JavaException(e));
1206      }
1207        }
1208    };
1209
1210    private static final Primitive JRUN_EXCEPTION_PROTECTED = new pf_jrun_exception_protection();
1211    @DocString(name="jrun-exception-protected", args="closure",
1212    doc="Invokes the function CLOSURE and returns the result.  "+
1213        "Signals an error if stack or heap exhaustion occurs.")
1214    private static final class pf_jrun_exception_protection extends Primitive
1215    {
1216        pf_jrun_exception_protection()
1217        {
1218            super("jrun-exception-protected", PACKAGE_JAVA, true);
1219        }
1220
1221        @Override
1222        public LispObject execute(LispObject closure) {
1223            Function fun = checkFunction(closure);
1224
1225            try {
1226                return LispThread.currentThread().execute(closure);
1227            }
1228            catch (OutOfMemoryError oom) {
1229                return error(new StorageCondition("Out of memory " + oom.getMessage()));
1230            }
1231            catch (StackOverflowError oos) {
1232                return error(new StorageCondition("Stack overflow."));
1233            }
1234        }
1235    };
1236
1237    static PropertyDescriptor getPropertyDescriptor(Object obj, LispObject propertyName) throws IntrospectionException {
1238        String prop = ((AbstractString) propertyName).getStringValue();
1239        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
1240        for(PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
1241          if(pd.getName().equals(prop)) {
1242            return pd;
1243          }
1244        }
1245        error(new LispError("Property " + prop + " not found in " + obj));
1246
1247        return null; // not reached
1248    }
1249   
1250    private static Class classForName(String className) {
1251  return classForName(className, JavaClassLoader.getPersistentInstance());
1252    }
1253
1254    private static Class classForName(String className, ClassLoader classLoader) {
1255        try {
1256            return Class.forName(className, true, classLoader);
1257        }
1258        catch (ClassNotFoundException e) {
1259      error(new LispError("Class not found: " + className));
1260      // Not reached.
1261      return null;
1262        }
1263    }
1264
1265    private static Class javaClass(LispObject obj) {
1266  return javaClass(obj, JavaClassLoader.getCurrentClassLoader());
1267    }
1268
1269    // Supports Java primitive types too.
1270    static Class javaClass(LispObject obj, ClassLoader classLoader)
1271    {
1272        if (obj instanceof AbstractString || obj instanceof Symbol) {
1273            String s = javaString(obj);
1274            if (s.equals("boolean"))
1275                return Boolean.TYPE;
1276            if (s.equals("byte"))
1277                return Byte.TYPE;
1278            if (s.equals("char"))
1279                return Character.TYPE;
1280            if (s.equals("short"))
1281                return Short.TYPE;
1282            if (s.equals("int"))
1283                return Integer.TYPE;
1284            if (s.equals("long"))
1285                return Long.TYPE;
1286            if (s.equals("float"))
1287                return Float.TYPE;
1288            if (s.equals("double"))
1289                return Double.TYPE;
1290            // Not a primitive Java type.
1291            Class c;
1292      c = classForName(s, classLoader);
1293            if (c == null)
1294                error(new LispError(s + " does not designate a Java class."));
1295
1296            return c;
1297        }
1298        // It's not a string, so it must be a JavaObject.
1299        final JavaObject javaObject;
1300        if (obj instanceof JavaObject) {
1301            javaObject = (JavaObject) obj;
1302        }
1303        else {
1304            type_error(obj, list(Symbol.OR, Symbol.STRING,
1305                                       Symbol.JAVA_OBJECT));
1306            // Not reached.
1307            return null;
1308        }
1309        final Object javaObjectgetObject = javaObject.getObject();
1310        if (javaObjectgetObject instanceof Class) {
1311            return (Class) javaObjectgetObject;
1312        }
1313            error(new LispError(obj.writeToString() + " does not designate a Java class."));
1314            return null;
1315    }
1316
1317    static final String getMessage(Throwable t)
1318    {
1319        String message = t.getMessage();
1320        if (message == null || message.length() == 0)
1321            message = t.getClass().getName();
1322        return message;
1323    }
1324}
Note: See TracBrowser for help on using the repository browser.