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

Last change on this file was 12255, checked in by ehuelsmann, 16 years ago

Rename ConditionThrowable? to ControlTransfer? and remove

try/catch blocks which don't have anything to do with
non-local transfer of control.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 32.2 KB
Line 
1/*
2 * Java.java
3 *
4 * Copyright (C) 2002-2006 Peter Graves, Andras Simon
5 * $Id: Java.java 12255 2009-11-06 22:36:32Z ehuelsmann $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import java.beans.BeanInfo;
37import java.beans.IntrospectionException;
38import java.beans.Introspector;
39import java.beans.PropertyDescriptor;
40import java.lang.reflect.Array;
41import java.lang.reflect.Constructor;
42import java.lang.reflect.Field;
43import java.lang.reflect.InvocationTargetException;
44import java.lang.reflect.Method;
45import java.lang.reflect.Modifier;
46import java.util.HashMap;
47import java.util.Map;
48
49public final class Java extends Lisp
50{
51    private static final Map<Class,Symbol> registeredExceptions =
52       new HashMap<Class,Symbol>();
53
54    private static final LispClass java_exception = LispClass.findClass(Symbol.JAVA_EXCEPTION);
55
56    private static boolean isJavaException(LispClass lc)
57    {
58        return lc.subclassp(java_exception);
59    }
60
61    // ### register-java-exception exception-name condition-symbol => T
62    private static final Primitive REGISTER_JAVA_EXCEPTION =
63        new Primitive("register-java-exception", PACKAGE_JAVA, true,
64                      "exception-name condition-symbol")
65    {
66        @Override
67        public LispObject execute(LispObject className, LispObject symbol)
68
69        {
70            // FIXME Verify that CONDITION-SYMBOL is a symbol that names a condition.
71            // FIXME Signal a continuable error if the exception is already registered.
72            if ((symbol instanceof Symbol) && isJavaException(LispClass.findClass((Symbol) symbol))) {
73                registeredExceptions.put(classForName(className.getStringValue()),
74                                         (Symbol)symbol);
75                return T;
76            }
77            return NIL;
78        }
79    };
80
81    // ### unregister-java-exception exception-name => T or NIL
82    private static final Primitive UNREGISTER_JAVA_EXCEPTION =
83        new Primitive("unregister-java-exception", PACKAGE_JAVA, true,
84                      "exception-name")
85    {
86        @Override
87        public LispObject execute(LispObject className)
88
89        {
90            // FIXME Verify that EXCEPTION-NAME designates a subclass of Throwable.
91            return registeredExceptions.remove(classForName(className.getStringValue())) == null ? NIL : T;
92        }
93    };
94
95    private static Symbol getCondition(Class cl)
96    {
97  Class o = classForName("java.lang.Object");
98      for (Class c = cl ; c != o ; c = c.getSuperclass()) {
99            Object object = registeredExceptions.get(c);
100            if (object != null && isJavaException(LispClass.findClass((Symbol) object))) {
101                return (Symbol) object;
102            }
103        }
104        return null;
105    }
106
107    // ### jclass name-or-class-ref => class-ref
108    private static final Primitive JCLASS =
109        new Primitive(Symbol.JCLASS, "name-or-class-ref",
110"Returns a reference to the Java class designated by NAME-OR-CLASS-REF.")
111    {
112        @Override
113        public LispObject execute(LispObject arg)
114        {
115            return JavaObject.getInstance(javaClass(arg));
116        }
117    };
118
119    // ### jfield - retrieve or modify a field in a Java class or instance.
120    //
121    // Supported argument patterns:
122    //
123    //   Case 1: class-ref  field-name:
124    //               to retrieve the value of a static field.
125    //
126    //   Case 2: class-ref  field-name  instance-ref:
127    //               to retrieve the value of a class field of the instance.
128    //
129    //   Case 3: class-ref  field-name  primitive-value:
130    //               to store primitive-value in a static field.
131    //
132    //   Case 4: class-ref  field-name  instance-ref  value:
133    //               to store value in a class field of the instance.
134    //
135    //   Case 5: class-ref  field-name  nil  value:
136    //               to store value in a static field (when value may be
137    //               confused with an instance-ref).
138    //
139    //   Case 6: field-name  instance:
140    //               to retrieve the value of a field of the instance. The
141    //               class is derived from the instance.
142    //
143    //   Case 7: field-name  instance  value:
144    //               to store value in a field of the instance. The class is
145    //               derived from the instance.
146    //
147
148    private static final LispObject jfield(Primitive fun, LispObject[] args, boolean translate)
149
150    {
151        if (args.length < 2 || args.length > 4)
152            error(new WrongNumberOfArgumentsException(fun));
153        String fieldName = null;
154        Class c;
155        Field f;
156        Class fieldType;
157        Object instance = null;
158        try {
159            if (args[1] instanceof AbstractString) {
160                // Cases 1-5.
161                fieldName = args[1].getStringValue();
162                c = javaClass(args[0]);
163            } else {
164                // Cases 6 and 7.
165                fieldName = args[0].getStringValue();
166                instance = JavaObject.getObject(args[1]);
167                c = instance.getClass();
168            }
169            f = c.getField(fieldName);
170            fieldType = f.getType();
171            switch (args.length) {
172                case 2:
173                    // Cases 1 and 6.
174                    break;
175                case 3:
176                    // Cases 2,3, and 7.
177                    if (instance == null) {
178                        // Cases 2 and 3.
179                        if (args[2] instanceof JavaObject) {
180                            // Case 2.
181                            instance = JavaObject.getObject(args[2]);
182                            break;
183                        } else {
184                            // Case 3.
185                            f.set(null,args[2].javaInstance(fieldType));
186                            return args[2];
187                        }
188                    } else {
189                        // Case 7.
190                        f.set(instance,args[2].javaInstance(fieldType));
191                        return args[2];
192                    }
193                case 4:
194                    // Cases 4 and 5.
195                    if (args[2] != NIL) {
196                        // Case 4.
197                        instance = JavaObject.getObject(args[2]);
198                    }
199                    f.set(instance,args[3].javaInstance(fieldType));
200                    return args[3];
201            }
202            return JavaObject.getInstance(f.get(instance), translate);
203        }
204        catch (NoSuchFieldException e) {
205            error(new LispError("no such field"));
206        }
207        catch (SecurityException e) {
208            error(new LispError("inaccessible field"));
209        }
210        catch (IllegalAccessException e) {
211            error(new LispError("illegal access"));
212        }
213        catch (IllegalArgumentException e) {
214            error(new LispError("illegal argument"));
215        }
216        catch (Throwable t) {
217            error(new LispError(getMessage(t)));
218        }
219        // Not reached.
220        return NIL;
221    }
222
223    private static final Primitive JFIELD =
224        new Primitive("jfield", PACKAGE_JAVA, true,
225                      "class-ref-or-field field-or-instance &optional instance value")
226    {
227        @Override
228        public LispObject execute(LispObject[] args)
229        {
230            return jfield(this, args, true);
231        }
232    };
233
234    // ### jfield-raw - retrieve or modify a field in a Java class or instance.
235    private static final Primitive JFIELD_RAW =
236        new Primitive("jfield-raw", PACKAGE_JAVA, true,
237                      "class-ref-or-field field-or-instance &optional instance value")
238    {
239        @Override
240        public LispObject execute(LispObject[] args)
241        {
242            return jfield(this, args, false);
243        }
244    };
245
246    // ### jconstructor class-ref &rest parameter-class-refs
247    private static final Primitive JCONSTRUCTOR =
248        new Primitive("jconstructor", PACKAGE_JAVA, true,
249                      "class-ref &rest parameter-class-refs")
250    {
251        @Override
252        public LispObject execute(LispObject[] args)
253        {
254            if (args.length < 1)
255                error(new WrongNumberOfArgumentsException(this));
256            try {
257                final Class<?> c = javaClass(args[0]);
258                int argCount = 0;
259                if (args.length == 2 && args[1] instanceof Fixnum) {
260                    argCount = Fixnum.getValue(args[1]);
261                } else {
262                    Class<?>[] parameterTypes = new Class[args.length-1];
263                    for (int i = 1; i < args.length; i++) {
264                        parameterTypes[i-1] = javaClass(args[i]);
265                    }
266                    return JavaObject.getInstance(c.getConstructor(parameterTypes));
267                }
268                // Parameter types not explicitly specified.
269                Constructor[] constructors = c.getConstructors();
270                for (int i = 0; i < constructors.length; i++) {
271                    Constructor constructor = constructors[i];
272                    if (constructor.getParameterTypes().length == argCount)
273                        return JavaObject.getInstance(constructor);
274                }
275                throw new NoSuchMethodException();
276            }
277            catch (NoSuchMethodException e) {
278                error(new LispError("no such constructor"));
279            }
280            catch (ControlTransfer e) {
281                throw e;
282            }
283            catch (Throwable t) {
284                error(new LispError(getMessage(t)));
285            }
286            // Not reached.
287            return NIL;
288        }
289    };
290
291    // ### jmethod class-ref name &rest parameter-class-refs
292    private static final Primitive JMETHOD =
293        new Primitive("jmethod", PACKAGE_JAVA, true,
294                      "class-ref name &rest parameter-class-refs")
295    {
296        @Override
297        public LispObject execute(LispObject[] args)
298        {
299            if (args.length < 2)
300                error(new WrongNumberOfArgumentsException(this));
301            final Class<?> c = javaClass(args[0]);
302            String methodName = args[1].getStringValue();
303            try {
304                int argCount = 0;
305                if (args.length == 3 && args[2] instanceof Fixnum) {
306                    argCount = ((Fixnum)args[2]).value;
307                } else {
308                    Class<?>[] parameterTypes = new Class[args.length-2];
309                    for (int i = 2; i < args.length; i++)
310                        parameterTypes[i-2] = javaClass(args[i]);
311                    return JavaObject.getInstance(c.getMethod(methodName,
312                                                              parameterTypes));
313                }
314                // Parameter types were not explicitly specified.
315                Method[] methods = c.getMethods();
316                for (int i = 0; i < methods.length; i++) {
317                    Method method = methods[i];
318                    if (method.getName().equals(methodName) &&
319                        method.getParameterTypes().length == argCount)
320                        return JavaObject.getInstance(method);
321                }
322                throw new NoSuchMethodException();
323            }
324            catch (NoSuchMethodException e) {
325                FastStringBuffer sb = new FastStringBuffer("No such method: ");
326                sb.append(c.getName());
327                sb.append('.');
328                sb.append(methodName);
329                sb.append('(');
330                for (int i = 2; i < args.length; i++) {
331                    sb.append(args[i].writeToString());
332                    if (i < args.length - 1)
333                        sb.append(',');
334                }
335                sb.append(')');
336                error(new LispError(sb.toString()));
337            }
338            catch (ControlTransfer e) {
339                throw e;
340            }
341            catch (Throwable t) {
342                error(new LispError(getMessage(t)));
343            }
344            // Not reached.
345            return NIL;
346        }
347    };
348
349    private static final LispObject jstatic(Primitive fun, LispObject[] args, boolean translate)
350
351    {
352        if (args.length < 2)
353            error(new WrongNumberOfArgumentsException(fun));
354        try {
355            Method m = null;
356            LispObject methodRef = args[0];
357            if (methodRef instanceof JavaObject) {
358                Object obj = ((JavaObject)methodRef).getObject();
359                if (obj instanceof Method)
360                    m = (Method) obj;
361            } else if (methodRef instanceof AbstractString) {
362                Class c = javaClass(args[1]);
363                if (c != null) {
364                    String methodName = methodRef.getStringValue();
365                    Method[] methods = c.getMethods();
366                    int argCount = args.length - 2;
367                    for (int i = 0; i < methods.length; i++) {
368                        Method method = methods[i];
369                        if (!Modifier.isStatic(method.getModifiers())
370                            || method.getParameterTypes().length != argCount)
371                            continue;
372                        if (method.getName().equals(methodName)) {
373                            m = method;
374                            break;
375                        }
376                    }
377                    if (m == null)
378                        error(new LispError("no such method"));
379                }
380            } else
381                error(new TypeError("wrong type: " + methodRef));
382            Object[] methodArgs = new Object[args.length-2];
383            Class[] argTypes = m.getParameterTypes();
384            for (int i = 2; i < args.length; i++) {
385                LispObject arg = args[i];
386                if (arg == NIL)
387                    methodArgs[i-2] = null;
388                else
389                    methodArgs[i-2] = arg.javaInstance(argTypes[i-2]);
390            }
391            Object result = m.invoke(null, methodArgs);
392            return JavaObject.getInstance(result, translate);
393        }
394        catch (Throwable t) {
395            if (t instanceof InvocationTargetException)
396                t = t.getCause();
397            Symbol condition = getCondition(t.getClass());
398            if (condition == null)
399                error(new JavaException(t));
400            else
401                Symbol.SIGNAL.execute(
402                    condition,
403                    Keyword.CAUSE,
404                    JavaObject.getInstance(t),
405                    Keyword.FORMAT_CONTROL,
406                    new SimpleString(getMessage(t)));
407        }
408        // Not reached.
409        return NIL;
410    }
411
412    // ### jstatic method class &rest args
413    private static final Primitive JSTATIC =
414        new Primitive("jstatic", PACKAGE_JAVA, true, "method class &rest args")
415    {
416        @Override
417        public LispObject execute(LispObject[] args)
418        {
419            return jstatic(this, args, true);
420        }
421    };
422
423    // ### jstatic-raw method class &rest args
424    private static final Primitive JSTATIC_RAW =
425        new Primitive("jstatic-raw", PACKAGE_JAVA, true,
426                      "method class &rest args")
427    {
428        @Override
429        public LispObject execute(LispObject[] args)
430        {
431            return jstatic(this, args, false);
432        }
433    };
434
435    // ### jnew constructor &rest args
436    private static final Primitive JNEW =
437        new Primitive("jnew", PACKAGE_JAVA, true, "constructor &rest args")
438    {
439        @Override
440        public LispObject execute(LispObject[] args)
441        {
442            if (args.length < 1)
443                error(new WrongNumberOfArgumentsException(this));
444            LispObject classRef = args[0];
445            try {
446                Constructor constructor = (Constructor) JavaObject.getObject(classRef);
447                Class[] argTypes = constructor.getParameterTypes();
448                Object[] initargs = new Object[args.length-1];
449                for (int i = 1; i < args.length; i++) {
450                    LispObject arg = args[i];
451                    if (arg == NIL)
452                        initargs[i-1] = null;
453                    else {
454                        initargs[i-1] = arg.javaInstance(argTypes[i-1]);
455                    }
456                }
457                return JavaObject.getInstance(constructor.newInstance(initargs));
458            }
459            catch (Throwable t) {
460                if (t instanceof InvocationTargetException)
461                    t = t.getCause();
462                Symbol condition = getCondition(t.getClass());
463                if (condition == null)
464                    error(new JavaException(t));
465                else
466                    Symbol.SIGNAL.execute(
467                        condition,
468                        Keyword.CAUSE,
469                        JavaObject.getInstance(t),
470                        Keyword.FORMAT_CONTROL,
471                        new SimpleString(getMessage(t)));
472            }
473            // Not reached.
474            return NIL;
475        }
476    };
477
478    // ### jnew-array element-type &rest dimensions
479    private static final Primitive JNEW_ARRAY =
480        new Primitive("jnew-array", PACKAGE_JAVA, true,
481                      "element-type &rest dimensions")
482    {
483        @Override
484        public LispObject execute(LispObject[] args)
485        {
486            if (args.length < 2)
487                error(new WrongNumberOfArgumentsException(this));
488            try {
489                Class c = javaClass(args[0]);
490                int[] dimensions = new int[args.length - 1];
491                for (int i = 1; i < args.length; i++)
492                    dimensions[i-1] = ((Integer)args[i].javaInstance()).intValue();
493                return JavaObject.getInstance(Array.newInstance(c, dimensions));
494            }
495            catch (Throwable t) {
496                error(new JavaException(t));
497            }
498            // Not reached.
499            return NIL;
500        }
501    };
502
503    private static final LispObject jarray_ref(Primitive fun, LispObject[] args, boolean translate)
504
505    {
506        if (args.length < 2)
507            error(new WrongNumberOfArgumentsException(fun));
508        try {
509            Object a = args[0].javaInstance();
510            for (int i = 1; i<args.length - 1; i++)
511                a = Array.get(a, ((Integer)args[i].javaInstance()).intValue());
512            return JavaObject.getInstance(Array.get(a,
513                    ((Integer)args[args.length - 1].javaInstance()).intValue()), translate);
514        }
515        catch (Throwable t) {
516            Symbol condition = getCondition(t.getClass());
517            if (condition == null)
518                error(new JavaException(t));
519            else
520                Symbol.SIGNAL.execute(
521                    condition,
522                    Keyword.CAUSE,
523                    JavaObject.getInstance(t),
524                    Keyword.FORMAT_CONTROL,
525                    new SimpleString(getMessage(t)));
526        }
527        // Not reached.
528        return NIL;
529    }
530
531    // ### jarray-ref java-array &rest indices
532    private static final Primitive JARRAY_REF =
533        new Primitive("jarray-ref", PACKAGE_JAVA, true,
534                      "java-array &rest indices")
535    {
536        @Override
537        public LispObject execute(LispObject[] args)
538        {
539            return jarray_ref(this, args, true);
540        }
541    };
542
543    // ### jarray-ref-raw java-array &rest indices
544    private static final Primitive JARRAY_REF_RAW =
545        new Primitive("jarray-ref-raw", PACKAGE_JAVA, true,
546                      "java-array &rest indices")
547    {
548        @Override
549        public LispObject execute(LispObject[] args)
550        {
551            return jarray_ref(this, args, false);
552        }
553    };
554
555    // ### jarray-set java-array new-value &rest indices
556    private static final Primitive JARRAY_SET =
557        new Primitive("jarray-set", PACKAGE_JAVA, true,
558                      "java-array new-value &rest indices")
559    {
560        @Override
561        public LispObject execute(LispObject[] args)
562        {
563            if (args.length < 3)
564                error(new WrongNumberOfArgumentsException(this));
565            try {
566                Object a = args[0].javaInstance();
567                LispObject v = args[1];
568                for (int i = 2; i<args.length - 1; i++)
569                    a = Array.get(a, ((Integer)args[i].javaInstance()).intValue());
570                Array.set(a, ((Integer)args[args.length - 1].javaInstance()).intValue(), v.javaInstance());
571                return v;
572            }
573            catch (Throwable t) {
574                Symbol condition = getCondition(t.getClass());
575                if (condition == null)
576                    error(new JavaException(t));
577                else
578                    Symbol.SIGNAL.execute(
579                        condition,
580                        Keyword.CAUSE,
581                        JavaObject.getInstance(t),
582                        Keyword.FORMAT_CONTROL,
583                        new SimpleString(getMessage(t)));
584            }
585            // Not reached.
586            return NIL;
587        }
588    };
589
590    // ### jcall method instance &rest args
591    // Calls makeLispObject() to convert the result to an appropriate Lisp type.
592    private static final Primitive JCALL =
593        new Primitive(Symbol.JCALL, "method-ref instance &rest args")
594    {
595        @Override
596        public LispObject execute(LispObject[] args)
597        {
598            return jcall(this, args, true);
599        }
600    };
601
602    // ### jcall-raw method instance &rest args
603    // Does no type conversion. The result of the call is simply wrapped in a
604    // JavaObject.
605    private static final Primitive JCALL_RAW =
606        new Primitive(Symbol.JCALL_RAW, "method-ref instance &rest args")
607    {
608        @Override
609        public LispObject execute(LispObject[] args)
610        {
611            return jcall(this, args, false);
612        }
613    };
614
615    private static LispObject jcall(Primitive fun, LispObject[] args, boolean translate)
616
617    {
618        if (args.length < 2)
619            error(new WrongNumberOfArgumentsException(fun));
620        final LispObject methodArg = args[0];
621        final LispObject instanceArg = args[1];
622        final Object instance;
623        if (instanceArg instanceof AbstractString)
624            instance = instanceArg.getStringValue();
625        else if (instanceArg instanceof JavaObject)
626            instance = ((JavaObject)instanceArg).getObject();
627        else {
628      instance = instanceArg.javaInstance();
629        }
630        try {
631            final Method method;
632            if (methodArg instanceof AbstractString) {
633                String methodName = methodArg.getStringValue();
634                Class c = instance.getClass();
635                // FIXME Use the actual args, not just the count!
636                method = findMethod(c, methodName, args.length - 2);
637            } else
638                method = (Method) JavaObject.getObject(methodArg);
639            Class<?>[] argTypes = (Class<?>[])method.getParameterTypes();
640            Object[] methodArgs = new Object[args.length - 2];
641            for (int i = 2; i < args.length; i++) {
642                LispObject arg = args[i];
643                if (arg == NIL)
644                    methodArgs[i-2] = null;
645                else
646                    methodArgs[i-2] = arg.javaInstance(argTypes[i-2]);
647            }
648            return JavaObject.getInstance(method.invoke(instance, methodArgs),
649                                          translate);
650        }
651        catch (ControlTransfer t) {
652            throw t;
653        }
654        catch (Throwable t) {
655            if (t instanceof InvocationTargetException)
656                t = t.getCause();
657            Symbol condition = getCondition(t.getClass());
658            if (condition == null)
659                error(new JavaException(t));
660            else
661                Symbol.SIGNAL.execute(
662                    condition,
663                    Keyword.CAUSE,
664                    JavaObject.getInstance(t),
665                    Keyword.FORMAT_CONTROL,
666                    new SimpleString(getMessage(t)));
667        }
668        // Not reached.
669        return null;
670    }
671
672    // FIXME This just returns the first matching method that it finds. Allegro
673    // signals a continuable error if there are multiple matching methods.
674    private static Method findMethod(Class c, String methodName, int argCount)
675    {
676        Method[] methods = c.getMethods();
677        for (int i = methods.length; i-- > 0;) {
678            Method method = methods[i];
679            if (method.getName().equals(methodName))
680                if (method.getParameterTypes().length == argCount)
681                    return method;
682        }
683        return null;
684    }
685
686    // ### make-immediate-object object &optional type
687    private static final Primitive MAKE_IMMEDIATE_OBJECT =
688        new Primitive("make-immediate-object", PACKAGE_JAVA, true,
689                      "object &optional type")
690    {
691        @Override
692        public LispObject execute(LispObject[] args)
693        {
694            if (args.length < 1)
695                error(new WrongNumberOfArgumentsException(this));
696            LispObject object = args[0];
697            try {
698                if (args.length > 1) {
699                    LispObject type = args[1];
700                    if (type == Keyword.BOOLEAN) {
701                        if (object == NIL)
702                            return JavaObject.getInstance(Boolean.FALSE);
703                        else
704                            return JavaObject.getInstance(Boolean.TRUE);
705                    }
706                    if (type == Keyword.REF) {
707                        if (object == NIL)
708                            return JavaObject.getInstance(null);
709                        else
710                            throw new Error();
711                    }
712                    // other special cases come here
713                }
714                return JavaObject.getInstance(object.javaInstance());
715            }
716            catch (Throwable t) {
717                error(new LispError("MAKE-IMMEDIATE-OBJECT: not implemented"));
718            }
719            // Not reached.
720            return NIL;
721        }
722    };
723
724    // ### java-object-p
725    private static final Primitive JAVA_OBJECT_P =
726        new Primitive("java-object-p", PACKAGE_JAVA, true, "object")
727    {
728        @Override
729        public LispObject execute(LispObject arg)
730        {
731            return (arg instanceof JavaObject) ? T : NIL;
732        }
733    };
734
735    // ### jobject-lisp-value java-object
736    private static final Primitive JOBJECT_LISP_VALUE =
737        new Primitive("jobject-lisp-value", PACKAGE_JAVA, true, "java-object")
738    {
739        @Override
740        public LispObject execute(LispObject arg)
741        {
742            return JavaObject.getInstance(arg.javaInstance(), true);
743        }
744    };
745   
746    private static final Primitive JGET_PROPERTY_VALUE =
747      new Primitive("%jget-property-value", PACKAGE_JAVA, true,
748                    "java-object property-name") {
749     
750        @Override
751        public LispObject execute(LispObject javaObject, LispObject propertyName) {
752      try {
753        Object obj = javaObject.javaInstance();
754        PropertyDescriptor pd = getPropertyDescriptor(obj, propertyName);
755        Object value = pd.getReadMethod().invoke(obj);
756        if(value instanceof LispObject) {
757            return (LispObject) value;
758        } else if(value != null) {
759            return JavaObject.getInstance(value, true);
760        } else {
761            return NIL;
762        }
763      } catch (Exception e) {
764                return error(new JavaException(e));
765      }
766        }
767    };
768   
769    private static final Primitive JSET_PROPERTY_VALUE =
770      new Primitive("%jset-property-value", PACKAGE_JAVA, true,
771                    "java-object property-name value") {
772     
773        @Override
774        public LispObject execute(LispObject javaObject, LispObject propertyName, LispObject value) {
775      Object obj = null;
776      try {
777    obj = javaObject.javaInstance();
778    PropertyDescriptor pd = getPropertyDescriptor(obj, propertyName);
779    Object jValue;
780    //TODO maybe we should do this in javaInstance(Class)
781    if(value instanceof JavaObject) {
782        jValue = value.javaInstance();
783    } else {
784        if(Boolean.TYPE.equals(pd.getPropertyType()) ||
785           Boolean.class.equals(pd.getPropertyType())) {
786      jValue = value != NIL;
787        } else {
788      jValue = value != NIL ? value.javaInstance() : null;
789        }
790    }
791    pd.getWriteMethod().invoke(obj, jValue);
792    return value;
793      } catch (Exception e) {
794            return error(new JavaException(e));
795      }
796        }
797    };
798   
799    private static PropertyDescriptor getPropertyDescriptor(Object obj, LispObject propertyName) throws IntrospectionException {
800        String prop = ((AbstractString) propertyName).getStringValue();
801        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
802        for(PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
803          if(pd.getName().equals(prop)) {
804            return pd;
805          }
806        }
807        error(new LispError("Property " + prop + " not found in " + obj));
808
809        return null; // not reached
810    }
811   
812    private static Class classForName(String className)
813    {
814        try {
815            return Class.forName(className);
816        }
817        catch (ClassNotFoundException e) {
818            try {
819                return Class.forName(className, true, JavaClassLoader.getPersistentInstance());
820            }
821            catch (ClassNotFoundException ex) {
822                error(new LispError("Class not found: " + className));
823                // Not reached.
824                return null;
825            }
826        }
827    }
828
829    // Supports Java primitive types too.
830    private static Class javaClass(LispObject obj)
831    {
832        if (obj instanceof AbstractString || obj instanceof Symbol) {
833            String s = javaString(obj);
834            if (s.equals("boolean"))
835                return Boolean.TYPE;
836            if (s.equals("byte"))
837                return Byte.TYPE;
838            if (s.equals("char"))
839                return Character.TYPE;
840            if (s.equals("short"))
841                return Short.TYPE;
842            if (s.equals("int"))
843                return Integer.TYPE;
844            if (s.equals("long"))
845                return Long.TYPE;
846            if (s.equals("float"))
847                return Float.TYPE;
848            if (s.equals("double"))
849                return Double.TYPE;
850            // Not a primitive Java type.
851            Class c = classForName(s);
852            if (c == null)
853                error(new LispError(s + " does not designate a Java class."));
854
855            return c;
856        }
857        // It's not a string, so it must be a JavaObject.
858        final JavaObject javaObject;
859        if (obj instanceof JavaObject) {
860            javaObject = (JavaObject) obj;
861        }
862        else {
863            type_error(obj, list(Symbol.OR, Symbol.STRING,
864                                       Symbol.JAVA_OBJECT));
865            // Not reached.
866            return null;
867        }
868        final Object javaObjectgetObject = javaObject.getObject();
869        if (javaObjectgetObject instanceof Class) {
870            return (Class) javaObjectgetObject;
871        }
872            error(new LispError(obj.writeToString() + " does not designate a Java class."));
873            return null;
874    }
875
876    private static final String getMessage(Throwable t)
877    {
878        String message = t.getMessage();
879        if (message == null || message.length() == 0)
880            message = t.getClass().getName();
881        return message;
882    }
883}
Note: See TracBrowser for help on using the repository browser.