PageRenderTime 45ms CodeModel.GetById 11ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/at_build150307/src/edu/vub/at/objects/mirrors/Reflection.java

http://ambienttalk.googlecode.com/
Java | 755 lines | 328 code | 47 blank | 380 comment | 55 complexity | 18c0da26f70a01477ad28443b966faf4 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * Reflection.java created on 10-aug-2006 at 16:19:17
  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.mirrors;
 29
 30import edu.vub.at.exceptions.InterpreterException;
 31import edu.vub.at.exceptions.XArityMismatch;
 32import edu.vub.at.exceptions.XIllegalArgument;
 33import edu.vub.at.exceptions.XSelectorNotFound;
 34import edu.vub.at.exceptions.XUndefinedField;
 35import edu.vub.at.objects.ATField;
 36import edu.vub.at.objects.ATMethod;
 37import edu.vub.at.objects.ATObject;
 38import edu.vub.at.objects.ATTable;
 39import edu.vub.at.objects.grammar.ATSymbol;
 40import edu.vub.at.objects.natives.NATException;
 41import edu.vub.at.objects.natives.NATTable;
 42import edu.vub.at.objects.natives.grammar.AGSymbol;
 43import edu.vub.at.objects.symbiosis.Symbiosis;
 44
 45import java.lang.reflect.Method;
 46import java.util.Vector;
 47import java.util.regex.Matcher;
 48import java.util.regex.Pattern;
 49
 50/**
 51 * Reflection is an auxiliary class meant to serve as a repository for methods
 52 * related to 'up' and 'down' Java values properly into and out of the AmbientTalk base level.
 53 * 
 54 * Keep the following mental picture in mind:
 55 * 
 56 *                ^        Java = implementation-level          |
 57 *  to deify      |        Java AT objects = meta-level         | to reify
 58 *  (= to up)     | ------------------------------------------  | (= to down)
 59 *  (= to absorb) |         AmbientTalk = base-level            v (= to reflect)
 60 * 
 61 * Although deification and reification are more accurate terms, we will use 'up' and 'down'
 62 * because they are the clearest terminology, and clarity matters.
 63 * 
 64 * In this class, the following conventions hold:
 65 *  - methods start with either 'up' or 'down', denoting whether they deify or reify something
 66 *  - arguments start with either 'j' or 'at', denoting whether they represent Java or AmbientTalk values
 67 *  With 'java values' is meant 'java objects representing mirrors'
 68 * 
 69 * @author tvc
 70 */
 71public final class Reflection {
 72	
 73	public static final String _BASE_PREFIX_ = "base_";
 74	public static final String _BGET_PREFIX_ = "base_get";
 75	public static final String _BSET_PREFIX_ = "base_set";
 76	
 77	public static final String _META_PREFIX_ = "meta_";
 78	public static final String _MGET_PREFIX_ = "meta_get";
 79	public static final String _MSET_PREFIX_ = "meta_set";
 80	
 81	/**
 82	 * A selector passed from the Java to the AmbientTalk level undergoes the following transformations:
 83	 * 
 84	 * - any pattern of the form _op{code}_ is transformed to a symbol corresponding to the operator code
 85	 *  Operator codes are:
 86	 *   pls -> +
 87	 *   mns -> -
 88	 *   tms -> *
 89	 *   div -> /
 90	 *   bsl -> \
 91	 *   and -> &
 92	 *   not -> !
 93	 *   gtx -> >
 94	 *   ltx -> <
 95	 *   eql -> =
 96	 *   til -> ~
 97	 *   que -> ?
 98	 *   rem -> %
 99	 * - any underscores (_) are replaced by colons (:)
100	 */
101	public static final ATSymbol downSelector(String jSelector) {
102		return AGSymbol.jAlloc(javaToAmbientTalkSelector(jSelector));
103	}
104	
105	/**
106	 * Transforms a Java selector prefixed with base_ into an AmbientTalk selector without the prefix.
107	 */
108	public static final ATSymbol downBaseLevelSelector(String jSelector) throws InterpreterException {
109		if (jSelector.startsWith(Reflection._BASE_PREFIX_)) {
110			return downSelector(stripPrefix(jSelector, Reflection._BASE_PREFIX_));
111		} else if (jSelector.startsWith(Reflection._MGET_PREFIX_)) {
112			return downFieldName(stripPrefix(jSelector, Reflection._MGET_PREFIX_));
113		} else if (jSelector.startsWith(Reflection._MSET_PREFIX_)) {
114			return downFieldName(stripPrefix(jSelector, Reflection._MSET_PREFIX_));
115		} else if (jSelector.startsWith(Reflection._META_PREFIX_)) {
116			return downSelector(stripPrefix(jSelector, Reflection._META_PREFIX_));
117		} else {
118			throw new XIllegalArgument("Illegal base level selector to down: " + jSelector);
119		}
120	}
121	
122	/**
123	 * Transforms a Java selector prefixed with meta_ into an AmbientTalk selector without the prefix.
124	 */
125	public static final ATSymbol downMetaLevelSelector(String jSelector) throws InterpreterException {
126		if (jSelector.startsWith(Reflection._META_PREFIX_)) {
127			return downSelector(stripPrefix(jSelector, Reflection._META_PREFIX_));
128		} else {
129			throw new XIllegalArgument("Illegal meta level selector to down: " + jSelector);
130		}
131	}
132	
133	/**
134	 * Transforms a Java selector prefixed with base_get into an equivalent AmbientTalk selector.
135	 * 
136	 * Example:
137	 *  downBaseFieldAccessSelector("base_getReceiver") => ATSymbol("receiver")
138	 */
139	public static final ATSymbol downBaseFieldAccessSelector(String jSelector) throws InterpreterException {
140		if (jSelector.startsWith(Reflection._BGET_PREFIX_)) {
141			return downFieldName(stripPrefix(jSelector, Reflection._BGET_PREFIX_));
142		} else {
143			throw new XIllegalArgument("Illegal base level accessor to down: " + jSelector);
144		}
145	}
146
147	/**
148	 * Transforms a Java selector prefixed with base_set into an equivalent AmbientTalk selector.
149	 * 
150	 * Example:
151	 *  downBaseFieldMutationSelector("base_setReceiver") => ATSymbol("receiver")
152	 */
153	public static final ATSymbol downBaseFieldMutationSelector(String jSelector) throws InterpreterException {
154		if (jSelector.startsWith(Reflection._BSET_PREFIX_)) {
155			return downFieldName(stripPrefix(jSelector, Reflection._BSET_PREFIX_));
156		} else {
157			throw new XIllegalArgument("Illegal base level mutator to down: " + jSelector);
158		}
159	}
160
161	/**
162	 * Transforms a Java selector prefixed with meta_get into an equivalent AmbientTalk selector
163	 * 
164	 * Example:
165	 *  downMetaFieldAccessSelector("meta_getReceiver") => ATSymbol("receiver")
166	 */
167	public static final ATSymbol downMetaFieldAccessSelector(String jSelector) throws InterpreterException {
168		if (jSelector.startsWith(Reflection._MGET_PREFIX_)) {
169			return downFieldName(stripPrefix(jSelector, Reflection._MGET_PREFIX_));
170		} else {
171			throw new XIllegalArgument("Illegal meta level accessor to down: " + jSelector);
172		}
173	}
174	
175	/**
176	 * Transforms a Java selector prefixed with meta_set into an equivalent AmbientTalk selector.
177	 * 
178	 * Example:
179	 *  downMetaFieldMutationSelector("meta_setReceiver") => ATSymbol("receiver")
180	 */
181	public static final ATSymbol downMetaFieldMutationSelector(String jSelector) throws InterpreterException {
182		if (jSelector.startsWith(Reflection._MSET_PREFIX_)) {
183			return downFieldName(stripPrefix(jSelector, Reflection._MSET_PREFIX_));
184		} else {
185			throw new XIllegalArgument("Illegal meta level mutator to down: " + jSelector);
186		}
187	}
188	
189	/**
190	 * A field name "Field" passed from the Java to the AmbientTalk level undergoes the following transformations:
191	 * 
192	 *  - the same transformations applicable to downSelector
193	 *    @see Reflection#downSelector(String)
194	 *  - the first letter is transformed into lower case (as it was uppercased for Java conventions)
195	 */
196	public static final ATSymbol downFieldName(String jName) throws InterpreterException {
197		char[] charArray = jName.toCharArray();
198		charArray[0] = Character.toLowerCase(charArray[0]);
199		return downSelector(new String(charArray));
200	}
201	
202	/**
203	 * A selector passed from the AmbientTalk to the Java level undergoes the following transformations:
204	 * 
205	 * - any colons (:) are replaced by underscores (_)
206	 * - any operator symbol is replaced by _op{code}_ where code is generated as follows:
207	 *  Operator codes are:
208	 *   + -> pls
209	 *   - -> mns
210	 *   * -> tms
211	 *   / -> div
212	 *   \ -> bsl
213	 *   & -> and
214	 *   ! -> not
215	 *   > -> gtx
216	 *   < -> ltx
217	 *   = -> eql
218	 *   ~ -> til
219	 *   ? -> que
220	 *   % -> rem
221	 */
222	public static final String upSelector(ATSymbol atSelector) throws InterpreterException {
223		// : -> _
224		String nam = atSelector.base_getText().asNativeText().javaValue;
225		nam = nam.replaceAll(":", "_");
226		
227		// operator symbol -> _op{code}_
228		Matcher m = symbol.matcher(nam);
229		StringBuffer sb = new StringBuffer();
230		while (m.find()) {
231			// find every occurence of a non-word character and convert it into a symbol
232			String oprCode = symbol2oprCode(m.group(0));
233			// only add the _op prefix and _ postfix if the code has been found...
234			m.appendReplacement(sb, (oprCode.length() == 3) ? "_op" + oprCode  + "_" : oprCode);
235		}
236		m.appendTail(sb);
237		return sb.toString();
238	}
239	
240	/**
241	 * Transforms an AmbientTalk selector into a Java-level selector prefixed with base_.
242	 */
243	public static final String upBaseLevelSelector(ATSymbol atSelector) throws InterpreterException {
244		return Reflection._BASE_PREFIX_ + upSelector(atSelector);
245	}
246
247	/**
248	 * Transforms an AmbientTalk selector into a Java-level selector prefixed with meta_.
249	 */
250	public static final String upMetaLevelSelector(ATSymbol atSelector) throws InterpreterException {
251		return Reflection._META_PREFIX_ + upSelector(atSelector);
252	}
253	
254	/**
255	 * A field name "field" passed from the AmbientTalk to the Java level undergoes the following transformations:
256	 * 
257	 *  - the same transformations applicable to upSelector
258	 *    @see Reflection#upSelector(ATSymbol)
259	 *  - the first letter is transformed into upper case such that it can be accessed using respectively
260	 *    "getField" | "setField" methods at the Java level.  
261	 */
262	public static final String upFieldName(ATSymbol atName) throws InterpreterException {
263		char[] charArray = upSelector(atName).toCharArray();
264		charArray[0] = Character.toUpperCase(charArray[0]);
265		return new String(charArray);
266	}
267	
268	/**
269	 * Transforms an AmbientTalk selector into an equivalent Java selector uppercased and prefixed with "base_get".
270	 * 
271	 * Example:
272	 *  upBaseFieldAccessSelector(ATSymbol('receiver')) => "base_getReceiver"
273	 */
274	public static final String upBaseFieldAccessSelector(ATSymbol atName) throws InterpreterException {
275		return Reflection._BGET_PREFIX_ + upFieldName(atName);
276	}
277
278	/**
279	 * Transforms an AmbientTalk selector into an equivalent Java selector uppercased and prefixed with "base_set".
280	 * 
281	 * Example:
282	 *  upBaseFieldMutationSelector(ATSymbol('receiver')) => "base_setReceiver"
283	 */
284	public static final String upBaseFieldMutationSelector(ATSymbol atName) throws InterpreterException {
285		return Reflection._BSET_PREFIX_ + upFieldName(atName);
286	}
287
288	/**
289	 * Transforms an AmbientTalk selector into an equivalent Java selector uppercased and prefixed with "meta_get".
290	 * 
291	 * Example:
292	 *  upMetaFieldAccessSelector(ATSymbol('receiver')) => "meta_getReceiver"
293	 */
294	public static final String upMetaFieldAccessSelector(ATSymbol atName) throws InterpreterException {
295		return Reflection._MGET_PREFIX_ + upFieldName(atName);
296	}
297	
298	/**
299	 * Transforms an AmbientTalk selector into an equivalent Java selector uppercased and prefixed with "meta_set".
300	 * 
301	 * Example:
302	 *  upMetaFieldMutationSelector(ATSymbol('receiver')) => "meta_setReceiver"
303	 */
304	public static final String upMetaFieldMutationSelector(ATSymbol atName) throws InterpreterException {
305		return Reflection._MSET_PREFIX_ + upFieldName(atName);
306	}
307	
308	/**
309	 * Constructs an AmbientTalk ATField from a pair of accessor/mutator methods of
310	 * a Java object. Given an object obj and a String sel, it is checked whether
311	 *  a) obj has a method named getPrefix + Sel, if so, a field can be created
312	 *  b) obj has a method named setPrefix + Sel, if so, the field is mutable, otherwise it is read-only
313	 *
314	 * The accessor method cannot take any arguments, the mutator method must have a unary arity.
315	 *
316	 * @param natObject the native AT object in whose class the accessor/mutator methods should be found
317	 * @param atSelector the AmbientTalk name of the field
318	 * @return a reified field, which may either be read-only or mutable depending on available methods
319	 * 
320	 * Example:
321	 *  eval "(reflect: msg).getField('selector')" where msg is a NATMessage
322	 *  => downField(aNATMessage, "selector", "base_get", "base_set")
323	 *  => NATMessage must have a zero-arg method base_getSelector and optionally base_setSelector
324	 */
325	public static final ATField downField(ATObject natObject, ATSymbol atSelector,
326			                              String getPrefix, String setPrefix) throws InterpreterException {
327		String fieldName = upFieldName(atSelector);
328		try {
329			Method accessorMethod = JavaInterfaceAdaptor.getNativeATMethod(natObject.getClass(), natObject, getPrefix + fieldName, atSelector);
330			Method mutatorMethod = null;
331			try {
332				mutatorMethod = JavaInterfaceAdaptor.getNativeATMethod(natObject.getClass(), natObject, setPrefix + fieldName, atSelector);
333			} catch (XSelectorNotFound e) {
334				// no setter, return a read-only field
335			}
336			return new NativeField(natObject, atSelector, accessorMethod, mutatorMethod);
337		} catch (XSelectorNotFound e) {
338			// selector not found exceptions have to be translated to field not found exceptions
339			throw new XUndefinedField("field access", atSelector.toString());
340		}
341	}
342	
343	public static final ATField downBaseLevelField(ATObject natObject, ATSymbol atSelector) throws InterpreterException {
344		return downField(natObject, atSelector, Reflection._BGET_PREFIX_, Reflection._BSET_PREFIX_);
345	}
346	
347	public static final ATField downMetaLevelField(ATObject natObject, ATSymbol atSelector) throws InterpreterException {
348		return downField(natObject, atSelector, Reflection._MGET_PREFIX_, Reflection._MSET_PREFIX_);
349	}
350	
351	/**
352	 * Constructs an AmbientTalk ATMethod from a Java method.
353	 * Given an object obj and a String sel, it is checked whether obj has a method
354	 * named sel. If so, the corresponding Java method is wrapped in a NativeMethod.
355	 * If not, the downing fails.
356	 *
357	 * @param natObject the native AmbientTalk object in whose class the method should be found
358	 * @param jSelector a selector which should yield a method in natObject
359	 * @param origName the original AmbientTalk name of the method
360	 * @return a reified method wrapping the Java method
361	 * 
362	 * Example:
363	 *  eval "(reflect: tbl).getMethod('at')" where tbl is a NATTable
364	 *  => downMethod(aNATTable, "base_at")
365	 *  => NATTable must have a method named base_at
366	 *  
367	 * Callers should use the more specialised 'downBaseLevelMethod' and 'downMetaLevelMethod'
368	 * methods to specify the prefix of the method to be found
369	 */
370	public static final ATMethod downMethod(ATObject natObject, String jSelector, ATSymbol origName) throws InterpreterException {
371		return new NativeMethod(JavaInterfaceAdaptor.getNativeATMethod(natObject.getClass(), natObject, jSelector, origName), origName);
372	}
373	
374	public static final ATMethod downBaseLevelMethod(ATObject natObject, ATSymbol atSelector) throws InterpreterException {
375		return downMethod(natObject, upBaseLevelSelector(atSelector), atSelector);
376	}
377	
378	public static final ATMethod downMetaLevelMethod(ATObject natObject, ATSymbol atSelector) throws InterpreterException {
379		return downMethod(natObject, upMetaLevelSelector(atSelector), atSelector);
380	}
381
382	/**
383	 * downInvocation takes an implicit Java invocation and turns it into an explicit
384	 * AmbientTalk invocation process. This happens when Java code sends normal
385	 * Java messages to AmbientTalk objects (wrapped by a mirage).
386	 * 
387	 * @param atRcvr the AmbientTalk object having received the Java method invocation
388	 * @param jSelector the Java selector, to be converted to an AmbientTalk selector
389	 * @param jArgs the arguments to the Java method invocation (normally all args are ATObjects)
390	 * jArgs may be null, indicating that there are no arguments
391	 * @return the return value of the AmbientTalk method invoked via the java invocation.
392	 * 
393	 * Example:
394	 *  in Java: "tbl.base_at(1)" where tbl is an ATTable coercer wrapping aNATObject
395	 *  => downInvocation(aNATObject, "base_at", ATObject[] { ATNumber(1) })
396	 *  => aNATObject must implement a method named "at"
397	 *  
398	 * Depending on the prefix of the invoked Java method selector, the following translation should occur:
399	 *  - obj.base_selector(args) => obj.meta_invoke(obj, selector, args)
400	 *  - obj.base_getSelector() => obj.meta_select(obj, selector)
401	 *  - obj.base_setSelector(x) => obj.meta_assignField(selector, x)
402	 *  - obj.meta_selector(args) => obj.meta_selector(args)
403	 *  - obj.meta_set|getSelector(args) => obj.meta_set|getSelector(args)
404	 *  - obj.selector(args) => either obj.selector(args) if selector is understood natively
405	 *                          or     obj.meta_invoke(obj, selector, args) otherwise
406	 */
407	public static final ATObject downInvocation(ATObject atRcvr, Method jMethod, ATObject[] jArgs) throws InterpreterException {
408		String jSelector = jMethod.getName();
409		if (jArgs == null) { jArgs = NATTable.EMPTY.elements_; }
410		
411		if (jSelector.startsWith(Reflection._BGET_PREFIX_)) {
412			// obj.base_getSelector() => obj.meta_select(obj, selector)
413			if (jArgs.length != 0) {
414				throw new XArityMismatch(downBaseFieldAccessSelector(jSelector).toString(), 0, jArgs.length);
415			}
416			return atRcvr.meta_select(atRcvr, downBaseFieldAccessSelector(jSelector));
417		} else if (jSelector.startsWith(Reflection._BSET_PREFIX_)) {
418			// obj.base_setSelector(x) => obj.meta_assignField(selector, x)
419			if (jArgs.length != 1) {
420				throw new XArityMismatch(downBaseFieldMutationSelector(jSelector).toString(), 1, jArgs.length);
421			}
422			return atRcvr.meta_assignField(atRcvr, downBaseFieldMutationSelector(jSelector), jArgs[0]);
423		} else if (jSelector.startsWith(Reflection._BASE_PREFIX_)) {
424			// obj.base_selector(args) => obj.meta_invoke(obj, selector, args)
425			return atRcvr.meta_invoke(atRcvr, downBaseLevelSelector(jSelector), NATTable.atValue(jArgs));
426		} else if (jSelector.startsWith(Reflection._META_PREFIX_)) {
427			// obj.meta_selector(args) => obj.meta_selector(args)
428			return JavaInterfaceAdaptor.invokeNativeATMethod(jMethod, atRcvr, jArgs);
429		} else {
430			// atRcvr can respond to the given method natively
431			if (jMethod.getDeclaringClass().isInstance(atRcvr)) {
432				return JavaInterfaceAdaptor.invokeNativeATMethod(jMethod, atRcvr, jArgs);
433			} else {
434			    // obj.selector(args) => obj.meta_invoke(obj, selector, args)
435			    return atRcvr.meta_invoke(atRcvr, downSelector(jSelector), NATTable.atValue(jArgs));
436			}
437		}
438	}
439
440	/**
441	 * upInvocation takes an explicit AmbientTalk method invocation and turns it into an
442	 * implicitly performed Java invocation.
443	 * 
444	 * Depending on whether the AmbientTalk invocation happens at the base-level or the meta-level
445	 * (i.e. the receiver denotes a base-level object or a mirror), the jSelector parameter will have
446	 * a different prefix.
447	 * 
448	 * @param atOrigRcvr the original AmbientTalk object that received the invocation
449	 * @param jSelector the selector of the message to be invoked, converted to a Java selector
450	 * @param atArgs the arguments to the AmbientTalk method invocation
451	 * @return the return value of the Java method invoked via the java invocation.
452	 * 
453	 * Example:
454	 *  eval "tbl.at(1)" where tbl is a NATTable
455	 *  => upInvocation(aNATTable, "base_at", ATObject[] { ATNumber(1) })
456	 *  => NATTable must have a method named base_at
457	 * 
458	 * Example:
459	 *  eval "(reflect: tbl).invoke(tbl, "at", [1])" where tbl is a NATTable
460	 *  => upInvocation(aNATTable, "meta_invoke", ATObject[] { aNATTable, ATSymbol('at'), ATTable([ATNumber(1)]) })
461	 *  => NATTable must have a method named meta_invoke
462	 */
463	public static final ATObject upInvocation(ATObject atOrigRcvr, String jSelector, ATSymbol atSelector, ATTable atArgs) throws InterpreterException {
464		return JavaInterfaceAdaptor.invokeNativeATMethod(
465				    atOrigRcvr.getClass(),
466				    atOrigRcvr,
467					jSelector,
468					atSelector, atArgs.asNativeTable().elements_);
469	}
470	
471	/**
472	 * upRespondsTo transforms an explicit AmbientTalk respondsTo meta-level request
473	 * into an implicit check whether the given jRcvr java object has a method
474	 * corresponding to the given selector, prefixed with base_
475	 * 
476	 * @param jRcvr the Java object being queried for a certain selector
477	 * @param jSelector the selector of the message to be invoked, converted to a Java selector
478	 * @return a boolean indicating whether the jRcvr implements a method corresponding to base_ + atSelector
479	 * 
480	 * Example:
481	 *  eval "(reflect: [1,2,3]).respondsTo("at")" where the receiver of repondsTo is a NATTable
482	 *  => upRespondsTo(aNATTable, "at")
483	 *  => NATTable must have a method named base_at
484	 */
485	public static final boolean upRespondsTo(ATObject jRcvr,String jSelector) throws InterpreterException {
486		return JavaInterfaceAdaptor.hasApplicableJavaMethod(
487				jRcvr.getClass(),
488				jSelector);
489	}
490
491//	/**
492//	 * downSelection takes an implicit Java selection (in the guise of invoking a getter method)
493//	 * and turns it into an explicit AmbientTalk selection process. This happens when Java code sends normal
494//	 * Java messages to AmbientTalk objects (wrapped by a mirage).
495//	 * 
496//	 * @param atRcvr the AmbientTalk object having received the Java selection
497//	 * @param jSelector the Java selector, without the 'get' prefix, to be converted to an AmbientTalk selector
498//	 * @return the value of the AmbientTalk field selected by the java selection.
499//	 * 
500//	 * Example:
501//	 *  in Java: "msg.getSelector()" where msg is am ATMessage mirage wrapping a NATObject
502//	 *  => downSelection(aNATObject, "selector")
503//	 *  => aNATObject must implement a field named "selector"
504//	 * The get prefix is normally stripped off by a mirage
505//	 */
506//	public static final ATObject downSelection(ATObject atRcvr, String jSelector) {
507//		return null;
508//	}
509	
510	/**
511	 * upFieldSelection takes an explicit AmbientTalk field selection and turns it into 
512	 * an implicitly performed Java selection by invoking a getter method, if such a getter method
513	 * exists. 
514	 * 
515	 * @param atOrigRcvr the original AmbientTalk object that received the selection
516	 * @param jSelector the selector of the message to be invoked, already converted to a Java selector
517	 * @return the return value of the Java getter method invoked via the AmbientTalk selection.
518	 * 
519	 * Example:
520	 *  eval "msg.selector" where msg is a NATMessage
521	 *  => upSelection(aNATMessage, "selector")
522	 *  => NATMessage must have a zero-argument method named getSelector
523	 *  
524	 */
525	public static final ATObject upFieldSelection(ATObject atOrigRcvr, String jSelector, ATSymbol atSelector) throws InterpreterException {
526		return JavaInterfaceAdaptor.invokeNativeATMethod(
527				atOrigRcvr.getClass(),
528				atOrigRcvr,
529				jSelector,
530				atSelector, NATTable.EMPTY.elements_);		
531	}
532	
533	/**
534	 * upFieldAssignment takes an explicit AmbientTalk field assignment and turns it into 
535	 * an implicitly performed Java field assignment by invoking a mutator method, if such a method
536	 * exists. 
537	 * 
538	 * @param atOrigRcvr the original AmbientTalk object that received the assignField request
539	 * @param jSelector the selector of the message to be invoked, already converted to a Java selector
540	 * @return the return value of the Java mutator method invoked via the AmbientTalk assignField request.
541	 * 
542	 * Example:
543	 *  eval "msg.selector := v" where msg is a NATMessage
544	 *  => upFieldAssignment(aNATMessage, "selector", v)
545	 *  => NATMessage must have a one-argument method named base_setSelector
546	 *  
547	 */
548	public static final ATObject upFieldAssignment(ATObject atOrigRcvr, String jSelector, ATSymbol atSelector, ATObject value) throws InterpreterException {
549		return JavaInterfaceAdaptor.invokeNativeATMethod(
550				atOrigRcvr.getClass(),
551				atOrigRcvr,
552				jSelector,
553				atSelector, new ATObject[] { value });
554	}
555	
556	/**
557	 * upMethodSelection takes an explicit AmbientTalk field selection and checks whether 
558	 * a Java method exists that matches the selector. If so, this method is wrapped in a 
559	 * NativeClosure and returned.
560	 * 
561	 * @param atOrigRcvr the original AmbientTalk object that received the selection
562	 * @param jSelector the selector of the message to be invoked, converted to a Java selector
563	 * @return a closure wrapping the method selected via the AmbientTalk selection.
564	 * 
565	 * Example:
566	 *  eval "[1,2,3].at"
567	 *  => upSelection(aNATTable, "at")
568	 *  => either NATTable must have a method base_at, which is then wrapped
569	 */
570	public static final NativeClosure upMethodSelection(ATObject atOrigRcvr, String jSelector, ATSymbol origSelector) throws InterpreterException {
571		Method m = JavaInterfaceAdaptor.getNativeATMethod(atOrigRcvr.getClass(), atOrigRcvr, jSelector, origSelector);
572		return new NativeClosure(atOrigRcvr, new NativeMethod(m, origSelector));
573	}
574	
575	/**
576	 * upInstanceCreation takes an explicit AmbientTalk 'new' invocation and turns it into an
577	 * implicit Java instance creation by calling a constructor. The initargs are upped as well
578	 * and are passed as arguments to the constructor.
579	 * 
580	 * @param jRcvr the Java object having received the call to new
581	 * @param atInitargs the arguments to the constructor
582	 * @return a new instance of a Java class
583	 * @throws InterpreterException
584	 */
585	public static final ATObject upInstanceCreation(ATObject jRcvr, ATTable atInitargs) throws InterpreterException {
586		ATObject[] args = atInitargs.asNativeTable().elements_;
587		return JavaInterfaceAdaptor.createNativeATObject(jRcvr.getClass(), args);
588	}
589	
590	public static final ATObject upExceptionCreation(InterpreterException jRcvr, ATTable atInitargs) throws InterpreterException {
591		ATObject[] args = atInitargs.asNativeTable().elements_;
592		//return JavaInterfaceAdaptor.createNativeATObject(jRcvr.getClass(), args);
593		return new NATException((InterpreterException)
594				Symbiosis.symbioticInstanceCreation(jRcvr.getClass(), args)
595				  .asJavaObjectUnderSymbiosis().getWrappedObject());
596	}
597
598	/**
599	 * Pass an AmbientTalk meta-level object into the base-level
600	 */
601	public static final ATObject downObject(ATObject metaObject) throws InterpreterException {
602		return metaObject;
603		/*if (metaObject.meta_isStripedWith(NativeStripes._MIRROR_).asNativeBoolean().javaValue) {
604			return metaObject.meta_select(metaObject, OBJMirrorRoot._BASE_NAME_);
605		} else {
606			return metaObject; // most native objects represent both the object at the base and at the meta-level
607		}*/
608	}
609	
610	/**
611	 * Pass an AmbientTalk base-level object to the meta-level
612	 */
613	public static final ATObject upObject(ATObject baseObject) {
614		if (baseObject instanceof NATMirage) {
615			return ((NATMirage) baseObject).getMirror();
616		} else {
617			return baseObject;
618		}
619	}
620	
621	/**
622	 * Returns, for a given AmbientTalk object atObj, an array of NativeField objects corresponding
623	 * to all non-static methods of that object's Java class, where each method's name is prefixed with 'base_get'
624	 */
625	public static final ATField[] downBaseLevelFields(ATObject atObj) throws InterpreterException {
626		Method[] allBaseGetMethods =
627			JavaInterfaceAdaptor.allMethodsPrefixed(atObj.getClass(), Reflection._BGET_PREFIX_, false);
628		ATField[] fields = new ATField[allBaseGetMethods.length];
629		for (int i = 0; i < allBaseGetMethods.length; i++) {
630			Method m = allBaseGetMethods[i];
631			fields[i] = downBaseLevelField(atObj, downBaseFieldAccessSelector(m.getName()));
632		}
633		return fields;
634	}
635	
636	/**
637	 * Returns, for a given AmbientTalk object atObj, an array of NativeField objects corresponding
638	 * to all non-static methods of that object's Java class, where each method's name is prefixed with 'meta_get'
639	 */
640	public static final ATField[] downMetaLevelFields(ATObject atObj) throws InterpreterException {
641		Method[] allMetaGetMethods =
642			JavaInterfaceAdaptor.allMethodsPrefixed(atObj.getClass(), Reflection._MGET_PREFIX_, false);
643		ATField[] fields = new ATField[allMetaGetMethods.length];
644		for (int i = 0; i < allMetaGetMethods.length; i++) {
645			Method m = allMetaGetMethods[i];
646			fields[i] = downMetaLevelField(atObj, downMetaFieldAccessSelector(m.getName()));
647		}
648		return fields;
649	}
650	
651	/**
652	 * Returns, for a given AmbientTalk object atObj, an array of NativeMethod objects corresponding
653	 * to all non-static methods of that object's class, where each method's name:
654	 *  - is prefixed with 'base_'
655	 *  - is not prefixed with 'base_get'
656	 *  - is not prefixed with 'base_set'
657	 */
658	public static final ATMethod[] downBaseLevelMethods(ATObject atObj) throws InterpreterException {
659		Method[] allBaseMethods =
660			JavaInterfaceAdaptor.allMethodsPrefixed(atObj.getClass(), Reflection._BASE_PREFIX_, false);
661		Vector allNonFieldBaseMethods = new Vector();
662		for (int i = 0; i < allBaseMethods.length; i++) {
663			Method m = allBaseMethods[i];
664			String nam = m.getName();
665			if (!((nam.startsWith(Reflection._BGET_PREFIX_)) ||
666			      (nam.startsWith(Reflection._BSET_PREFIX_)))) {
667				allNonFieldBaseMethods.add(new NativeMethod(m, downBaseLevelSelector(nam)));
668			}
669		}
670		return (ATMethod[]) allNonFieldBaseMethods.toArray(new ATMethod[allNonFieldBaseMethods.size()]);
671	}
672	
673	/**
674	 * Returns, for a given AmbientTalk object natObj, an array of NativeMethod objects corresponding
675	 * to all non-static methods of that object's class, where each method's name:
676	 *  - is prefixed with 'meta_'
677	 *  - is not prefixed with 'meta_get'
678	 *  - is not prefixed with 'meta_set'
679	 */
680	public static final ATMethod[] downMetaLevelMethods(ATObject natObj) throws InterpreterException {
681		Method[] allMetaMethods =
682			JavaInterfaceAdaptor.allMethodsPrefixed(natObj.getClass(), Reflection._META_PREFIX_, false);
683		Vector allNonFieldMetaMethods = new Vector();
684		for (int i = 0; i < allMetaMethods.length; i++) {
685			Method m = allMetaMethods[i];
686			String nam = m.getName();
687			if (!((nam.startsWith(Reflection._MGET_PREFIX_)) ||
688			      (nam.startsWith(Reflection._MSET_PREFIX_)))) {
689				allNonFieldMetaMethods.add(new NativeMethod(m, downMetaLevelSelector(nam)));
690			}
691		}
692		return (ATMethod[]) allNonFieldMetaMethods.toArray(new ATMethod[allNonFieldMetaMethods.size()]);
693	}
694	
695	private static final Pattern oprCode = Pattern.compile("_op(\\w\\w\\w)_"); //'_op', 3 chars, '_'
696	private static final Pattern symbol = Pattern.compile("\\W"); //any non-word character
697	
698	private static String stripPrefix(String input, String prefix) {
699		// \A matches start of input
700		return input.replaceFirst("\\A"+prefix, "");
701	}
702	
703	private static final String oprCode2Symbol(String code) {
704		switch (code.charAt(0)) {
705		  case 'p': if (code.equals("pls")) { return "+"; } else break;
706		  case 'm': if (code.equals("mns")) { return "-"; } else break;
707		  case 't': if (code.equals("tms")) { return "*"; } else
708			        if (code.equals("til")) { return "~"; } else break;
709		  case 'd': if (code.equals("div")) { return "/"; } else break;
710		  case 'b': if (code.equals("bsl")) { return "\\"; } else break;
711		  case 'a': if (code.equals("and")) { return "&"; } else break;
712		  case 'n': if (code.equals("not")) { return "!"; } else break;
713		  case 'g': if (code.equals("gtx")) { return ">"; } else break;
714		  case 'l': if (code.equals("ltx")) { return "<"; } else break;
715		  case 'e': if (code.equals("eql")) { return "="; } else break;
716		  case 'q': if (code.equals("que")) { return "?"; } else break;
717		  case 'r': if (code.equals("rem")) { return "%"; } else break;
718		}
719		return "_op" + code + "_"; // no match, return original input
720	}
721	
722	private static final String symbol2oprCode(String symbol) {
723		switch (symbol.charAt(0)) {
724		  case '+': return "pls";
725		  case '-': return "mns";
726		  case '*': return "tms";
727		  case '/': return "div";
728		  case '\\': return "bsl";
729		  case '&': return "and";
730		  case '!': return "not";
731		  case '>': return "gtx";
732		  case '<': return "ltx";
733		  case '=': return "eql";
734		  case '~': return "til";
735		  case '?': return "que";
736		  case '%': return "rem";
737		  default: return symbol; // no match, return original input
738		}	
739	}
740	
741	private static final String javaToAmbientTalkSelector(String jSelector) {
742		// _op{code}_ -> operator symbol
743		Matcher m = oprCode.matcher(jSelector);
744		StringBuffer sb = new StringBuffer();
745		while (m.find()) {
746             // find every occurence of _op\w\w\w_ and convert it into a symbol
747			m.appendReplacement(sb, oprCode2Symbol(m.group(1)));
748		}
749		m.appendTail(sb);
750		
751		// _ -> :
752		return sb.toString().replaceAll("_", ":");
753	}
754	
755}