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

Last change on this file was 12663, checked in by Mark Evenson, 15 years ago

Adjust JCLASS docstring to reflect optional classloader argument.

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