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

http://ambienttalk.googlecode.com/ · Java · 366 lines · 198 code · 32 blank · 136 comment · 24 complexity · f7a2a881e2068f41a75ab017925592d0 MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * JavaObject.java created on 3-nov-2006 at 11:32:48
  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.symbiosis;
  29. import edu.vub.at.exceptions.InterpreterException;
  30. import edu.vub.at.exceptions.XDuplicateSlot;
  31. import edu.vub.at.exceptions.XIllegalOperation;
  32. import edu.vub.at.exceptions.XSelectorNotFound;
  33. import edu.vub.at.exceptions.XTypeMismatch;
  34. import edu.vub.at.exceptions.XUnassignableField;
  35. import edu.vub.at.exceptions.XUndefinedField;
  36. import edu.vub.at.objects.ATBoolean;
  37. import edu.vub.at.objects.ATField;
  38. import edu.vub.at.objects.ATMethod;
  39. import edu.vub.at.objects.ATNil;
  40. import edu.vub.at.objects.ATObject;
  41. import edu.vub.at.objects.ATStripe;
  42. import edu.vub.at.objects.ATTable;
  43. import edu.vub.at.objects.grammar.ATSymbol;
  44. import edu.vub.at.objects.mirrors.Reflection;
  45. import edu.vub.at.objects.natives.NATBoolean;
  46. import edu.vub.at.objects.natives.NATNil;
  47. import edu.vub.at.objects.natives.NATObject;
  48. import edu.vub.at.objects.natives.NATTable;
  49. import edu.vub.at.objects.natives.NATText;
  50. import java.io.Serializable;
  51. import java.lang.ref.SoftReference;
  52. import java.util.HashMap;
  53. /**
  54. * JavaObject instances represent java objects under symbiosis.
  55. * A Java Object is represented in AmbientTalk as an AmbientTalk object where:
  56. * - the Java Object's fields and methods correspond to the AmbientTalk object's fields and methods
  57. * - overloaded methods are coerced into a single AmbientTalk method, which must be disambiguated at call site
  58. * - the Java Object has a shares-a relation with its class (i.e. the class of the object is its dynamic parent)
  59. *
  60. * In addition, a JavaObject carries with it a static type. The static type is used during method dispatch
  61. * to select the appropriate Java method to be invoked. A JavaObject's static type can be altered by casting it.
  62. * Casting can be done using JavaClass instances.
  63. *
  64. * @author tvcutsem
  65. */
  66. public final class JavaObject extends NATObject implements ATObject {
  67. /**
  68. * A thread-local hashmap pooling all of the JavaObject wrappers for
  69. * the current actor, referring to them using SOFT references, such
  70. * that unused wrappers can be GC-ed when running low on memory.
  71. */
  72. private static final ThreadLocal _JAVAOBJECT_POOL_ = new ThreadLocal() {
  73. protected synchronized Object initialValue() {
  74. return new HashMap();
  75. }
  76. };
  77. public static final JavaObject wrapperFor(Object o) {
  78. HashMap map = (HashMap) _JAVAOBJECT_POOL_.get();
  79. if (map.containsKey(o)) {
  80. SoftReference ref = (SoftReference) map.get(o);
  81. JavaObject obj = (JavaObject) ref.get();
  82. if (obj != null) {
  83. return obj;
  84. } else {
  85. map.remove(obj);
  86. obj = new JavaObject(o);
  87. map.put(o, new SoftReference(obj));
  88. return obj;
  89. }
  90. } else {
  91. JavaObject jo = new JavaObject(o);
  92. map.put(o, new SoftReference(jo));
  93. return jo;
  94. }
  95. }
  96. private final Object wrappedObject_;
  97. /**
  98. * A JavaObject wrapping an object o has a dynamic SHARES-A parent pointing to the
  99. * wrapper of o's class.
  100. *
  101. * A symbiotic Java object is striped with all of the Java interface
  102. * stripes that correspond to the interface types implemented by the
  103. * wrapped Java object's class.
  104. */
  105. private JavaObject(Object wrappedObject) {
  106. super(JavaClass.wrapperFor(wrappedObject.getClass()), NATObject._SHARES_A_);
  107. wrappedObject_ = wrappedObject;
  108. // initialize the Java symbiotic object's stripes
  109. Class[] extendedInterfaces = wrappedObject_.getClass().getInterfaces();
  110. if (extendedInterfaces.length > 0) {
  111. stripes_ = new ATStripe[extendedInterfaces.length];
  112. for (int i = 0; i < extendedInterfaces.length; i++) {
  113. stripes_[i] = JavaClass.wrapperFor(extendedInterfaces[i]);
  114. }
  115. }
  116. }
  117. public Object getWrappedObject() {
  118. return wrappedObject_;
  119. }
  120. public boolean isJavaObjectUnderSymbiosis() {
  121. return true;
  122. }
  123. public JavaObject asJavaObjectUnderSymbiosis() throws XTypeMismatch {
  124. return this;
  125. }
  126. /* ------------------------------------------------------
  127. * - Symbiotic implementation of the ATObject interface -
  128. * ------------------------------------------------------ */
  129. /**
  130. * When a method is invoked upon a symbiotic Java object, an underlying Java method
  131. * with the same name as the AmbientTalk selector is invoked. Its arguments are converted
  132. * into their Java equivalents. Conversely, the result of the method invocation is converted
  133. * into an AmbientTalk object. If no such method exists, a method is searched for in the
  134. * symbiotic AmbientTalk part.
  135. */
  136. public ATObject meta_invoke(ATObject receiver, ATSymbol atSelector, ATTable arguments) throws InterpreterException {
  137. try {
  138. String jSelector = Reflection.upSelector(atSelector);
  139. return Symbiosis.symbioticInvocation(
  140. this, wrappedObject_, wrappedObject_.getClass(), jSelector,
  141. arguments.asNativeTable().elements_);
  142. } catch (XSelectorNotFound e) {
  143. e.catchOnlyIfSelectorEquals(atSelector);
  144. return super.meta_invoke(receiver, atSelector, arguments);
  145. }
  146. }
  147. /**
  148. * A symbiotic Java object responds to all of the public non-static selectors of its Java class
  149. * plus all of the per-instance selectors added to its AmbientTalk symbiont.
  150. */
  151. public ATBoolean meta_respondsTo(ATSymbol atSelector) throws InterpreterException {
  152. String jSelector = Reflection.upSelector(atSelector);
  153. if (Symbiosis.hasMethod(wrappedObject_.getClass(), jSelector, false) ||
  154. Symbiosis.hasField(wrappedObject_.getClass(), jSelector, false)) {
  155. return NATBoolean._TRUE_;
  156. } else {
  157. return super.meta_respondsTo(atSelector);
  158. }
  159. }
  160. /**
  161. * When selecting a field from a symbiotic Java object, if the
  162. * Java symbiont object's class has a non-static field with a matching selector,
  163. * it is automatically read; if it has a corresponding method, the method is returned
  164. * in a closure. If no matching field is found, the fields and methods of the
  165. * AmbientTalk symbiont are checked.
  166. */
  167. public ATObject meta_select(ATObject receiver, ATSymbol selector) throws InterpreterException {
  168. String jSelector = Reflection.upSelector(selector);
  169. try {
  170. return Symbiosis.readField(wrappedObject_, wrappedObject_.getClass(), jSelector);
  171. } catch(XUndefinedField e) {
  172. JavaMethod choices = Symbiosis.getMethods(wrappedObject_.getClass(), jSelector, false);
  173. if (choices != null) {
  174. return new JavaClosure(this, choices);
  175. } else {
  176. return super.meta_select(receiver, selector);
  177. }
  178. }
  179. }
  180. /**
  181. * A variable lookup is resolved by first checking whether the Java object has a field with
  182. * a matching name. If not, the symbiotic AmbientTalk object is checked.
  183. */
  184. public ATObject meta_lookup(ATSymbol selector) throws InterpreterException {
  185. try {
  186. String jSelector = Reflection.upSelector(selector);
  187. return Symbiosis.readField(wrappedObject_, wrappedObject_.getClass(), jSelector);
  188. } catch(XUndefinedField e) {
  189. return super.meta_lookup(selector);
  190. }
  191. }
  192. /**
  193. * Fields can be defined within a symbiotic Java object. They are added
  194. * to its AmbientTalk symbiont, but only if they do not clash with already
  195. * existing field names.
  196. */
  197. public ATNil meta_defineField(ATSymbol name, ATObject value) throws InterpreterException {
  198. if (Symbiosis.hasField(wrappedObject_.getClass(), Reflection.upSelector(name), false)) {
  199. throw new XDuplicateSlot(XDuplicateSlot._FIELD_, name);
  200. } else {
  201. return super.meta_defineField(name, value);
  202. }
  203. }
  204. /**
  205. * Variables can be assigned within a symbiotic Java object if that object's class
  206. * has a mutable field with a matching name.
  207. */
  208. public ATNil meta_assignVariable(ATSymbol name, ATObject value) throws InterpreterException {
  209. try {
  210. String jSelector = Reflection.upSelector(name);
  211. Symbiosis.writeField(wrappedObject_, wrappedObject_.getClass(), jSelector, value);
  212. return NATNil._INSTANCE_;
  213. } catch (XUnassignableField e) {
  214. return super.meta_assignVariable(name, value);
  215. }
  216. }
  217. /**
  218. * Fields can be assigned within a symbiotic Java object if that object's class
  219. * has a mutable field with a matching name. Field assignment is first resolved
  220. * in the wrapped Java object and afterwards in the AT symbiont.
  221. */
  222. public ATNil meta_assignField(ATObject receiver, ATSymbol name, ATObject value) throws InterpreterException {
  223. try {
  224. String jSelector = Reflection.upSelector(name);
  225. Symbiosis.writeField(wrappedObject_, wrappedObject_.getClass(), jSelector, value);
  226. return NATNil._INSTANCE_;
  227. } catch (XUnassignableField e) {
  228. return super.meta_assignField(receiver, name, value);
  229. }
  230. }
  231. /**
  232. * Cloning a symbiotic object is not always possible as Java has no uniform cloning semantics.
  233. * Even if the symbiotic object implements java.lang.Cloneable, a clone cannot be made of
  234. * the wrapped object as java.lang.Object's clone method is protected, and must be overridden
  235. * by a public clone method in the cloneable subclass.
  236. */
  237. public ATObject meta_clone() throws InterpreterException {
  238. throw new XIllegalOperation("Cannot clone Java object under symbiosis: " + wrappedObject_.toString());
  239. }
  240. /**
  241. * Invoking new on a JavaObject will exhibit the same behaviour as if new was invoked on the parent class.
  242. */
  243. public ATObject meta_newInstance(ATTable initargs) throws InterpreterException {
  244. return base_getSuper().meta_newInstance(initargs);
  245. }
  246. /**
  247. * Methods can be added to a symbiotic Java object provided they do not already
  248. * exist in the Java object's class.
  249. */
  250. public ATNil meta_addMethod(ATMethod method) throws InterpreterException {
  251. ATSymbol name = method.base_getName();
  252. if (Symbiosis.hasMethod(wrappedObject_.getClass(), Reflection.upSelector(name), false)) {
  253. throw new XDuplicateSlot(XDuplicateSlot._METHOD_, name);
  254. } else {
  255. return super.meta_addMethod(method);
  256. }
  257. }
  258. /**
  259. * Fields can be grabbed from a symbiotic Java object. Fields that correspond
  260. * to fields in the Java object's class are returned as JavaField instances.
  261. */
  262. public ATField meta_grabField(ATSymbol fieldName) throws InterpreterException {
  263. try {
  264. return new JavaField(wrappedObject_,
  265. Symbiosis.getField(wrappedObject_.getClass(), Reflection.upSelector(fieldName), false));
  266. } catch(XUndefinedField e) {
  267. return super.meta_grabField(fieldName);
  268. }
  269. }
  270. /**
  271. * Methods can be grabbed from a symbiotic Java object. Methods that correspond
  272. * to methods in the Java object's class are returned as JavaMethod instances.
  273. */
  274. public ATMethod meta_grabMethod(ATSymbol methodName) throws InterpreterException {
  275. JavaMethod choices = Symbiosis.getMethods(wrappedObject_.getClass(), Reflection.upSelector(methodName), false);
  276. if (choices != null) {
  277. return choices;
  278. } else {
  279. return super.meta_grabMethod(methodName);
  280. }
  281. }
  282. /**
  283. * Querying a symbiotic Java object for its fields results in a table containing
  284. * both the 'native' Java fields and the fields of its AT symbiont
  285. */
  286. public ATTable meta_listFields() throws InterpreterException {
  287. // instance fields of the wrapped object's class
  288. JavaField[] jFields = Symbiosis.getAllFields(wrappedObject_, wrappedObject_.getClass());
  289. // fields of the AT symbiont
  290. ATObject[] symbiontFields = super.meta_listFields().asNativeTable().elements_;
  291. return NATTable.atValue(NATTable.collate(jFields, symbiontFields));
  292. }
  293. /**
  294. * Querying a symbiotic Java object for its methods results in a table containing
  295. * both all 'native' Java instance methods and the methods of its AT symbiont
  296. */
  297. public ATTable meta_listMethods() throws InterpreterException {
  298. // instance methods of the wrapped object's class
  299. JavaMethod[] jMethods = Symbiosis.getAllMethods(wrappedObject_.getClass(), false);
  300. // methods of the AT symbiont
  301. ATObject[] symbiontMethods = super.meta_listMethods().asNativeTable().elements_;
  302. return NATTable.atValue(NATTable.collate(jMethods, symbiontMethods));
  303. }
  304. public ATBoolean meta_isCloneOf(ATObject original) throws InterpreterException {
  305. return NATBoolean.atValue(this == original);
  306. }
  307. public NATText meta_print() throws InterpreterException {
  308. return NATText.atValue("<java:"+wrappedObject_.toString()+">");
  309. }
  310. /**
  311. * Passing a Java Object wrapper to another actor has the following effect:
  312. * - if the wrapped Java object is serializable, the symbiotic AmbientTalk object
  313. * is treated as by copy (i.e. as an isolate).
  314. * - if the wrapped Java object is not serializable, the symbiotic AmbientTalk object
  315. * is treated as by reference and a far reference will be passed instead.
  316. */
  317. public ATObject meta_pass() throws InterpreterException {
  318. if (wrappedObject_ instanceof Serializable) {
  319. return this;
  320. } else {
  321. return super.meta_pass();
  322. }
  323. }
  324. /**
  325. * If the wrapped object was serializable, we may be asked to resolve ourselves.
  326. */
  327. public ATObject meta_resolve() throws InterpreterException {
  328. if (wrappedObject_ instanceof Serializable) {
  329. return this;
  330. } else {
  331. return super.meta_resolve();
  332. }
  333. }
  334. }