PageRenderTime 59ms CodeModel.GetById 20ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://ambienttalk.googlecode.com/
Java | 642 lines | 383 code | 27 blank | 232 comment | 131 complexity | 9475039d1b3f56308f98a7bd477ae56b 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.exceptions.InterpreterException;
 31import edu.vub.at.exceptions.XArityMismatch;
 32import edu.vub.at.exceptions.XIllegalArgument;
 33import edu.vub.at.exceptions.XNotInstantiatable;
 34import edu.vub.at.exceptions.XReflectionFailure;
 35import edu.vub.at.exceptions.XSelectorNotFound;
 36import edu.vub.at.exceptions.XSymbiosisFailure;
 37import edu.vub.at.exceptions.XTypeMismatch;
 38import edu.vub.at.exceptions.XUnassignableField;
 39import edu.vub.at.exceptions.XUndefinedField;
 40import edu.vub.at.exceptions.signals.Signal;
 41import edu.vub.at.objects.ATObject;
 42import edu.vub.at.objects.coercion.Coercer;
 43import edu.vub.at.objects.mirrors.JavaInterfaceAdaptor;
 44import edu.vub.at.objects.mirrors.Reflection;
 45import edu.vub.at.objects.natives.NATException;
 46import edu.vub.at.objects.natives.NATNil;
 47import edu.vub.at.objects.natives.NATTable;
 48import edu.vub.at.objects.natives.NATText;
 49
 50import java.lang.reflect.Array;
 51import java.lang.reflect.Constructor;
 52import java.lang.reflect.Field;
 53import java.lang.reflect.InvocationTargetException;
 54import java.lang.reflect.Method;
 55import java.lang.reflect.Modifier;
 56import java.util.HashSet;
 57import java.util.Hashtable;
 58import java.util.Iterator;
 59import java.util.LinkedList;
 60import java.util.Vector;
 61
 62/**
 63 * The Symbiosis class is a container for auxiliary methods pertaining to making symbiotic
 64 * reflective Java invocations.
 65 * 
 66 * @author tvcutsem
 67 */
 68public final class Symbiosis {
 69
 70	public static ATObject symbioticInvocation(ATObject wrapper, Object symbiont, Class ofClass, String selector, ATObject[] atArgs) throws InterpreterException {
 71		return symbioticInvocation(wrapper, symbiont, selector, getMethods(ofClass, selector, (symbiont==null)), atArgs);
 72	}
 73	
 74	/**
 75	 * The Java method invocation algorithm is as follows:
 76	 * 
 77	 * case of # of methods matching selector:
 78	 *   0 => XSelectorNotFound
 79	 *   1 => invoke the method OR XIllegalArgument, XArityMismatch, XReflectionFailure
 80	 *   * => (case of # of methods with matching arity OR taking varargs:
 81	 *           0 => XSymbiosisFailure
 82	 *           1 => invoke the method OR XIllegalArgument, XReflectionFailure
 83	 *           * => (case of # of methods matching 'default type' of the actual arguments:
 84	 *                   0 => XSymbiosisFailure
 85	 *                   1 => invoke OR XReflectionFailure
 86	 *                   * => XSymbiosisFailure))
 87	 * 
 88	 * A Java method takes a variable number of AT arguments <=> it has one formal parameter of type ATObject[]
 89	 * 
 90	 * @param wrapper the ATObject wrapper for the symbiont
 91	 * @param symbiont the Java object being accessed from within AmbientTalk
 92	 * @param selector the Java selector corresponding to the method invocation
 93	 * @param jMethod a JavaMethod encapsulating all applicable Java methods that correspond to the selector
 94	 * @param atArgs the AT args to the symbiotic invocation
 95	 * @return the wrapped result of the Java method invocation
 96	 * 
 97	 * @throws XArityMismatch if the wrong number of arguments were supplied
 98	 * @throws XSelectorNotFound if no methods correspond to the given selector (i.e. jMethod is null)
 99	 * @throws XTypeMismatch if one of the arguments cannot be converted into the static type expected by the Java method
100	 * @throws XSymbiosisFailure if the method is overloaded and cannot be unambiguously resolved given the actual arguments
101	 * @throws XReflectionFailure if the invoked method is not accessible from within AmbientTalk
102	 * @throws XJavaException if the invoked Java method throws a Java exception
103	 */
104	public static ATObject symbioticInvocation(ATObject wrapper, Object symbiont, String selector, JavaMethod jMethod, ATObject[] atArgs)
105	                                           throws InterpreterException {
106		if (jMethod == null) {
107		    // no methods found? selector does not exist...
108			throw new XSelectorNotFound(Reflection.downSelector(selector), wrapper);
109		} else {
110			Method[] methods = jMethod.choices_;
111			if (methods.length == 1) {
112				// just one method found, no need to resolve overloaded methods
113				// if the Java method takes an ATObject array as its sole parameter, it is interpreted as taking
114				// a variable number of ambienttalk arguments
115				Class[] params = methods[0].getParameterTypes();
116				Object[] args;
117				if ((params.length == 1) && params[0].equals(ATObject[].class)) {
118					args = new Object[] { atArgs };
119				} else {
120					if (params.length != atArgs.length) {
121						throw new XArityMismatch("Java method "+Reflection.downSelector(methods[0].getName()), params.length, atArgs.length);
122					}
123					// make sure to properly 'coerce' each argument into the proper AT interface type
124					args = atArgsToJavaArgs(atArgs, params);
125				}
126				return invokeUniqueSymbioticMethod(symbiont, methods[0], args);
127			} else {	
128				// overloading: filter out all methods that do not match arity or whose
129				// argument types do not match
130				Object[] actuals = null;
131				Class[] params;
132				LinkedList matchingMethods = new LinkedList();
133				for (int i = 0; i < methods.length; i++) {
134					params = methods[i].getParameterTypes();
135					// is the method a varargs method?
136					if ((params.length == 1) && params[0].equals(ATObject[].class)) {
137						actuals = new Object[] { atArgs };
138						matchingMethods.addFirst(methods[i]);
139					// does the arity match?
140					} else if (params.length == atArgs.length) {
141						// can it be invoked with the given actuals?
142						try {
143							actuals = atArgsToJavaArgs(atArgs, params);
144							matchingMethods.addFirst(methods[i]);
145						} catch(XTypeMismatch e) {
146							// types don't match
147						}
148					} else {
149				      // arity does not match
150					}
151				}
152				
153				switch (matchingMethods.size()) {
154				    case 0: {
155					    // no methods left: overloading resolution failed
156					    throw new XSymbiosisFailure(symbiont, selector, atArgs);
157				    }
158				    case 1: {
159				    	// just one method left, invoke it
160						return invokeUniqueSymbioticMethod(symbiont, (Method) matchingMethods.getFirst(), actuals);
161				    }
162				    default: {
163				    	// more than one method left: overloading resolution failed
164						throw new XSymbiosisFailure(symbiont, selector, matchingMethods, atArgs);
165				    }
166				}
167			}
168		}
169	}
170	
171	/**
172	 * Creates a new instance of a Java class.
173	 * 
174	 * @param ofClass the Java class of which to create an instance
175	 * @param atArgs the AmbientTalk arguments to the constructor, to be converted to Java arguments
176	 * @return an unitialized JavaObject wrapper around a newly created instance of the class
177	 * 
178	 * @throws XArityMismatch if the wrong number of arguments were supplied
179	 * @throws XNotInstantiatable if no public constructors are available or if the class is abstract
180	 * @throws XTypeMismatch if one of the arguments cannot be converted into the static type expected by the constructor
181	 * @throws XSymbiosisFailure if the constructor is overloaded and cannot be unambiguously resolved given the actual arguments
182	 * @throws XReflectionFailure if the invoked constructor is not accessible from within AmbientTalk
183	 * @throws XJavaException if the invoked Java constructor throws a Java exception
184	 */
185	public static ATObject symbioticInstanceCreation(Class ofClass, ATObject[] atArgs) throws InterpreterException {
186		Constructor[] ctors = ofClass.getConstructors();
187		switch (ctors.length) {
188		     // no constructors found? class is not instantiatable...
189		case 0:
190			throw new XNotInstantiatable(ofClass);
191			// just one constructor found, no need to resolve overloaded methods
192		case 1: {
193			// if the constructor takes an ATObject array as its sole parameter, it is interpreted as taking
194			// a variable number of ambienttalk arguments
195			Class[] params = ctors[0].getParameterTypes();
196			Object[] args;
197			if ((params.length == 1) && params[0].equals(ATObject[].class)) {
198				args = new Object[] { atArgs };
199			} else {
200				if (params.length != atArgs.length) {
201					throw new XArityMismatch("Java constructor "+Reflection.downSelector(ctors[0].getName()), params.length, atArgs.length);
202				}
203				// make sure to properly convert actual arguments into Java objects
204				args = atArgsToJavaArgs(atArgs, params);
205			}
206			return invokeUniqueSymbioticConstructor(ctors[0], args);
207		  }	
208		}
209		
210		// overloading: filter out all constructors that do not match arity or whose argument types do not match
211		int matchingCtors = 0;
212		Constructor matchingCtor = null;
213		Object[] actuals = null;
214		Class[] params;
215		for (int i = 0; i < ctors.length; i++) {
216			params = ctors[i].getParameterTypes();
217			// is the constructor a varargs constructor?
218			if ((params.length == 1) && params[0].equals(ATObject[].class)) {
219				actuals = new Object[] { atArgs };
220				matchingCtor = ctors[i];
221				matchingCtors++;
222				// does the arity match?
223			} else if (params.length == atArgs.length) {
224				// can it be invoked with the given actuals?
225				try {
226					actuals = atArgsToJavaArgs(atArgs, params);
227					matchingCtor = ctors[i];
228					matchingCtors++;
229				} catch(XTypeMismatch e) {
230					// types don't match
231					ctors[i] = null; // TODO: don't assign to null, array may be cached or used later on (or by wrapper method)
232				}
233			} else {
234				// arity does not match
235				ctors[i] = null;
236			}
237		}
238		
239		if (matchingCtors != 1) {
240			// no constructors left or more than one constructor left? overloading resolution failed
241			throw new XSymbiosisFailure(ofClass, ctors, atArgs, matchingCtors);
242		} else {
243			// just one constructor left, invoke it
244			return invokeUniqueSymbioticConstructor(matchingCtor, actuals);
245		}
246	}
247	
248	/**
249	 * Read a field from the given Java object reflectively.
250	 * @return the contents of the Java field, converted into its AmbientTalk equivalent
251	 */
252	public static ATObject readField(Object fromObject, Class ofClass, String fieldName)
253	                     throws InterpreterException {
254		Field f = getField(ofClass, fieldName, (fromObject == null));
255		return readField(fromObject, f);
256	}
257	
258	/**
259	 * Read a field from the given Java object reflectively.
260	 * @return the contents of the Java field, converted into its AmbientTalk equivalent
261	 */
262	public static ATObject readField(Object fromObject, Field f) throws InterpreterException {
263		try {
264			return Symbiosis.javaToAmbientTalk(f.get(fromObject));
265		} catch (IllegalArgumentException e) {
266			// the given object is of the wrong class, should not happen!
267			throw new XReflectionFailure("Illegal class for field access of "+f.getName() + ": " + e.getMessage());
268		} catch (IllegalAccessException e) {
269             // the read field is not publicly accessible
270			throw new XReflectionFailure("field access of " + f.getName() + " not accessible.");
271		}
272	}
273	
274	/**
275	 * Write a field in the given Java object reflectively.
276	 * @param toObject if null, the field is assumed to be static
277	 * @param value the AmbientTalk value which will be converted into its Java equivalent to be written int he field
278	 */
279	public static void writeField(Object toObject, Class ofClass, String fieldName, ATObject value)
280	                              throws InterpreterException {
281		Field f = getField(ofClass, fieldName, (toObject == null));
282		writeField(toObject, f, value);
283	}
284	
285	/**
286	 * Write a field in the given Java object reflectively.
287	 * @param value the AmbientTalk value which will be converted into its Java equivalent to be written int he field
288	 */
289	public static void writeField(Object toObject, Field f, ATObject value) throws InterpreterException {
290		try {
291			f.set(toObject, Symbiosis.ambientTalkToJava(value, f.getType()));
292		} catch (IllegalArgumentException e) {
293			// the given value is of the wrong type
294			throw new XIllegalArgument("Illegal value for field "+f.getName() + ": " + e.getMessage());
295		} catch (IllegalAccessException e) {
296             // the read field is not publicly accessible or final
297			throw new XUnassignableField(Reflection.downSelector(f.getName()).toString());
298		}
299	}
300	
301	/**
302	 * Query whether the given Java Class contains a (non-)static method with the given selector
303	 */
304	public static boolean hasMethod(Class c, String selector, boolean isStatic) {
305		Method[] methods = c.getMethods();
306		for (int i = 0; i < methods.length; i++) {
307			if (Modifier.isStatic(methods[i].getModifiers()) == isStatic) {
308				if (methods[i].getName().equals(selector)) {
309					return true;
310				}
311			}
312		}
313		return false;
314	}
315	
316	/**
317	 * Query whether the given Java Class contains a (non-)static field with the given selector
318	 */
319	public static boolean hasField(Class c, String selector, boolean isStatic) {
320		try {
321			Field f = c.getField(selector);
322			return (Modifier.isStatic(f.getModifiers()) == isStatic);
323		} catch (NoSuchFieldException e) {
324			return false;
325		}
326	}
327	
328	/**
329	 * Retrieve a field from a Java object.
330	 * @throws XUndefinedField if the field does not exist or its static property does not match
331	 */
332	public static Field getField(Class fromClass, String fieldName, boolean isStatic) throws XUndefinedField {
333		try {
334			Field f = fromClass.getField(fieldName);
335			if ((Modifier.isStatic(f.getModifiers())) == isStatic) {
336				return f;
337			} else {
338				throw new XUndefinedField("field access ", fieldName + " not accessible.");
339			}
340		} catch (NoSuchFieldException e) {
341			// the field does not exist
342			throw new XUndefinedField("field access ", fieldName + " not accessible.");
343		}
344	}
345	
346	/**
347	 * Retrieve all methods of a given name from a Java object. These are bundled together
348	 * in a first-class JavaMethod object, which is cached for later reference.
349	 * 
350	 * A null return value indicates no matches.
351	 */
352	public static JavaMethod getMethods(Class fromClass, String selector, boolean isStatic) {
353		// first, check the method cache
354		JavaMethod cachedEntry = JMethodCache._INSTANCE_.get(fromClass, selector, isStatic);
355		if (cachedEntry != null) {
356			// cache hit
357			return cachedEntry;
358		} else {
359			// cache miss: assemble a new JavaMethod entry
360			Method[] methods = fromClass.getMethods();
361			Method m;
362			Vector properMethods = new Vector(methods.length);
363			for (int i = 0; i < methods.length; i++) {
364				m = methods[i];
365				if ((Modifier.isStatic(m.getModifiers())) == isStatic && m.getName().equals(selector)) {
366					properMethods.add(methods[i]);
367				}
368			}
369			Method[] choices = (Method[]) properMethods.toArray(new Method[properMethods.size()]);
370			if (choices.length == 0) {
371				// no matches
372				return null;
373			} else {
374				// add entry to cache and return it
375				JavaMethod jMethod = new JavaMethod(choices);
376				JMethodCache._INSTANCE_.put(fromClass, selector, isStatic, jMethod);
377				return jMethod;
378			}
379		}
380	}
381	
382	/**
383	 * Retrieve all public static or non-static methods from a given Java class
384	 * (this includes methods defined in superclasses). All methods are properly wrapped in a
385	 * JavaMethod wrapper, taking care to wrap a set of overloaded methods using the same wrapper.
386	 * 
387	 * @param isStatic if true, all static methods of fromClass are returned, otherwise the instance methods are returned
388	 */
389	public static JavaMethod[] getAllMethods(Class fromClass, boolean isStatic) {
390		// assemble a set of all unique selectors of all (non-)static methods of the class
391		HashSet uniqueNames = new HashSet();
392		Method[] methods = fromClass.getMethods();
393		for (int i = 0; i < methods.length; i++) {
394			Method m = methods[i];
395			if ((Modifier.isStatic(m.getModifiers())) == isStatic) {
396				uniqueNames.add(m.getName());
397			}
398		}
399		
400		// create a JavaMethod[] array large enough to contain all 'unique methods'
401		JavaMethod[] jmethods = new JavaMethod[uniqueNames.size()];
402		// loop over all entries and group the methods into a single wrapper
403		int i = 0;
404		for (Iterator iter = uniqueNames.iterator(); iter.hasNext();) {
405			String methodName = (String) iter.next();
406			jmethods[i++] = getMethods(fromClass, methodName, isStatic);
407		}
408		return jmethods;
409	}
410	
411	/**
412	 * Retrieve all public static or non-static fields from a given Java class
413	 * (this includes fields defined in superclasses, but excludes shadowed superclass fields). All fields are properly wrapped in a
414	 * JavaField wrapper.
415	 * 
416	 * @param ofObject if null, all static fields of fromClass are returned, otherwise the instance fields are returned
417	 */
418	public static JavaField[] getAllFields(Object ofObject, Class fromClass) {
419		boolean isStatic = (ofObject == null);
420		Field[] fields = fromClass.getFields();
421		// we do not consider shadowed superclass fields, therefore we store all encountered fields
422		// in a table and only keep the field with the most specific class
423		Hashtable recordedFields = new Hashtable();
424		for (int i = 0; i < fields.length; i++) {
425			Field f = fields[i];
426			if ((Modifier.isStatic(f.getModifiers())) == isStatic) {
427				// did we already encounter this field?
428				if (recordedFields.contains(f.getName())) {
429					// yes, then compare encountered field with previous field and only store most specific one
430					Field prev = (Field) recordedFields.get(f.getName());
431					// is f's Class a subclass of prev's Class?
432					if (prev.getDeclaringClass().isAssignableFrom(f.getDeclaringClass())) {
433						// yes, so f is more specific, store it instead of prev
434						recordedFields.remove(prev.getName());
435						recordedFields.put(f.getName(), f);
436					} // if not, keep previous field
437				} else {
438					// field not encountered  yet, store it
439					recordedFields.put(f.getName(), f);
440				}
441			}
442		}
443		// create a JavaField[] array large enough to contain all entries in the table
444		JavaField[] jfields = new JavaField[recordedFields.size()];
445		// loop over all entries in the table and wrap each field
446		int i = 0;
447		for (Iterator iter = recordedFields.values().iterator(); iter.hasNext(); i++) {
448			jfields[i] = new JavaField(ofObject, (Field) iter.next());
449		}
450		return jfields;
451	}
452
453	/**
454	 * Convert a Java object into an AmbientTalk object.
455	 * 
456	 * @param jObj the Java object representing a mirror or a native type
457	 * @return the same object if it implements the ATObject interface
458	 */
459	public static final ATObject javaToAmbientTalk(Object jObj) throws InterpreterException {
460		// -- IMPLEMENTATION-level OBJECTS --
461		if (jObj instanceof ATObject) {
462			// the object is already an AmbientTalk object
463			return (ATObject) jObj;
464		}
465		// -- NULL => NIL --
466	    if (jObj == null) {
467		  return NATNil._INSTANCE_;
468		// -- AmbientTalk implementation-level objects --
469	    } else if(jObj instanceof ATObject) {
470			return (ATObject) jObj;
471	    // -- PRIMITIVE TYPE => NUMERIC, TXT --
472		} else if (JavaInterfaceAdaptor.isPrimitiveType(jObj.getClass())) {
473		    return JavaInterfaceAdaptor.primitiveJavaToATObject(jObj);
474		// -- STRING => TEXT --
475		} else if (jObj instanceof String) {
476			return NATText.atValue((String) jObj);
477		// -- ARRAY => TABLE --
478		} else if (jObj.getClass().isArray()) {
479			int length = Array.getLength(jObj);
480			ATObject[] atTable = new ATObject[length];
481			for (int i = 0; i < length; i++) {
482				atTable[i] = javaToAmbientTalk(Array.get(jObj, i));
483			}
484			return NATTable.atValue(atTable);
485	    // -- EXCEPTION => NATEXCEPTION --
486		} else if(jObj instanceof InterpreterException) {
487			return ((InterpreterException)jObj).getAmbientTalkRepresentation();
488		} else if (jObj instanceof Exception) {
489			return new NATException(new XJavaException((Exception) jObj));
490		// -- Symbiotic AmbientTalk object => AmbientTalk object --
491		} else if (jObj instanceof SymbioticATObjectMarker) {
492			return ((SymbioticATObjectMarker) jObj)._returnNativeAmbientTalkObject();
493		// -- java.lang.Class => Symbiotic Class --
494		} else if (jObj instanceof Class) {
495			return JavaClass.wrapperFor((Class) jObj);
496		// -- Object => Symbiotic AT Object --
497		} else {
498			return JavaObject.wrapperFor(jObj);
499		}
500	}
501
502	/**
503	 * Convert an AmbientTalk object into an equivalent Java object.
504	 * @param atObj the AmbientTalk object to convert to a Java value
505	 * @param targetType the known static type of the Java object that should be attained
506	 * @return a Java object o where (o instanceof targetType) should yield true
507	 * 
508	 * @throws XTypeMismatch if the object cannot be converted into the correct Java targetType
509	 */
510	public static final Object ambientTalkToJava(ATObject atObj, Class targetType) throws InterpreterException {
511		// -- WRAPPED JAVA OBJECTS --
512	    if (atObj.isJavaObjectUnderSymbiosis()) {
513	    		Object jObj = atObj.asJavaObjectUnderSymbiosis().getWrappedObject();
514		    Class jCls = jObj.getClass();
515		    // dynamic subtype test: is jCls a subclass of targetType?
516		    if (targetType.isAssignableFrom(jCls)) {
517		    		return jObj;
518		    } else {
519		    		throw new XTypeMismatch(targetType, atObj);
520		    }
521		// -- IMPLEMENTATION-LEVEL OBJECTS --
522	    } else if (targetType.isInstance(atObj)) {
523			// target type is a subtype of ATObject, return the implementation-level object itself
524			return atObj;
525		// -- PRIMITIVE TYPES --
526	    } else if (JavaInterfaceAdaptor.isPrimitiveType(targetType)) {
527			return JavaInterfaceAdaptor.atObjectToPrimitiveJava(atObj, targetType);
528		// -- STRINGS --
529		} else if (targetType == String.class) {
530			return atObj.asNativeText().javaValue;
531		// -- ARRAYS --
532		} else if (targetType.isArray()) {
533			ATObject[] atArray = atObj.asNativeTable().elements_;
534			Object jArray = Array.newInstance(targetType.getComponentType(), atArray.length);
535			for (int i = 0; i < Array.getLength(jArray); i++) {
536				Array.set(jArray, i, ambientTalkToJava(atArray[i], targetType.getComponentType()));
537			}
538			return jArray;
539		// -- EXCEPTIONS --
540		} else if (targetType == Exception.class) {
541			return atObj.asNativeException().getWrappedException();
542		// -- CLASS OBJECTS --
543		} else if (targetType == Class.class) {
544			return atObj.asJavaClassUnderSymbiosis().getWrappedClass();
545	    // -- nil => NULL --
546		} else if (atObj == NATNil._INSTANCE_) {
547			return null;
548		// -- INTERFACE TYPES AND NAT CLASSES --
549		} else {
550			return Coercer.coerce(atObj, targetType);	
551		}
552	}
553	
554	private static ATObject invokeUniqueSymbioticMethod(Object symbiont, Method javaMethod, Object[] jArgs) throws InterpreterException {
555		try {
556			return Symbiosis.javaToAmbientTalk(javaMethod.invoke(symbiont, jArgs));
557		} catch (IllegalAccessException e) {
558			// the invoked method is not publicly accessible
559			// sometimes this may happen when accessing inner classes, try again with an interface method:
560			Method interfaceMethod = toInterfaceMethod(javaMethod);
561			if (interfaceMethod == null) { // no success
562			    throw new XReflectionFailure("Java method "+Reflection.downSelector(javaMethod.getName()) + " is not accessible.", e);
563			} else {
564				return invokeUniqueSymbioticMethod(symbiont, interfaceMethod, jArgs);
565			}
566		} catch (IllegalArgumentException e) {
567			// illegal argument types were supplied, should not happen because the conversion should have already failed earlier (in atArgsToJavaArgs)
568			throw new RuntimeException("[broken at2java conversion?] Illegal argument for Java method "+javaMethod.getName(), e);
569		} catch (InvocationTargetException e) {
570			// the invoked method threw an exception
571			if (e.getCause() instanceof InterpreterException)
572				throw (InterpreterException) e.getCause();
573			else if (e.getCause() instanceof Signal) {
574			    throw (Signal) e.getCause();	
575			} else {
576				throw new XJavaException(symbiont, javaMethod, e.getCause());
577		    }
578		}
579	}
580	
581	private static ATObject invokeUniqueSymbioticConstructor(Constructor ctor, Object[] jArgs) throws InterpreterException {
582		try {
583			return JavaObject.wrapperFor(ctor.newInstance(jArgs));
584		} catch (IllegalAccessException e) {
585			// the invoked method is not publicly accessible
586			throw new XReflectionFailure("Java constructor "+Reflection.downSelector(ctor.getName()) + " is not accessible.", e);
587		} catch (IllegalArgumentException e) {
588			// illegal argument types were supplied, should not happen because the conversion should have already failed earlier (in atArgsToJavaArgs)
589			throw new RuntimeException("[broken at2java conversion?] Illegal argument for Java constructor "+ctor.getName(), e);
590		} catch (InstantiationException e) {
591			// the given class is abstract
592			throw new XNotInstantiatable(ctor.getDeclaringClass(), e);
593		} catch (InvocationTargetException e) {
594			// the invoked method threw an exception
595			if (e.getCause() instanceof InterpreterException)
596				throw (InterpreterException) e.getCause();
597			else if (e.getCause() instanceof Signal) {
598			    throw (Signal) e.getCause();	
599			} else {
600				throw new XJavaException(null, ctor, e.getCause());
601		    }
602		}
603	}
604	
605	private static Object[] atArgsToJavaArgs(ATObject[] args, Class[] types) throws InterpreterException {
606		Object[] jArgs = new Object[args.length];
607		for (int i = 0; i < args.length; i++) {
608			jArgs[i] = Symbiosis.ambientTalkToJava(args[i], types[i]);
609		}
610		return jArgs;
611	}
612	
613	/**
614	 * Extremely vague and dirty feature of Java reflection: it can sometimes happen that
615	 * a method is invoked on a private inner class via a publicly accessible interface method.
616	 * In those cases, invoking that method results in an IllegalAccessException.
617	 * One example is invoking aVector.iterator().hasNext()
618	 * 
619	 * The problem is that aVector.iterator() returns an instance of java.util.AbstractList$Itr
620	 * which is probably private. Selecting that class's hasNext method and invoking it results in
621	 * an IllegalAccessException. This can be circumvented by invoking the hasNext method through
622	 * the java.util.Iterator interface class.
623	 */
624	private static Method toInterfaceMethod(Method m) {
625		Class[] interfaces = m.getDeclaringClass().getInterfaces();
626		if (interfaces == null) {
627			return null;
628		} else {
629			// find the method in one of the interface declarations
630			for (int i = 0; i < interfaces.length; i++) {
631				try {
632					return interfaces[i].getMethod(m.getName(), m.getParameterTypes());
633				} catch(NoSuchMethodException e) {
634					// continue searching
635				}
636			}
637			// no declared method found
638			return null;
639		}
640	}
641	
642}