/interpreter/tags/at_build150307/src/edu/vub/at/objects/mirrors/Reflection.java
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}