source: branches/0.17.x/abcl/src/org/armedbear/lisp/JavaObject.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: 12.3 KB
Line 
1/*
2 * JavaObject.java
3 *
4 * Copyright (C) 2002-2005 Peter Graves
5 * $Id: JavaObject.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.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)
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
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) {
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
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()
225    {
226        if (obj instanceof ControlTransfer)
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() {
249  return new SimpleString(describeJavaObject(this));
250    }
251
252    @Override
253    public LispObject getParts() {
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  {
277  final LispObject[] acc = new LispObject[] { NIL };
278  doClassHierarchy(obj.getClass(), new Function() {
279    @Override
280    public LispObject execute(LispObject arg)
281        {
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  {
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  {
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    {
349  final LispObject[] acc = new LispObject[] { NIL };
350  doClassHierarchy(clss, new Function() {
351    @Override
352    public LispObject execute(LispObject arg)
353        {
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  {
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
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.