/interpreter/tags/at2-build060407/src/edu/vub/at/objects/mirrors/Reflection.java

http://ambienttalk.googlecode.com/ · Java · 761 lines · 321 code · 51 blank · 389 comment · 72 complexity · 2641625ce40453f3e621243f2d7e03dd 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 edu.vub.util.Matcher;
  44. import edu.vub.util.Pattern;
  45. import java.lang.reflect.Method;
  46. import java.util.Vector;
  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. // Backport from JDK 1.4 to 1.3
  212. // nam = nam.replaceAll(":", "_");
  213. nam = Pattern.compile(":").matcher(new StringBuffer(nam)).replaceAll("_");
  214. // operator symbol -> _op{code}_
  215. Matcher m = symbol.matcher(new StringBuffer(nam));
  216. StringBuffer sb = new StringBuffer();
  217. while (m.find()) {
  218. // find every occurence of a non-word character and convert it into a symbol
  219. String oprCode = symbol2oprCode(m.group(0));
  220. // only add the _op prefix and _ postfix if the code has been found...
  221. m.appendReplacement(sb, (oprCode.length() == 3) ? "_op" + oprCode + "_" : oprCode);
  222. }
  223. m.appendTail(sb);
  224. return sb.toString();
  225. }
  226. /**
  227. * Transforms an AmbientTalk selector into a Java-level selector prefixed with base_.
  228. */
  229. public static final String upBaseLevelSelector(ATSymbol atSelector) throws InterpreterException {
  230. return Reflection._BASE_PREFIX_ + upSelector(atSelector);
  231. }
  232. /**
  233. * Transforms an AmbientTalk selector into a Java-level selector prefixed with meta_.
  234. */
  235. public static final String upMetaLevelSelector(ATSymbol atSelector) throws InterpreterException {
  236. return Reflection._META_PREFIX_ + upSelector(atSelector);
  237. }
  238. /**
  239. * A field name "field" passed from the AmbientTalk to the Java level undergoes the following transformations:
  240. *
  241. * - the same transformations applicable to upSelector
  242. * @see Reflection#upSelector(ATSymbol)
  243. * - the first letter is transformed into upper case such that it can be accessed using respectively
  244. * "getField" | "setField" methods at the Java level.
  245. */
  246. public static final String upFieldName(ATSymbol atName) throws InterpreterException {
  247. char[] charArray = upSelector(atName).toCharArray();
  248. charArray[0] = Character.toUpperCase(charArray[0]);
  249. return new String(charArray);
  250. }
  251. /**
  252. * Transforms an AmbientTalk selector into an equivalent Java selector uppercased and prefixed with "base_get".
  253. *
  254. * Example:
  255. * upBaseFieldAccessSelector(ATSymbol('receiver')) => "base_getReceiver"
  256. */
  257. public static final String upBaseFieldAccessSelector(ATSymbol atName) throws InterpreterException {
  258. return Reflection._BGET_PREFIX_ + upFieldName(atName);
  259. }
  260. /**
  261. * Transforms an AmbientTalk selector into an equivalent Java selector uppercased and prefixed with "base_set".
  262. *
  263. * Example:
  264. * upBaseFieldMutationSelector(ATSymbol('receiver')) => "base_setReceiver"
  265. */
  266. public static final String upBaseFieldMutationSelector(ATSymbol atName) throws InterpreterException {
  267. return Reflection._BSET_PREFIX_ + upFieldName(atName);
  268. }
  269. /**
  270. * Transforms an AmbientTalk selector into an equivalent Java selector uppercased and prefixed with "meta_get".
  271. *
  272. * Example:
  273. * upMetaFieldAccessSelector(ATSymbol('receiver')) => "meta_getReceiver"
  274. */
  275. public static final String upMetaFieldAccessSelector(ATSymbol atName) throws InterpreterException {
  276. return Reflection._MGET_PREFIX_ + upFieldName(atName);
  277. }
  278. /**
  279. * Transforms an AmbientTalk selector into an equivalent Java selector uppercased and prefixed with "meta_set".
  280. *
  281. * Example:
  282. * upMetaFieldMutationSelector(ATSymbol('receiver')) => "meta_setReceiver"
  283. */
  284. public static final String upMetaFieldMutationSelector(ATSymbol atName) throws InterpreterException {
  285. return Reflection._MSET_PREFIX_ + upFieldName(atName);
  286. }
  287. /**
  288. * Constructs an AmbientTalk ATField from a pair of accessor/mutator methods of
  289. * a Java object. Given an object obj and a String sel, it is checked whether
  290. * a) obj has a method named getPrefix + Sel, if so, a field can be created
  291. * b) obj has a method named setPrefix + Sel, if so, the field is mutable, otherwise it is read-only
  292. *
  293. * The accessor method cannot take any arguments, the mutator method must have a unary arity.
  294. *
  295. * @param natObject the native AT object in whose class the accessor/mutator methods should be found
  296. * @param atSelector the AmbientTalk name of the field
  297. * @return a reified field, which may either be read-only or mutable depending on available methods
  298. *
  299. * Example:
  300. * eval "(reflect: msg).getField('selector')" where msg is a NATMessage
  301. * => downField(aNATMessage, "selector", "base_get", "base_set")
  302. * => NATMessage must have a zero-arg method base_getSelector and optionally base_setSelector
  303. */
  304. public static final ATField downField(ATObject natObject, ATSymbol atSelector,
  305. String getPrefix, String setPrefix) throws InterpreterException {
  306. String fieldName = upFieldName(atSelector);
  307. try {
  308. Method accessorMethod = JavaInterfaceAdaptor.getNativeATMethod(natObject.getClass(), natObject, getPrefix + fieldName, atSelector);
  309. Method mutatorMethod = null;
  310. try {
  311. mutatorMethod = JavaInterfaceAdaptor.getNativeATMethod(natObject.getClass(), natObject, setPrefix + fieldName, atSelector);
  312. } catch (XSelectorNotFound e) {
  313. // no setter, return a read-only field
  314. }
  315. return new NativeField(natObject, atSelector, accessorMethod, mutatorMethod);
  316. } catch (XSelectorNotFound e) {
  317. // selector not found exceptions have to be translated to field not found exceptions
  318. throw new XUndefinedField("field access", atSelector.toString());
  319. }
  320. }
  321. public static final ATField downBaseLevelField(ATObject natObject, ATSymbol atSelector) throws InterpreterException {
  322. return downField(natObject, atSelector, Reflection._BGET_PREFIX_, Reflection._BSET_PREFIX_);
  323. }
  324. public static final ATField downMetaLevelField(ATObject natObject, ATSymbol atSelector) throws InterpreterException {
  325. return downField(natObject, atSelector, Reflection._MGET_PREFIX_, Reflection._MSET_PREFIX_);
  326. }
  327. /**
  328. * Constructs an AmbientTalk ATMethod from a Java method.
  329. * Given an object obj and a String sel, it is checked whether obj has a method
  330. * named sel. If so, the corresponding Java method is wrapped in a NativeMethod.
  331. * If not, the downing fails.
  332. *
  333. * @param natObject the native AmbientTalk object in whose class the method should be found
  334. * @param jSelector a selector which should yield a method in natObject
  335. * @param origName the original AmbientTalk name of the method
  336. * @return a reified method wrapping the Java method
  337. *
  338. * Example:
  339. * eval "(reflect: tbl).getMethod('at')" where tbl is a NATTable
  340. * => downMethod(aNATTable, "base_at")
  341. * => NATTable must have a method named base_at
  342. *
  343. * Callers should use the more specialised 'downBaseLevelMethod' and 'downMetaLevelMethod'
  344. * methods to specify the prefix of the method to be found
  345. */
  346. public static final ATMethod downMethod(ATObject natObject, String jSelector, ATSymbol origName) throws InterpreterException {
  347. return new NativeMethod(JavaInterfaceAdaptor.getNativeATMethod(natObject.getClass(), natObject, jSelector, origName), origName);
  348. }
  349. public static final ATMethod downBaseLevelMethod(ATObject natObject, ATSymbol atSelector) throws InterpreterException {
  350. return downMethod(natObject, upBaseLevelSelector(atSelector), atSelector);
  351. }
  352. public static final ATMethod downMetaLevelMethod(ATObject natObject, ATSymbol atSelector) throws InterpreterException {
  353. return downMethod(natObject, upMetaLevelSelector(atSelector), atSelector);
  354. }
  355. /**
  356. * downInvocation takes an implicit Java invocation and turns it into an explicit
  357. * AmbientTalk invocation process. This happens when Java code sends normal
  358. * Java messages to AmbientTalk objects (wrapped by a mirage).
  359. *
  360. * @param atRcvr the AmbientTalk object having received the Java method invocation
  361. * @param jSelector the Java selector, to be converted to an AmbientTalk selector
  362. * @param jArgs the arguments to the Java method invocation (normally all args are ATObjects)
  363. * jArgs may be null, indicating that there are no arguments
  364. * @return the return value of the AmbientTalk method invoked via the java invocation.
  365. *
  366. * Example:
  367. * in Java: "tbl.base_at(1)" where tbl is an ATTable coercer wrapping aNATObject
  368. * => downInvocation(aNATObject, "base_at", ATObject[] { ATNumber(1) })
  369. * => aNATObject must implement a method named "at"
  370. *
  371. * Depending on the prefix of the invoked Java method selector, the following translation should occur:
  372. * - obj.base_selector(args) => obj.meta_invoke(obj, selector, args)
  373. * - obj.base_getSelector() => obj.meta_select(obj, selector)
  374. * - obj.base_setSelector(x) => obj.meta_assignField(selector, x)
  375. * - obj.meta_selector(args) => obj.meta_selector(args)
  376. * - obj.meta_set|getSelector(args) => obj.meta_set|getSelector(args)
  377. * - obj.selector(args) => either obj.selector(args) if selector is understood natively
  378. * or obj.meta_invoke(obj, selector, args) otherwise
  379. */
  380. public static final ATObject downInvocation(ATObject atRcvr, Method jMethod, ATObject[] jArgs) throws InterpreterException {
  381. String jSelector = jMethod.getName();
  382. if (jArgs == null) { jArgs = NATTable.EMPTY.elements_; }
  383. if (jSelector.startsWith(Reflection._BGET_PREFIX_)) {
  384. // obj.base_getSelector() => obj.meta_select(obj, selector)
  385. if (jArgs.length != 0) {
  386. throw new XArityMismatch(downBaseFieldAccessSelector(jSelector).toString(), 0, jArgs.length);
  387. }
  388. return atRcvr.meta_select(atRcvr, downBaseFieldAccessSelector(jSelector));
  389. } else if (jSelector.startsWith(Reflection._BSET_PREFIX_)) {
  390. // obj.base_setSelector(x) => obj.meta_assignField(selector, x)
  391. if (jArgs.length != 1) {
  392. throw new XArityMismatch(downBaseFieldMutationSelector(jSelector).toString(), 1, jArgs.length);
  393. }
  394. return atRcvr.meta_assignField(atRcvr, downBaseFieldMutationSelector(jSelector), jArgs[0]);
  395. } else if (jSelector.startsWith(Reflection._BASE_PREFIX_)) {
  396. // obj.base_selector(args) => obj.meta_invoke(obj, selector, args)
  397. return atRcvr.meta_invoke(atRcvr, downBaseLevelSelector(jSelector), NATTable.atValue(jArgs));
  398. } else if (jSelector.startsWith(Reflection._META_PREFIX_)) {
  399. // obj.meta_selector(args) => obj.meta_selector(args)
  400. return JavaInterfaceAdaptor.invokeNativeATMethod(jMethod, atRcvr, jArgs);
  401. } else {
  402. // atRcvr can respond to the given method natively
  403. if (jMethod.getDeclaringClass().isInstance(atRcvr)) {
  404. return JavaInterfaceAdaptor.invokeNativeATMethod(jMethod, atRcvr, jArgs);
  405. } else {
  406. // obj.selector(args) => obj.meta_invoke(obj, selector, args)
  407. return atRcvr.meta_invoke(atRcvr, downSelector(jSelector), NATTable.atValue(jArgs));
  408. }
  409. }
  410. }
  411. /**
  412. * upInvocation takes an explicit AmbientTalk method invocation and turns it into an
  413. * implicitly performed Java invocation.
  414. *
  415. * Depending on whether the AmbientTalk invocation happens at the base-level or the meta-level
  416. * (i.e. the receiver denotes a base-level object or a mirror), the jSelector parameter will have
  417. * a different prefix.
  418. *
  419. * @param atOrigRcvr the original AmbientTalk object that received the invocation
  420. * @param jSelector the selector of the message to be invoked, converted to a Java selector
  421. * @param atArgs the arguments to the AmbientTalk method invocation
  422. * @return the return value of the Java method invoked via the java invocation.
  423. *
  424. * Example:
  425. * eval "tbl.at(1)" where tbl is a NATTable
  426. * => upInvocation(aNATTable, "base_at", ATObject[] { ATNumber(1) })
  427. * => NATTable must have a method named base_at
  428. *
  429. * Example:
  430. * eval "(reflect: tbl).invoke(tbl, "at", [1])" where tbl is a NATTable
  431. * => upInvocation(aNATTable, "meta_invoke", ATObject[] { aNATTable, ATSymbol('at'), ATTable([ATNumber(1)]) })
  432. * => NATTable must have a method named meta_invoke
  433. */
  434. public static final ATObject upInvocation(ATObject atOrigRcvr, String jSelector, ATSymbol atSelector, ATTable atArgs) throws InterpreterException {
  435. return JavaInterfaceAdaptor.invokeNativeATMethod(
  436. atOrigRcvr.getClass(),
  437. atOrigRcvr,
  438. jSelector,
  439. atSelector, atArgs.asNativeTable().elements_);
  440. }
  441. /**
  442. * upRespondsTo transforms an explicit AmbientTalk respondsTo meta-level request
  443. * into an implicit check whether the given jRcvr java object has a method
  444. * corresponding to the given selector, prefixed with base_
  445. *
  446. * @param jRcvr the Java object being queried for a certain selector
  447. * @param jSelector the selector of the message to be invoked, converted to a Java selector
  448. * @return a boolean indicating whether the jRcvr implements a method corresponding to base_ + atSelector
  449. *
  450. * Example:
  451. * eval "(reflect: [1,2,3]).respondsTo("at")" where the receiver of repondsTo is a NATTable
  452. * => upRespondsTo(aNATTable, "at")
  453. * => NATTable must have a method named base_at
  454. */
  455. public static final boolean upRespondsTo(ATObject jRcvr,String jSelector) throws InterpreterException {
  456. return JavaInterfaceAdaptor.hasApplicableJavaMethod(
  457. jRcvr.getClass(),
  458. jSelector);
  459. }
  460. // /**
  461. // * downSelection takes an implicit Java selection (in the guise of invoking a getter method)
  462. // * and turns it into an explicit AmbientTalk selection process. This happens when Java code sends normal
  463. // * Java messages to AmbientTalk objects (wrapped by a mirage).
  464. // *
  465. // * @param atRcvr the AmbientTalk object having received the Java selection
  466. // * @param jSelector the Java selector, without the 'get' prefix, to be converted to an AmbientTalk selector
  467. // * @return the value of the AmbientTalk field selected by the java selection.
  468. // *
  469. // * Example:
  470. // * in Java: "msg.getSelector()" where msg is am ATMessage mirage wrapping a NATObject
  471. // * => downSelection(aNATObject, "selector")
  472. // * => aNATObject must implement a field named "selector"
  473. // * The get prefix is normally stripped off by a mirage
  474. // */
  475. // public static final ATObject downSelection(ATObject atRcvr, String jSelector) {
  476. // return null;
  477. // }
  478. /**
  479. * upFieldSelection takes an explicit AmbientTalk field selection and turns it into
  480. * an implicitly performed Java selection by invoking a getter method, if such a getter method
  481. * exists.
  482. *
  483. * @param atOrigRcvr the original AmbientTalk object that received the selection
  484. * @param jSelector the selector of the message to be invoked, already converted to a Java selector
  485. * @return the return value of the Java getter method invoked via the AmbientTalk selection.
  486. *
  487. * Example:
  488. * eval "msg.selector" where msg is a NATMessage
  489. * => upSelection(aNATMessage, "selector")
  490. * => NATMessage must have a zero-argument method named getSelector
  491. *
  492. */
  493. public static final ATObject upFieldSelection(ATObject atOrigRcvr, String jSelector, ATSymbol atSelector) throws InterpreterException {
  494. return JavaInterfaceAdaptor.invokeNativeATMethod(
  495. atOrigRcvr.getClass(),
  496. atOrigRcvr,
  497. jSelector,
  498. atSelector, NATTable.EMPTY.elements_);
  499. }
  500. /**
  501. * upFieldAssignment takes an explicit AmbientTalk field assignment and turns it into
  502. * an implicitly performed Java field assignment by invoking a mutator method, if such a method
  503. * exists.
  504. *
  505. * @param atOrigRcvr the original AmbientTalk object that received the assignField request
  506. * @param jSelector the selector of the message to be invoked, already converted to a Java selector
  507. * @return the return value of the Java mutator method invoked via the AmbientTalk assignField request.
  508. *
  509. * Example:
  510. * eval "msg.selector := v" where msg is a NATMessage
  511. * => upFieldAssignment(aNATMessage, "selector", v)
  512. * => NATMessage must have a one-argument method named base_setSelector
  513. *
  514. */
  515. public static final ATObject upFieldAssignment(ATObject atOrigRcvr, String jSelector, ATSymbol atSelector, ATObject value) throws InterpreterException {
  516. return JavaInterfaceAdaptor.invokeNativeATMethod(
  517. atOrigRcvr.getClass(),
  518. atOrigRcvr,
  519. jSelector,
  520. atSelector, new ATObject[] { value });
  521. }
  522. /**
  523. * upMethodSelection takes an explicit AmbientTalk field selection and checks whether
  524. * a Java method exists that matches the selector. If so, this method is wrapped in a
  525. * NativeClosure and returned.
  526. *
  527. * @param atOrigRcvr the original AmbientTalk object that received the selection
  528. * @param jSelector the selector of the message to be invoked, converted to a Java selector
  529. * @return a closure wrapping the method selected via the AmbientTalk selection.
  530. *
  531. * Example:
  532. * eval "[1,2,3].at"
  533. * => upSelection(aNATTable, "at")
  534. * => either NATTable must have a method base_at, which is then wrapped
  535. */
  536. public static final NativeClosure upMethodSelection(ATObject atOrigRcvr, String jSelector, ATSymbol origSelector) throws InterpreterException {
  537. Method m = JavaInterfaceAdaptor.getNativeATMethod(atOrigRcvr.getClass(), atOrigRcvr, jSelector, origSelector);
  538. return new NativeClosure(atOrigRcvr, new NativeMethod(m, origSelector));
  539. }
  540. /**
  541. * upInstanceCreation takes an explicit AmbientTalk 'new' invocation and turns it into an
  542. * implicit Java instance creation by calling a constructor. The initargs are upped as well
  543. * and are passed as arguments to the constructor.
  544. *
  545. * @param jRcvr the Java object having received the call to new
  546. * @param atInitargs the arguments to the constructor
  547. * @return a new instance of a Java class
  548. * @throws InterpreterException
  549. */
  550. public static final ATObject upInstanceCreation(ATObject jRcvr, ATTable atInitargs) throws InterpreterException {
  551. ATObject[] args = atInitargs.asNativeTable().elements_;
  552. return JavaInterfaceAdaptor.createNativeATObject(jRcvr.getClass(), args);
  553. }
  554. public static final ATObject upExceptionCreation(InterpreterException jRcvr, ATTable atInitargs) throws InterpreterException {
  555. ATObject[] args = atInitargs.asNativeTable().elements_;
  556. //return JavaInterfaceAdaptor.createNativeATObject(jRcvr.getClass(), args);
  557. return new NATException((InterpreterException)
  558. Symbiosis.symbioticInstanceCreation(jRcvr.getClass(), args)
  559. .asJavaObjectUnderSymbiosis().getWrappedObject());
  560. }
  561. /**
  562. * Pass an AmbientTalk meta-level object into the base-level
  563. */
  564. public static final ATObject downObject(ATObject metaObject) throws InterpreterException {
  565. return metaObject;
  566. /*if (metaObject.meta_isStripedWith(NativeStripes._MIRROR_).asNativeBoolean().javaValue) {
  567. return metaObject.meta_select(metaObject, OBJMirrorRoot._BASE_NAME_);
  568. } else {
  569. return metaObject; // most native objects represent both the object at the base and at the meta-level
  570. }*/
  571. }
  572. /**
  573. * Pass an AmbientTalk base-level object to the meta-level
  574. */
  575. public static final ATObject upObject(ATObject baseObject) {
  576. if (baseObject instanceof NATMirage) {
  577. return ((NATMirage) baseObject).getMirror();
  578. } else {
  579. return baseObject;
  580. }
  581. }
  582. /**
  583. * Returns, for a given AmbientTalk object atObj, an array of NativeField objects corresponding
  584. * to all non-static methods of that object's Java class, where each method's name is prefixed with 'base_get'
  585. */
  586. public static final ATField[] downBaseLevelFields(ATObject atObj) throws InterpreterException {
  587. Method[] allBaseGetMethods =
  588. JavaInterfaceAdaptor.allMethodsPrefixed(atObj.getClass(), Reflection._BGET_PREFIX_, false);
  589. ATField[] fields = new ATField[allBaseGetMethods.length];
  590. for (int i = 0; i < allBaseGetMethods.length; i++) {
  591. Method m = allBaseGetMethods[i];
  592. fields[i] = downBaseLevelField(atObj, downBaseFieldAccessSelector(m.getName()));
  593. }
  594. return fields;
  595. }
  596. /**
  597. * Returns, for a given AmbientTalk object atObj, an array of NativeField objects corresponding
  598. * to all non-static methods of that object's Java class, where each method's name is prefixed with 'meta_get'
  599. */
  600. public static final ATField[] downMetaLevelFields(ATObject atObj) throws InterpreterException {
  601. Method[] allMetaGetMethods =
  602. JavaInterfaceAdaptor.allMethodsPrefixed(atObj.getClass(), Reflection._MGET_PREFIX_, false);
  603. ATField[] fields = new ATField[allMetaGetMethods.length];
  604. for (int i = 0; i < allMetaGetMethods.length; i++) {
  605. Method m = allMetaGetMethods[i];
  606. fields[i] = downMetaLevelField(atObj, downMetaFieldAccessSelector(m.getName()));
  607. }
  608. return fields;
  609. }
  610. /**
  611. * Returns, for a given AmbientTalk object atObj, an array of NativeMethod objects corresponding
  612. * to all non-static methods of that object's class, where each method's name:
  613. * - is prefixed with 'base_'
  614. * - is not prefixed with 'base_get'
  615. * - is not prefixed with 'base_set'
  616. */
  617. public static final ATMethod[] downBaseLevelMethods(ATObject atObj) throws InterpreterException {
  618. Method[] allBaseMethods =
  619. JavaInterfaceAdaptor.allMethodsPrefixed(atObj.getClass(), Reflection._BASE_PREFIX_, false);
  620. Vector allNonFieldBaseMethods = new Vector();
  621. for (int i = 0; i < allBaseMethods.length; i++) {
  622. Method m = allBaseMethods[i];
  623. String nam = m.getName();
  624. if (!((nam.startsWith(Reflection._BGET_PREFIX_)) ||
  625. (nam.startsWith(Reflection._BSET_PREFIX_)))) {
  626. allNonFieldBaseMethods.add(new NativeMethod(m, downBaseLevelSelector(nam)));
  627. }
  628. }
  629. return (ATMethod[]) allNonFieldBaseMethods.toArray(new ATMethod[allNonFieldBaseMethods.size()]);
  630. }
  631. /**
  632. * Returns, for a given AmbientTalk object natObj, an array of NativeMethod objects corresponding
  633. * to all non-static methods of that object's class, where each method's name:
  634. * - is prefixed with 'meta_'
  635. * - is not prefixed with 'meta_get'
  636. * - is not prefixed with 'meta_set'
  637. */
  638. public static final ATMethod[] downMetaLevelMethods(ATObject natObj) throws InterpreterException {
  639. Method[] allMetaMethods =
  640. JavaInterfaceAdaptor.allMethodsPrefixed(natObj.getClass(), Reflection._META_PREFIX_, false);
  641. Vector allNonFieldMetaMethods = new Vector();
  642. for (int i = 0; i < allMetaMethods.length; i++) {
  643. Method m = allMetaMethods[i];
  644. String nam = m.getName();
  645. if (!((nam.startsWith(Reflection._MGET_PREFIX_)) ||
  646. (nam.startsWith(Reflection._MSET_PREFIX_)))) {
  647. allNonFieldMetaMethods.add(new NativeMethod(m, downMetaLevelSelector(nam)));
  648. }
  649. }
  650. return (ATMethod[]) allNonFieldMetaMethods.toArray(new ATMethod[allNonFieldMetaMethods.size()]);
  651. }
  652. private static final Pattern oprCode = Pattern.compile("_op(\\w\\w\\w)_"); //'_op', 3 chars, '_'
  653. private static final Pattern symbol = Pattern.compile("\\W"); //any non-word character
  654. private static String stripPrefix(String input, String prefix) {
  655. // \A matches start of input
  656. // Backport from JDK 1.4 to 1.3
  657. // return input.replaceFirst("\\A"+prefix, "");
  658. return Pattern.compile("\\A"+prefix).matcher(new StringBuffer(input)).replaceFirst("");
  659. }
  660. private static final String oprCode2Symbol(String code) {
  661. switch (code.charAt(0)) {
  662. case 'p': if (code.equals("pls")) { return "+"; } else break;
  663. case 'm': if (code.equals("mns")) { return "-"; } else break;
  664. case 't': if (code.equals("tms")) { return "*"; } else
  665. if (code.equals("til")) { return "~"; } else break;
  666. case 'd': if (code.equals("div")) { return "/"; } else break;
  667. case 'b': if (code.equals("bsl")) { return "\\"; } else break;
  668. case 'a': if (code.equals("and")) { return "&"; } else break;
  669. case 'n': if (code.equals("not")) { return "!"; } else break;
  670. case 'g': if (code.equals("gtx")) { return ">"; } else break;
  671. case 'l': if (code.equals("ltx")) { return "<"; } else break;
  672. case 'e': if (code.equals("eql")) { return "="; } else break;
  673. case 'q': if (code.equals("que")) { return "?"; } else break;
  674. case 'r': if (code.equals("rem")) { return "%"; } else break;
  675. }
  676. return "_op" + code + "_"; // no match, return original input
  677. }
  678. private static final String symbol2oprCode(String symbol) {
  679. switch (symbol.charAt(0)) {
  680. case '+': return "pls";
  681. case '-': return "mns";
  682. case '*': return "tms";
  683. case '/': return "div";
  684. case '\\': return "bsl";
  685. case '&': return "and";
  686. case '!': return "not";
  687. case '>': return "gtx";
  688. case '<': return "ltx";
  689. case '=': return "eql";
  690. case '~': return "til";
  691. case '?': return "que";
  692. case '%': return "rem";
  693. default: return symbol; // no match, return original input
  694. }
  695. }
  696. private static final String javaToAmbientTalkSelector(String jSelector) {
  697. // _op{code}_ -> operator symbol
  698. Matcher m = oprCode.matcher(new StringBuffer(jSelector));
  699. StringBuffer sb = new StringBuffer();
  700. while (m.find()) {
  701. // find every occurence of _op\w\w\w_ and convert it into a symbol
  702. m.appendReplacement(sb, oprCode2Symbol(m.group(1)));
  703. }
  704. m.appendTail(sb);
  705. // _ -> :
  706. // Backport from JDK 1.4 to 1.3
  707. // return sb.toString().replaceAll("_", ":");
  708. return Pattern.compile("_").matcher(sb).replaceAll(":");
  709. }
  710. }