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

Last change on this file was 14239, checked in by Mark Evenson, 12 years ago

Fixes #229 so that JCALL works with static member classes.

Added non-working test to putative JSS test suite.

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