PageRenderTime 71ms CodeModel.GetById 15ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/at2dist170907/src/edu/vub/at/objects/symbiosis/Symbiosis.java

http://ambienttalk.googlecode.com/
Java | 716 lines | 399 code | 32 blank | 285 comment | 135 complexity | f563801a6a2c02be63e6bf35d92c3dac MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * Symbiosis.java created on 5-nov-2006 at 19:22:26
  4 * (c) Programming Technology Lab, 2006 - 2007
  5 * Authors: Tom Van Cutsem & Stijn Mostinckx
  6 * 
  7 * Permission is hereby granted, free of charge, to any person
  8 * obtaining a copy of this software and associated documentation
  9 * files (the "Software"), to deal in the Software without
 10 * restriction, including without limitation the rights to use,
 11 * copy, modify, merge, publish, distribute, sublicense, and/or
 12 * sell copies of the Software, and to permit persons to whom the
 13 * Software is furnished to do so, subject to the following
 14 * conditions:
 15 *
 16 * The above copyright notice and this permission notice shall be
 17 * included in all copies or substantial portions of the Software.
 18 *
 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 26 * OTHER DEALINGS IN THE SOFTWARE.
 27 */
 28package edu.vub.at.objects.symbiosis;
 29
 30import edu.vub.at.eval.Evaluator;
 31import edu.vub.at.exceptions.InterpreterException;
 32import edu.vub.at.exceptions.XArityMismatch;
 33import edu.vub.at.exceptions.XIllegalArgument;
 34import edu.vub.at.exceptions.XNotInstantiatable;
 35import edu.vub.at.exceptions.XReflectionFailure;
 36import edu.vub.at.exceptions.XSelectorNotFound;
 37import edu.vub.at.exceptions.XSymbiosisFailure;
 38import edu.vub.at.exceptions.XTypeMismatch;
 39import edu.vub.at.exceptions.XUnassignableField;
 40import edu.vub.at.exceptions.XUndefinedSlot;
 41import edu.vub.at.exceptions.signals.Signal;
 42import edu.vub.at.objects.ATObject;
 43import edu.vub.at.objects.coercion.Coercer;
 44import edu.vub.at.objects.mirrors.JavaInterfaceAdaptor;
 45import edu.vub.at.objects.mirrors.Reflection;
 46import edu.vub.at.objects.natives.NATException;
 47import edu.vub.at.objects.natives.OBJNil;
 48import edu.vub.at.objects.natives.NATTable;
 49import edu.vub.at.objects.natives.NATText;
 50
 51import java.lang.reflect.Array;
 52import java.lang.reflect.Constructor;
 53import java.lang.reflect.Field;
 54import java.lang.reflect.InvocationTargetException;
 55import java.lang.reflect.Method;
 56import java.lang.reflect.Modifier;
 57import java.util.EventListener;
 58import java.util.HashSet;
 59import java.util.Hashtable;
 60import java.util.Iterator;
 61import java.util.LinkedList;
 62import java.util.Vector;
 63
 64/**
 65 * The Symbiosis class is a container for auxiliary methods pertaining to making symbiotic
 66 * reflective Java invocations.
 67 * 
 68 * @author tvcutsem
 69 */
 70public final class Symbiosis {
 71
 72	/**
 73	 * Invoke a java method symbiotically, given only its name (not its implementation).
 74	 * First retrieves all of the methods matching the given selector in the given class, then tries
 75	 * to invoke the method symbiotically using the default symbiotic invocation algorithm.
 76	 * 
 77	 * @see #symbioticInvocation(ATObject, Object, String, JavaMethod, ATObject[]) the symbiotic invocation algorithm.
 78	 */
 79	public static ATObject symbioticInvocation(ATObject wrapper, Object symbiont, Class ofClass, String selector, ATObject[] atArgs) throws InterpreterException {
 80		return symbioticInvocation(wrapper, symbiont, selector, getMethods(ofClass, selector, (symbiont==null)), atArgs);
 81	}
 82	
 83	/**
 84	 * Invoke a java method symbiotically.
 85	 * The Java method invocation algorithm is as follows:
 86	 * <pre>
 87	 * case of # of methods matching selector:
 88	 *   0 => XSelectorNotFound
 89	 *   1 => invoke the method OR XIllegalArgument, XArityMismatch, XReflectionFailure
 90	 *   * => (case of # of methods with matching arity OR taking varargs:
 91	 *           0 => XSymbiosisFailure
 92	 *           1 => invoke the method OR XIllegalArgument, XReflectionFailure
 93	 *           * => (case of # of methods matching 'default type' of the actual arguments:
 94	 *                   0 => XSymbiosisFailure
 95	 *                   1 => invoke OR XReflectionFailure
 96	 *                   * => XSymbiosisFailure))
 97	 * </pre>
 98	 * A Java method takes a variable number of AT arguments <=> it has one formal parameter of type ATObject[]
 99	 * 
100	 * @param wrapper the ATObject wrapper for the symbiont
101	 * @param symbiont the Java object being accessed from within AmbientTalk
102	 * @param selector the Java selector corresponding to the method invocation
103	 * @param jMethod a JavaMethod encapsulating all applicable Java methods that correspond to the selector
104	 * @param atArgs the AT args to the symbiotic invocation
105	 * @return the wrapped result of the Java method invocation
106	 * 
107	 * @throws XArityMismatch if the wrong number of arguments were supplied
108	 * @throws XSelectorNotFound if no methods correspond to the given selector (i.e. jMethod is null)
109	 * @throws XTypeMismatch if one of the arguments cannot be converted into the static type expected by the Java method
110	 * @throws XSymbiosisFailure if the method is overloaded and cannot be unambiguously resolved given the actual arguments
111	 * @throws XReflectionFailure if the invoked method is not accessible from within AmbientTalk
112	 * @throws XJavaException if the invoked Java method throws a Java exception
113	 */
114	public static ATObject symbioticInvocation(ATObject wrapper, Object symbiont, String selector, JavaMethod jMethod, ATObject[] atArgs)
115	                                           throws InterpreterException {
116		if (jMethod == null) {
117		    // no methods found? selector does not exist...
118			throw new XSelectorNotFound(Reflection.downSelector(selector), wrapper);
119		} else {
120			Method[] methods = jMethod.choices_;
121			if (methods.length == 1) {
122				// just one method found, no need to resolve overloaded methods
123				// if the Java method takes an ATObject array as its sole parameter, it is interpreted as taking
124				// a variable number of ambienttalk arguments
125				Class[] params = methods[0].getParameterTypes();
126				Object[] args;
127				if ((params.length == 1) && params[0].equals(ATObject[].class)) {
128					args = new Object[] { atArgs };
129				} else {
130					if (params.length != atArgs.length) {
131						throw new XArityMismatch("Java method "+Reflection.downSelector(methods[0].getName()), params.length, atArgs.length);
132					}
133					// make sure to properly 'coerce' each argument into the proper AT interface type
134					args = atArgsToJavaArgs(atArgs, params);
135				}
136				return invokeUniqueSymbioticMethod(symbiont, methods[0], args);
137			} else {	
138				// overloading: filter out all methods that do not match arity or whose
139				// argument types do not match
140				Object[] actuals = null;
141				Class[] params;
142				LinkedList matchingMethods = new LinkedList();
143				// this boolean keeps track of whether or not failure to resolve the overloaded
144				// method is solely because of an arity mismatch, not because of a type mismatch
145				
146				boolean failedDueToArityOnly = true;
147				
148				for (int i = 0; i < methods.length; i++) {
149					params = methods[i].getParameterTypes();
150					// is the method a varargs method?
151					if ((params.length == 1) && params[0].equals(ATObject[].class)) {
152						actuals = new Object[] { atArgs };
153						matchingMethods.addFirst(methods[i]);
154					// does the arity match?
155					} else if (params.length == atArgs.length) {
156						// can it be invoked with the given actuals?
157						try {
158							actuals = atArgsToJavaArgs(atArgs, params);
159							matchingMethods.addFirst(methods[i]);
160						} catch(XTypeMismatch e) {
161							// types don't match
162							failedDueToArityOnly = false;
163						}
164					} else {
165				      // arity does not match
166					}
167				}
168				
169				switch (matchingMethods.size()) {
170				    case 0: {
171					    // no methods left: overloading resolution failed...
172				    	if (failedDueToArityOnly) {
173				    		// ... because of an arity mismatch
174				    		throw new XSymbiosisFailure(methods[0], atArgs.length);
175				    	} else {
176				    		// ... because the types could not be resolved
177				    		throw new XSymbiosisFailure(symbiont, methods[0], atArgs);
178				    	}
179					    
180				    }
181				    case 1: {
182				    	// just one method left, invoke it
183						return invokeUniqueSymbioticMethod(symbiont, (Method) matchingMethods.getFirst(), actuals);
184				    }
185				    default: {
186				    	// more than one method left: overloading resolution failed
187						throw new XSymbiosisFailure(symbiont, selector, matchingMethods, atArgs);
188				    }
189				}
190			}
191		}
192	}
193	
194	/**
195	 * Creates a new instance of a Java class.
196	 * 
197	 * @param ofClass the Java class of which to create an instance
198	 * @param atArgs the AmbientTalk arguments to the constructor, to be converted to Java arguments
199	 * @return an unitialized JavaObject wrapper around a newly created instance of the class
200	 * 
201	 * @throws XArityMismatch if the wrong number of arguments were supplied
202	 * @throws XNotInstantiatable if no public constructors are available or if the class is abstract
203	 * @throws XTypeMismatch if one of the arguments cannot be converted into the static type expected by the constructor
204	 * @throws XSymbiosisFailure if the constructor is overloaded and cannot be unambiguously resolved given the actual arguments
205	 * @throws XReflectionFailure if the invoked constructor is not accessible from within AmbientTalk
206	 * @throws XJavaException if the invoked Java constructor throws a Java exception
207	 */
208	public static ATObject symbioticInstanceCreation(Class ofClass, ATObject[] atArgs) throws InterpreterException {
209		Constructor[] ctors = ofClass.getConstructors();
210		switch (ctors.length) {
211		     // no constructors found? class is not instantiatable...
212		case 0:
213			throw new XNotInstantiatable(ofClass);
214			// just one constructor found, no need to resolve overloaded methods
215		case 1: {
216			// if the constructor takes an ATObject array as its sole parameter, it is interpreted as taking
217			// a variable number of ambienttalk arguments
218			Class[] params = ctors[0].getParameterTypes();
219			Object[] args;
220			if ((params.length == 1) && params[0].equals(ATObject[].class)) {
221				args = new Object[] { atArgs };
222			} else {
223				if (params.length != atArgs.length) {
224					throw new XArityMismatch("Java constructor "+Reflection.downSelector(ctors[0].getName()), params.length, atArgs.length);
225				}
226				// make sure to properly convert actual arguments into Java objects
227				args = atArgsToJavaArgs(atArgs, params);
228			}
229			return invokeUniqueSymbioticConstructor(ctors[0], args);
230		  }	
231		}
232		
233		// overloading: filter out all constructors that do not match arity or whose argument types do not match
234		int matchingCtors = 0;
235		Constructor matchingCtor = null;
236		Object[] actuals = null;
237		Class[] params;
238		for (int i = 0; i < ctors.length; i++) {
239			params = ctors[i].getParameterTypes();
240			// is the constructor a varargs constructor?
241			if ((params.length == 1) && params[0].equals(ATObject[].class)) {
242				actuals = new Object[] { atArgs };
243				matchingCtor = ctors[i];
244				matchingCtors++;
245				// does the arity match?
246			} else if (params.length == atArgs.length) {
247				// can it be invoked with the given actuals?
248				try {
249					actuals = atArgsToJavaArgs(atArgs, params);
250					matchingCtor = ctors[i];
251					matchingCtors++;
252				} catch(XTypeMismatch e) {
253					// types don't match
254					ctors[i] = null; // TODO: don't assign to null, array may be cached or used later on (or by wrapper method)
255				}
256			} else {
257				// arity does not match
258				ctors[i] = null;
259			}
260		}
261		
262		if (matchingCtors != 1) {
263			// no constructors left or more than one constructor left? overloading resolution failed
264			throw new XSymbiosisFailure(ofClass, ctors, atArgs, matchingCtors);
265		} else {
266			// just one constructor left, invoke it
267			return invokeUniqueSymbioticConstructor(matchingCtor, actuals);
268		}
269	}
270	
271	/**
272	 * Read a field from the given Java object reflectively.
273	 * @return the contents of the Java field, converted into its AmbientTalk equivalent
274	 */
275	public static ATObject readField(Object fromObject, Class ofClass, String fieldName)
276	                     throws InterpreterException {
277		Field f = getField(ofClass, fieldName, (fromObject == null));
278		return readField(fromObject, f);
279	}
280	
281	/**
282	 * Read a field from the given Java object reflectively.
283	 * @return the contents of the Java field, converted into its AmbientTalk equivalent
284	 */
285	public static ATObject readField(Object fromObject, Field f) throws InterpreterException {
286		try {
287			return Symbiosis.javaToAmbientTalk(f.get(fromObject));
288		} catch (IllegalArgumentException e) {
289			// the given object is of the wrong class, should not happen!
290			throw new XReflectionFailure("Illegal class for field access of "+f.getName() + ": " + e.getMessage());
291		} catch (IllegalAccessException e) {
292             // the read field is not publicly accessible
293			throw new XReflectionFailure("field access of " + f.getName() + " not accessible.");
294		}
295	}
296	
297	/**
298	 * Write a field in the given Java object reflectively.
299	 * @param toObject if null, the field is assumed to be static
300	 * @param value the AmbientTalk value which will be converted into its Java equivalent to be written int he field
301	 */
302	public static void writeField(Object toObject, Class ofClass, String fieldName, ATObject value)
303	                              throws InterpreterException {
304		Field f = getField(ofClass, fieldName, (toObject == null));
305		writeField(toObject, f, value);
306	}
307	
308	/**
309	 * Write a field in the given Java object reflectively.
310	 * @param value the AmbientTalk value which will be converted into its Java equivalent to be written int he field
311	 */
312	public static void writeField(Object toObject, Field f, ATObject value) throws InterpreterException {
313		try {
314			f.set(toObject, Symbiosis.ambientTalkToJava(value, f.getType()));
315		} catch (IllegalArgumentException e) {
316			// the given value is of the wrong type
317			throw new XIllegalArgument("Illegal value for field "+f.getName() + ": " + e.getMessage());
318		} catch (IllegalAccessException e) {
319             // the read field is not publicly accessible or final
320			throw new XUnassignableField(Reflection.downSelector(f.getName()).toString());
321		}
322	}
323	
324	/**
325	 * Query whether the given Java Class contains a (non-)static method with the given selector
326	 */
327	public static boolean hasMethod(Class c, String selector, boolean isStatic) {
328		Method[] methods = c.getMethods();
329		for (int i = 0; i < methods.length; i++) {
330			if (Modifier.isStatic(methods[i].getModifiers()) == isStatic) {
331				if (methods[i].getName().equals(selector)) {
332					return true;
333				}
334			}
335		}
336		return false;
337	}
338	
339	/**
340	 * Query whether the given Java Class contains a (non-)static field with the given selector
341	 */
342	public static boolean hasField(Class c, String selector, boolean isStatic) {
343		try {
344			Field f = c.getField(selector);
345			return (Modifier.isStatic(f.getModifiers()) == isStatic);
346		} catch (NoSuchFieldException e) {
347			return false;
348		}
349	}
350	
351	/**
352	 * Retrieve a field from a Java object.
353	 * @throws XUndefinedSlot if the field does not exist or its static property does not match
354	 */
355	public static Field getField(Class fromClass, String fieldName, boolean isStatic) throws XUndefinedSlot {
356		try {
357			Field f = fromClass.getField(fieldName);
358			if ((Modifier.isStatic(f.getModifiers())) == isStatic) {
359				return f;
360			} else {
361				throw new XUndefinedSlot("field access ", fieldName + " not accessible.");
362			}
363		} catch (NoSuchFieldException e) {
364			// the field does not exist
365			throw new XUndefinedSlot("field access ", fieldName + " not accessible.");
366		}
367	}
368	
369	/**
370	 * Retrieve all methods of a given name from a Java object. These are bundled together
371	 * in a first-class JavaMethod object, which is cached for later reference.
372	 * 
373	 * A null return value indicates no matches.
374	 */
375	public static JavaMethod getMethods(Class fromClass, String selector, boolean isStatic) {
376		// first, check the method cache
377		JavaMethod cachedEntry = JMethodCache._INSTANCE_.get(fromClass, selector, isStatic);
378		if (cachedEntry != null) {
379			// cache hit
380			return cachedEntry;
381		} else {
382			// cache miss: assemble a new JavaMethod entry
383			Method[] methods = fromClass.getMethods();
384			Method m;
385			Vector properMethods = new Vector(methods.length);
386			for (int i = 0; i < methods.length; i++) {
387				m = methods[i];
388				if ((Modifier.isStatic(m.getModifiers())) == isStatic && m.getName().equals(selector)) {
389					properMethods.add(methods[i]);
390				}
391			}
392			Method[] choices = (Method[]) properMethods.toArray(new Method[properMethods.size()]);
393			if (choices.length == 0) {
394				// no matches
395				return null;
396			} else {
397				// add entry to cache and return it
398				JavaMethod jMethod = new JavaMethod(choices);
399				JMethodCache._INSTANCE_.put(fromClass, selector, isStatic, jMethod);
400				return jMethod;
401			}
402		}
403	}
404	
405	/**
406	 * Retrieve all public static or non-static methods from a given Java class
407	 * (this includes methods defined in superclasses). All methods are properly wrapped in a
408	 * JavaMethod wrapper, taking care to wrap a set of overloaded methods using the same wrapper.
409	 * 
410	 * @param isStatic if true, all static methods of fromClass are returned, otherwise the instance methods are returned
411	 */
412	public static JavaMethod[] getAllMethods(Class fromClass, boolean isStatic) {
413		// assemble a set of all unique selectors of all (non-)static methods of the class
414		HashSet uniqueNames = new HashSet();
415		Method[] methods = fromClass.getMethods();
416		for (int i = 0; i < methods.length; i++) {
417			Method m = methods[i];
418			if ((Modifier.isStatic(m.getModifiers())) == isStatic) {
419				uniqueNames.add(m.getName());
420			}
421		}
422		
423		// create a JavaMethod[] array large enough to contain all 'unique methods'
424		JavaMethod[] jmethods = new JavaMethod[uniqueNames.size()];
425		// loop over all entries and group the methods into a single wrapper
426		int i = 0;
427		for (Iterator iter = uniqueNames.iterator(); iter.hasNext();) {
428			String methodName = (String) iter.next();
429			jmethods[i++] = getMethods(fromClass, methodName, isStatic);
430		}
431		return jmethods;
432	}
433	
434	/**
435	 * Retrieve all public static or non-static fields from a given Java class
436	 * (this includes fields defined in superclasses, but excludes shadowed superclass fields). All fields are properly wrapped in a
437	 * JavaField wrapper.
438	 * 
439	 * @param ofObject if null, all static fields of fromClass are returned, otherwise the instance fields are returned
440	 */
441	public static JavaField[] getAllFields(Object ofObject, Class fromClass) {
442		boolean isStatic = (ofObject == null);
443		Field[] fields = fromClass.getFields();
444		// we do not consider shadowed superclass fields, therefore we store all encountered fields
445		// in a table and only keep the field with the most specific class
446		Hashtable recordedFields = new Hashtable();
447		for (int i = 0; i < fields.length; i++) {
448			Field f = fields[i];
449			if ((Modifier.isStatic(f.getModifiers())) == isStatic) {
450				// did we already encounter this field?
451				if (recordedFields.contains(f.getName())) {
452					// yes, then compare encountered field with previous field and only store most specific one
453					Field prev = (Field) recordedFields.get(f.getName());
454					// is f's Class a subclass of prev's Class?
455					if (prev.getDeclaringClass().isAssignableFrom(f.getDeclaringClass())) {
456						// yes, so f is more specific, store it instead of prev
457						recordedFields.remove(prev.getName());
458						recordedFields.put(f.getName(), f);
459					} // if not, keep previous field
460				} else {
461					// field not encountered  yet, store it
462					recordedFields.put(f.getName(), f);
463				}
464			}
465		}
466		// create a JavaField[] array large enough to contain all entries in the table
467		JavaField[] jfields = new JavaField[recordedFields.size()];
468		// loop over all entries in the table and wrap each field
469		int i = 0;
470		for (Iterator iter = recordedFields.values().iterator(); iter.hasNext(); i++) {
471			jfields[i] = new JavaField(ofObject, (Field) iter.next());
472		}
473		return jfields;
474	}
475
476	/**
477	 * Convert a Java object into an AmbientTalk object, according to
478	 * the following rules:
479	 * <pre>
480	 * null = nil
481	 * ATObject obj = obj
482	 * int n = Number(n)
483	 * double d = Fraction(d)
484	 * boolean b = Boolean(b)
485	 * String s = Text(s)
486	 * T[] array = Table(array.length)
487	 * InterpreterException e = NATException(e)
488	 * Exception e = NATException(XJavaException(e))
489	 * Class c = JavaClass(c)
490	 * Object o = JavaObject(o)
491	 * </pre>
492	 * 
493	 * @param jObj the Java object representing a mirror or a native type
494	 * @return the same object if it implements the ATObject interface
495	 */
496	public static final ATObject javaToAmbientTalk(Object jObj) throws InterpreterException {
497		// -- NULL => NIL --
498	    if (jObj == null) {
499		  return OBJNil._INSTANCE_;
500		// -- AmbientTalk implementation-level objects --
501		// -- This also includes COERCED objects! (i.e. symbiotic objects) --
502	    } else if (jObj instanceof ATObject) {
503			return (ATObject) jObj;
504	    // -- PRIMITIVE TYPE => NUMERIC, TXT --
505		} else if (JavaInterfaceAdaptor.isPrimitiveType(jObj.getClass())) {
506		    return JavaInterfaceAdaptor.primitiveJavaToATObject(jObj);
507		// -- STRING => TEXT --
508		} else if (jObj instanceof String) {
509			return NATText.atValue((String) jObj);
510		// -- ARRAY => TABLE --
511		} else if (jObj.getClass().isArray()) {
512			int length = Array.getLength(jObj);
513			ATObject[] atTable = new ATObject[length];
514			for (int i = 0; i < length; i++) {
515				atTable[i] = javaToAmbientTalk(Array.get(jObj, i));
516			}
517			return NATTable.atValue(atTable);
518	    // -- EXCEPTION => NATEXCEPTION --
519		} else if(jObj instanceof InterpreterException) {
520			return ((InterpreterException)jObj).getAmbientTalkRepresentation();
521		} else if (jObj instanceof Exception) {
522			return new NATException(new XJavaException((Exception) jObj));
523		// -- java.lang.Class => Symbiotic Class --
524		} else if (jObj instanceof Class) {
525			return JavaClass.wrapperFor((Class) jObj);
526		// -- Object => Symbiotic AT Object --
527		} else {
528			return JavaObject.wrapperFor(jObj);
529		}
530	}
531
532	/**
533	 * Convert an AmbientTalk object into an equivalent Java object, according
534	 * to the following rules:
535	 * <pre>
536	 * Number n -> int = n.javaValue
537	 * Fraction f -> double = f.javaValue
538	 * Boolean b -> boolean = b.javaValue
539	 * Text t -> String = t.javaValue
540	 * JavaObject jobj -> T = (T) jobj.wrappedObject
541	 * ATObject obj -> ATObject = obj
542	 * Table obj -> T[] = new T[obj.length]
543	 * NATException exc -> Exception = exc.wrappedException
544	 * JavaClass jcls -> Class = jcls.wrappedClass
545	 * nil -> Object = null
546	 * ATObject obj -> Interface = Coercer<obj,Interface>
547	 * </pre>
548	 * @param atObj the AmbientTalk object to convert to a Java value
549	 * @param targetType the known static type of the Java object that should be attained
550	 * @return a Java object o where (o instanceof targetType) should yield true
551	 * 
552	 * @throws XTypeMismatch if the object cannot be converted into the correct Java targetType
553	 */
554	public static final Object ambientTalkToJava(ATObject atObj, Class targetType) throws InterpreterException {
555		// -- PRIMITIVE TYPES --
556        if (JavaInterfaceAdaptor.isPrimitiveType(targetType)) {
557		    return JavaInterfaceAdaptor.atObjectToPrimitiveJava(atObj, targetType);
558		// -- WRAPPED JAVA OBJECTS --
559        } else if (atObj.isJavaObjectUnderSymbiosis()) {
560	    	Object jObj = atObj.asJavaObjectUnderSymbiosis().getWrappedObject();
561		    Class jCls = jObj.getClass();
562		    // dynamic subtype test: is jCls a subclass of targetType?
563		    if (targetType.isAssignableFrom(jCls)) {
564		    	return jObj;
565		    }
566	    }
567        
568        // -- IMPLEMENTATION-LEVEL OBJECTS --
569        if (targetType.isInstance(atObj)) {
570			// target type is a subtype of ATObject, return the implementation-level object itself
571        	// also, it may be that atObj is actually a coerced AT object, so it may
572        	// directly match a target Java interface
573			return atObj;
574		// -- STRINGS --
575		} else if (targetType == String.class) {
576			return atObj.asNativeText().javaValue;
577		// -- ARRAYS --
578		} else if (targetType.isArray()) {
579			ATObject[] atArray = atObj.asNativeTable().elements_;
580			Object jArray = Array.newInstance(targetType.getComponentType(), atArray.length);
581			for (int i = 0; i < Array.getLength(jArray); i++) {
582				Array.set(jArray, i, ambientTalkToJava(atArray[i], targetType.getComponentType()));
583			}
584			return jArray;
585		// -- EXCEPTIONS --
586		} else if (Exception.class.isAssignableFrom(targetType)) {
587			return Evaluator.asNativeException(atObj);
588		// -- CLASS OBJECTS --
589		} else if (targetType == Class.class) {
590			return atObj.asJavaClassUnderSymbiosis().getWrappedClass();
591	    // -- nil => NULL --
592		} else if (atObj == OBJNil._INSTANCE_) {
593			return null;
594		// -- INTERFACE TYPES AND NAT CLASSES --
595		} else {
596			// only allow NATObject and subclasses to be coerced into an interface
597			if (atObj.isAmbientTalkObject()) {
598				return Coercer.coerce(atObj, targetType);
599			} else {
600				throw new XTypeMismatch(targetType, atObj);
601			}
602		}
603	}
604	
605	/**
606	 * Returns whether the symbiosis layer should process the given method purely
607	 * asynchronously or not.
608	 * 
609	 * @return whether the specified Java method denotes an event notification
610	 */
611	public static boolean isEvent(Method method) {
612		return EventListener.class.isAssignableFrom(method.getDeclaringClass()) // is an EventListener
613		    && (method.getReturnType() == Void.TYPE) // does not return a value
614		    && (method.getExceptionTypes().length == 0); // throws no exceptions
615	}
616	
617	private static ATObject invokeUniqueSymbioticMethod(Object symbiont, Method javaMethod, Object[] jArgs) throws InterpreterException {
618		try {
619			return Symbiosis.javaToAmbientTalk(javaMethod.invoke(symbiont, jArgs));
620		} catch (IllegalAccessException e) {
621			// the invoked method is not publicly accessible
622			// sometimes this may happen when accessing inner classes, try again with an interface method:
623			Method interfaceMethod = toInterfaceMethod(javaMethod);
624			if (interfaceMethod == null) { // no success
625				// try to perform the call without access protection
626				if (!javaMethod.isAccessible()) {
627					javaMethod.setAccessible(true);
628					return invokeUniqueSymbioticMethod(symbiont, javaMethod, jArgs);
629				} else {
630					// if access protection was already disabled, bail out
631		            throw new XReflectionFailure("Java method "+Reflection.downSelector(javaMethod.getName()) + " is not accessible.", e);
632				}
633			} else {
634				return invokeUniqueSymbioticMethod(symbiont, interfaceMethod, jArgs);
635			}
636		} catch (IllegalArgumentException e) {
637			// illegal argument types were supplied, should not happen because the conversion should have already failed earlier (in atArgsToJavaArgs)
638            // Backport from JDK 1.4 to 1.3
639            // throw new RuntimeException("[broken at2java conversion?] Illegal argument for Java method "+javaMethod.getName(), e);
640			throw new RuntimeException("[broken at2java conversion?] Illegal argument for Java method "+javaMethod.getName());
641		} catch (InvocationTargetException e) {
642			// the invoked method threw an exception
643			if (e.getTargetException() instanceof InterpreterException)
644				throw (InterpreterException) e.getTargetException();
645			else if (e.getTargetException() instanceof Signal) {
646			    throw (Signal) e.getTargetException();	
647			} else {
648				throw new XJavaException(symbiont, javaMethod, e.getTargetException());
649		    }
650		}
651	}
652	
653	private static ATObject invokeUniqueSymbioticConstructor(Constructor ctor, Object[] jArgs) throws InterpreterException {
654		try {
655			return Symbiosis.javaToAmbientTalk(ctor.newInstance(jArgs));
656		} catch (IllegalAccessException e) {
657			// the invoked method is not publicly accessible
658			throw new XReflectionFailure("Java constructor "+Reflection.downSelector(ctor.getName()) + " is not accessible.", e);
659		} catch (IllegalArgumentException e) {
660			// illegal argument types were supplied, should not happen because the conversion should have already failed earlier (in atArgsToJavaArgs)
661		    // Backport from JDK 1.4 to 1.3
662            // throw new RuntimeException("[broken at2java conversion?] Illegal argument for Java constructor "+ctor.getName(), e);
663			throw new RuntimeException("[broken at2java conversion?] Illegal argument for Java constructor "+ctor.getName());
664		} catch (InstantiationException e) {
665			// the given class is abstract
666			throw new XNotInstantiatable(ctor.getDeclaringClass(), e);
667		} catch (InvocationTargetException e) {
668			// the invoked method threw an exception
669			if (e.getTargetException() instanceof InterpreterException)
670				throw (InterpreterException) e.getTargetException();
671			else if (e.getTargetException() instanceof Signal) {
672			    throw (Signal) e.getTargetException();	
673			} else {
674				throw new XJavaException(null, ctor, e.getTargetException());
675		    }
676		}
677	}
678	
679	private static Object[] atArgsToJavaArgs(ATObject[] args, Class[] types) throws InterpreterException {
680		Object[] jArgs = new Object[args.length];
681		for (int i = 0; i < args.length; i++) {
682			jArgs[i] = Symbiosis.ambientTalkToJava(args[i], types[i]);
683		}
684		return jArgs;
685	}
686	
687	/**
688	 * Extremely vague and dirty feature of Java reflection: it can sometimes happen that
689	 * a method is invoked on a private inner class via a publicly accessible interface method.
690	 * In those cases, invoking that method results in an IllegalAccessException.
691	 * One example is invoking aVector.iterator().hasNext()
692	 * 
693	 * The problem is that aVector.iterator() returns an instance of java.util.AbstractList$Itr
694	 * which is probably private. Selecting that class's hasNext method and invoking it results in
695	 * an IllegalAccessException. This can be circumvented by invoking the hasNext method through
696	 * the java.util.Iterator interface class.
697	 */
698	private static Method toInterfaceMethod(Method m) {
699		Class[] interfaces = m.getDeclaringClass().getInterfaces();
700		if (interfaces == null) {
701			return null;
702		} else {
703			// find the method in one of the interface declarations
704			for (int i = 0; i < interfaces.length; i++) {
705				try {
706					return interfaces[i].getMethod(m.getName(), m.getParameterTypes());
707				} catch(NoSuchMethodException e) {
708					// continue searching
709				}
710			}
711			// no declared method found
712			return null;
713		}
714	}
715	
716}