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

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