source: branches/0.16.x/abcl/src/org/armedbear/lisp/JavaObject.java

Last change on this file was 12083, checked in by astalla, 16 years ago

Fixed (reverted) wrong implementation of JavaObject?.javaInstance(Class)
introduced with revision 12081.
Fixed incorrect wrapping of LispObjects? that are elements of a Java array when
the array is inspected.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 12.7 KB
Line 
1/*
2 * JavaObject.java
3 *
4 * Copyright (C) 2002-2005 Peter Graves
5 * $Id: JavaObject.java 12083 2009-08-05 18:16:57Z astalla $
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.lang.reflect.*;
37
38import java.math.BigInteger;
39
40import java.util.*;
41
42public final class JavaObject extends LispObject
43{
44    private final Object obj;
45
46    public JavaObject(Object obj)
47    {
48        this.obj = obj;
49    }
50
51    @Override
52    public LispObject typeOf()
53    {
54        return Symbol.JAVA_OBJECT;
55    }
56
57    @Override
58    public LispObject classOf()
59    {
60        if(obj == null) {
61                return BuiltInClass.JAVA_OBJECT;
62        } else {
63                return JavaClass.findJavaClass(obj.getClass());
64        }
65    }
66
67    @Override
68    public LispObject typep(LispObject type) throws ConditionThrowable
69    {
70        if (type == Symbol.JAVA_OBJECT)
71            return T;
72        if (type == BuiltInClass.JAVA_OBJECT)
73            return T;
74        if(type instanceof JavaClass && obj != null) {
75                return ((JavaClass) type).getJavaClass().isAssignableFrom(obj.getClass()) ? T : NIL;
76        }
77        return super.typep(type);
78    }
79
80    public final Object getObject()
81    {
82        return obj;
83    }
84
85    /** Encapsulates obj, if required.
86     * If obj is a {@link  LispObject}, it's returned as-is.
87     *
88     * @param obj Any java object
89     * @return obj or a new JavaObject encapsulating obj
90     */
91    public final static LispObject getInstance(Object obj) {
92        if (obj == null)
93            return new JavaObject(null);
94       
95        if (obj instanceof LispObject)
96            return (LispObject)obj;
97
98        return new JavaObject(obj);
99    }
100
101    /** Encapsulates obj, if required.
102     * If obj is a {@link LispObject}, it's returned as-is.
103     * If obj is of a type which can be mapped to a lisp type,
104     * an object of the mapped type is returned, if translated is true.
105     *
106     * @param obj
107     * @param translated
108     * @return a LispObject representing or encapsulating obj
109     */
110    public final static LispObject getInstance(Object obj, boolean translated)
111            throws ConditionThrowable
112    {
113        if (! translated)
114            return getInstance(obj);
115
116        if (obj == null) return NIL;
117
118        if (obj instanceof LispObject)
119            return (LispObject)obj;
120
121        if (obj instanceof String)
122            return new SimpleString((String)obj);
123
124        if (obj instanceof Number) {
125            // Number types ordered according to decreasing
126            // estimated chances of occurrance
127
128            if (obj instanceof Integer)
129                return Fixnum.getInstance(((Integer)obj).intValue());
130
131            if (obj instanceof Float)
132                return new SingleFloat((Float)obj);
133
134            if (obj instanceof Double)
135                return new DoubleFloat((Double)obj);
136
137            if (obj instanceof Long)
138                return LispInteger.getInstance(((Long)obj).longValue());
139
140            if (obj instanceof BigInteger)
141                return Bignum.getInstance((BigInteger)obj);
142
143            if (obj instanceof Short)
144                return Fixnum.getInstance(((Short)obj).shortValue());
145
146            if (obj instanceof Byte)
147                return Fixnum.getInstance(((Byte)obj).byteValue());
148            // We don't handle BigDecimal: it doesn't map to a Lisp type
149        }
150
151        if (obj instanceof Boolean)
152            return ((Boolean)obj).booleanValue() ? T : NIL;
153
154        if (obj instanceof Character)
155            return new LispCharacter((Character)obj);
156
157        if (obj instanceof Object[]) {
158            Object[] array = (Object[]) obj;
159            SimpleVector v = new SimpleVector(array.length);
160            for (int i = array.length; i-- > 0;)
161                v.aset(i, JavaObject.getInstance(array[i], translated));
162            return v;
163        }
164        // TODO
165        // We might want to handle:
166        //  - streams
167        //  - others?
168        return new JavaObject(obj);
169    }
170
171    @Override
172    public Object javaInstance()
173    {
174        return obj;
175    }
176
177    @Override
178    public Object javaInstance(Class c) throws ConditionThrowable {
179  return javaInstance();
180    }
181
182    /** Returns the encapsulated Java object for
183     * interoperability with wait, notify, synchronized, etc.
184     *
185     * @return The encapsulated object
186     */
187    @Override
188    public Object lockableInstance() {
189        return obj;
190    }
191
192    public static final Object getObject(LispObject o)
193        throws ConditionThrowable
194    {
195        if (o instanceof JavaObject)
196                return ((JavaObject)o).obj;       
197        return             // Not reached.
198        type_error(o, Symbol.JAVA_OBJECT);       
199    }
200
201    @Override
202    public final boolean equal(LispObject other)
203    {
204        if (this == other)
205            return true;
206        if (other instanceof JavaObject)
207            return (obj == ((JavaObject)other).obj);
208        return false;
209    }
210
211    @Override
212    public final boolean equalp(LispObject other)
213    {
214        return equal(other);
215    }
216
217    @Override
218    public int sxhash()
219    {
220        return obj == null ? 0 : (obj.hashCode() & 0x7ffffff);
221    }
222
223    @Override
224    public String writeToString() throws ConditionThrowable
225    {
226        if (obj instanceof ConditionThrowable)
227            return obj.toString();
228  final String s;
229  if(obj != null) {
230      Class<?> c = obj.getClass();
231      FastStringBuffer sb
232    = new FastStringBuffer(c.isArray() ? "jarray" : c.getName());
233      sb.append(' ');
234      String ts = obj.toString();
235      if(ts.length() > 32) { //random value, should be chosen sensibly
236    sb.append(ts.substring(0, 32) + "...");
237      } else {
238    sb.append(ts);
239      }
240      s = sb.toString();
241  } else {
242      s = "null";
243  }
244        return unreadableString(s);
245    }
246
247    @Override
248    public LispObject getDescription() throws ConditionThrowable {
249  return new SimpleString(describeJavaObject(this));
250    }
251
252    @Override
253    public LispObject getParts() throws ConditionThrowable {
254  if(obj != null) {
255      LispObject parts = NIL;
256      if(obj.getClass().isArray()) {
257    SimpleString empty = new SimpleString("");
258    int length = Array.getLength(obj);
259    for(int i = 0; i < length; i++) {
260        parts = parts.push
261      (new Cons(empty, JavaObject.getInstance(Array.get(obj, i))));
262    }
263    parts = parts.nreverse();
264      } else {
265    parts = parts.push(new Cons("Java class",
266              new JavaObject(obj.getClass())));
267    parts = Symbol.NCONC.execute(parts, getInspectedFields());
268      }
269      return parts;
270  } else {
271      return NIL;
272  }
273    }
274
275    private LispObject getInspectedFields()
276  throws ConditionThrowable {
277  final LispObject[] acc = new LispObject[] { NIL };
278  doClassHierarchy(obj.getClass(), new Function() {
279    @Override
280    public LispObject execute(LispObject arg)
281        throws ConditionThrowable {
282        //No possibility of type error - we're mapping this function
283        //over a list of classes
284        Class<?> c = (Class) arg.javaInstance();
285        for(Field f : c.getDeclaredFields()) {
286      LispObject value = NIL;
287      try {
288          if(!f.isAccessible()) {
289        f.setAccessible(true);
290          }
291          value = JavaObject.getInstance(f.get(obj));
292      } catch(Exception e) {}
293      acc[0] = acc[0].push(new Cons(f.getName(), value));
294        }
295        return acc[0];
296    }
297      });
298  return acc[0].nreverse();
299    }
300
301    /**
302     * Executes a function repeatedly over the minimal subtree of the
303     * Java class hierarchy which contains every class in <classes>.
304     */
305    private static void doClassHierarchy(Collection<Class<?>> classes,
306           LispObject callback,
307           Set<Class<?>> visited)
308  throws ConditionThrowable {
309  Collection<Class<?>> newClasses = new LinkedList<Class<?>>();
310  for(Class<?> clss : classes) {
311      if(clss == null) {
312    continue;
313      }
314      if(!visited.contains(clss)) {
315    callback.execute(JavaObject.getInstance(clss, true));
316    visited.add(clss);
317      }
318      if(!visited.contains(clss.getSuperclass())) {
319    newClasses.add(clss.getSuperclass());
320      }
321      for(Class<?> iface : clss.getInterfaces()) {
322    if (!visited.contains(iface)) {
323        newClasses.add(iface);
324    }
325      }
326  }
327  if(!newClasses.isEmpty()) {
328      doClassHierarchy(newClasses, callback, visited);
329  }
330    }
331
332    /**
333     * Executes a function recursively over <clss> and its superclasses and
334     * interfaces.
335     */
336    public static void doClassHierarchy(Class<?> clss, LispObject callback)
337  throws ConditionThrowable {
338  if (clss != null) {
339      Set<Class<?>> visited = new HashSet<Class<?>>();
340      Collection<Class<?>> classes = new ArrayList<Class<?>>(1);
341      classes.add(clss);
342      doClassHierarchy(classes, callback, visited);
343  }
344    }
345
346    public static LispObject mapcarClassHierarchy(Class<?> clss,
347              final LispObject fn)
348    throws ConditionThrowable {
349  final LispObject[] acc = new LispObject[] { NIL };
350  doClassHierarchy(clss, new Function() {
351    @Override
352    public LispObject execute(LispObject arg)
353        throws ConditionThrowable {
354        acc[0] = acc[0].push(fn.execute(arg));
355        return acc[0];
356    }
357      });
358  return acc[0].nreverse();
359    }
360
361    public static String describeJavaObject(final JavaObject javaObject)
362  throws ConditionThrowable {
363  final Object obj = javaObject.getObject();
364  final FastStringBuffer sb =
365      new FastStringBuffer(javaObject.writeToString());
366  sb.append(" is an object of type ");
367  sb.append(Symbol.JAVA_OBJECT.writeToString());
368  sb.append(".");
369  sb.append(System.getProperty("line.separator"));
370  sb.append("The wrapped Java object is ");
371  if (obj == null) {
372      sb.append("null.");
373  } else {
374      sb.append("an ");
375      final Class c = obj.getClass();
376      String className = c.getName();
377      if (c.isArray()) {
378    sb.append("array of ");
379    if (className.startsWith("[L") && className.endsWith(";")) {
380        className = className.substring(1, className.length() - 1);
381        sb.append(className);
382        sb.append(" objects");
383    } else if (className.startsWith("[") && className.length() > 1) {
384        char descriptor = className.charAt(1);
385        final String type;
386        switch (descriptor) {
387        case 'B': type = "bytes"; break;
388        case 'C': type = "chars"; break;
389        case 'D': type = "doubles"; break;
390        case 'F': type = "floats"; break;
391        case 'I': type = "ints"; break;
392        case 'J': type = "longs"; break;
393        case 'S': type = "shorts"; break;
394        case 'Z': type = "booleans"; break;
395        default:
396      type = "unknown type";
397        }
398        sb.append(type);
399    }
400    sb.append(" with ");
401    final int length = java.lang.reflect.Array.getLength(obj);
402    sb.append(length);
403    sb.append(" element");
404    if (length != 1)
405        sb.append('s');
406    sb.append('.');
407      } else {
408    sb.append("instance of ");
409    sb.append(className);
410    sb.append(':');
411    sb.append(System.getProperty("line.separator"));
412    sb.append("  \"");
413    sb.append(obj.toString());
414    sb.append('"');
415      }
416  }
417  return sb.toString();
418    }
419
420    // ### describe-java-object
421    private static final Primitive DESCRIBE_JAVA_OBJECT =
422        new Primitive("describe-java-object", PACKAGE_JAVA, true)
423    {
424        @Override
425        public LispObject execute(LispObject first, LispObject second)
426            throws ConditionThrowable
427        {
428            if (!(first instanceof JavaObject))
429                return type_error(first, Symbol.JAVA_OBJECT);
430            final Stream stream = checkStream(second);
431            final JavaObject javaObject = (JavaObject) first;
432            stream._writeString(describeJavaObject(javaObject));
433            return LispThread.currentThread().nothing();
434        }
435    };
436}
Note: See TracBrowser for help on using the repository browser.