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

http://ambienttalk.googlecode.com/ · Java · 330 lines · 169 code · 34 blank · 127 comment · 22 complexity · 9aa0aac7ea68e37749724acf8809392f 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.XTypeMismatch;
  33. import edu.vub.at.exceptions.XUnassignableField;
  34. import edu.vub.at.exceptions.XUndefinedSlot;
  35. import edu.vub.at.objects.ATBoolean;
  36. import edu.vub.at.objects.ATField;
  37. import edu.vub.at.objects.ATMethod;
  38. import edu.vub.at.objects.ATNil;
  39. import edu.vub.at.objects.ATObject;
  40. import edu.vub.at.objects.ATTable;
  41. import edu.vub.at.objects.ATTypeTag;
  42. import edu.vub.at.objects.grammar.ATSymbol;
  43. import edu.vub.at.objects.mirrors.Reflection;
  44. import edu.vub.at.objects.natives.NATBoolean;
  45. import edu.vub.at.objects.natives.NATObject;
  46. import edu.vub.at.objects.natives.NATTable;
  47. import edu.vub.at.objects.natives.NATText;
  48. import java.io.Serializable;
  49. import java.lang.ref.SoftReference;
  50. import com.thoughtworks.xstream.core.util.ObjectIdDictionary;
  51. /**
  52. * JavaObject instances represent java objects under symbiosis.
  53. * A Java Object is represented in AmbientTalk as an AmbientTalk object where:
  54. * - the Java Object's fields and methods correspond to the AmbientTalk object's fields and methods
  55. * - overloaded methods are coerced into a single AmbientTalk method, which must be disambiguated at call site
  56. * - the Java Object has a shares-a relation with its class (i.e. the class of the object is its dynamic parent)
  57. *
  58. * In addition, a JavaObject carries with it a static type. The static type is used during method dispatch
  59. * to select the appropriate Java method to be invoked. A JavaObject's static type can be altered by casting it.
  60. * Casting can be done using JavaClass instances.
  61. *
  62. * @author tvcutsem
  63. */
  64. public final class JavaObject extends NATObject implements ATObject {
  65. /**
  66. * A thread-local identity hashmap pooling all of the JavaObject wrappers for
  67. * the current actor, referring to them using SOFT references, such
  68. * that unused wrappers can be GC-ed when running low on memory.
  69. *
  70. * Note that the use of an identity hashmap rather than a normal hashmap
  71. * is crucial here! Using a normal hashmap compares objects by means of their
  72. * equals method, which means that two distinct Java objects could be assigned
  73. * the same wrapper, which is obviously unwanted. Using an identity hashmap
  74. * avoids this.
  75. */
  76. private static final ThreadLocal _JAVAOBJECT_POOL_ = new ThreadLocal() {
  77. protected synchronized Object initialValue() {
  78. return new ObjectIdDictionary();
  79. }
  80. };
  81. /**
  82. * Return a unique appearance for the Java object.
  83. */
  84. public static final JavaObject wrapperFor(Object o) {
  85. ObjectIdDictionary map = (ObjectIdDictionary) _JAVAOBJECT_POOL_.get();
  86. if (map.containsId(o)) {
  87. SoftReference ref = (SoftReference) map.lookupId(o);
  88. JavaObject obj = (JavaObject) ref.get();
  89. if (obj != null) {
  90. return obj;
  91. } else {
  92. map.removeId(obj);
  93. obj = new JavaObject(o);
  94. map.associateId(o, new SoftReference(obj));
  95. return obj;
  96. }
  97. } else {
  98. JavaObject jo = new JavaObject(o);
  99. map.associateId(o, new SoftReference(jo));
  100. return jo;
  101. }
  102. }
  103. private final Object wrappedObject_;
  104. /**
  105. * A JavaObject wrapping an object o has a dynamic SHARES-A parent pointing to the
  106. * wrapper of o's class.
  107. *
  108. * A symbiotic Java object is tagged with all of the Java interface
  109. * type tags that correspond to the interface types implemented by the
  110. * wrapped Java object's class.
  111. */
  112. private JavaObject(Object wrappedObject) {
  113. super(JavaClass.wrapperFor(wrappedObject.getClass()), NATObject._SHARES_A_);
  114. wrappedObject_ = wrappedObject;
  115. // initialize the Java symbiotic object's type tags
  116. Class[] extendedInterfaces = wrappedObject_.getClass().getInterfaces();
  117. if (extendedInterfaces.length > 0) {
  118. typeTags_ = new ATTypeTag[extendedInterfaces.length];
  119. for (int i = 0; i < extendedInterfaces.length; i++) {
  120. typeTags_[i] = JavaClass.wrapperFor(extendedInterfaces[i]);
  121. }
  122. }
  123. }
  124. /**
  125. * @return the Java object denoted by this JavaObject
  126. */
  127. public Object getWrappedObject() {
  128. return wrappedObject_;
  129. }
  130. public boolean isJavaObjectUnderSymbiosis() {
  131. return true;
  132. }
  133. public JavaObject asJavaObjectUnderSymbiosis() throws XTypeMismatch {
  134. return this;
  135. }
  136. /* ------------------------------------------------------
  137. * - Symbiotic implementation of the ATObject interface -
  138. * ------------------------------------------------------ */
  139. /**
  140. * Fields can be defined within a symbiotic Java object. They are added
  141. * to its AmbientTalk symbiont, but only if they do not clash with already
  142. * existing field names.
  143. */
  144. public ATNil meta_defineField(ATSymbol name, ATObject value) throws InterpreterException {
  145. if (Symbiosis.hasField(wrappedObject_.getClass(), Reflection.upSelector(name), false)) {
  146. throw new XDuplicateSlot(name);
  147. } else {
  148. return super.meta_defineField(name, value);
  149. }
  150. }
  151. /**
  152. * Cloning a symbiotic object is not always possible as Java has no uniform cloning semantics.
  153. * Even if the symbiotic object implements java.lang.Cloneable, a clone cannot be made of
  154. * the wrapped object as java.lang.Object's clone method is protected, and must be overridden
  155. * by a public clone method in the cloneable subclass.
  156. */
  157. public ATObject meta_clone() throws InterpreterException {
  158. throw new XIllegalOperation("Cannot clone Java object under symbiosis: " + wrappedObject_.toString());
  159. }
  160. /**
  161. * Invoking new on a JavaObject will exhibit the same behaviour as if new was invoked on the parent class.
  162. */
  163. public ATObject meta_newInstance(ATTable initargs) throws InterpreterException {
  164. return base_super().meta_newInstance(initargs);
  165. }
  166. /**
  167. * Methods can be added to a symbiotic Java object provided they do not already
  168. * exist in the Java object's class.
  169. */
  170. public ATNil meta_addMethod(ATMethod method) throws InterpreterException {
  171. ATSymbol name = method.base_name();
  172. if (Symbiosis.hasMethod(wrappedObject_.getClass(), Reflection.upSelector(name), false)) {
  173. throw new XDuplicateSlot(name);
  174. } else {
  175. return super.meta_addMethod(method);
  176. }
  177. }
  178. /**
  179. * Fields can be grabbed from a symbiotic Java object. Fields that correspond
  180. * to fields in the Java object's class are returned as JavaField instances.
  181. */
  182. public ATField meta_grabField(ATSymbol fieldName) throws InterpreterException {
  183. try {
  184. return new JavaField(wrappedObject_,
  185. Symbiosis.getField(wrappedObject_.getClass(), Reflection.upSelector(fieldName), false));
  186. } catch(XUndefinedSlot e) {
  187. return super.meta_grabField(fieldName);
  188. }
  189. }
  190. /**
  191. * Methods can be grabbed from a symbiotic Java object. Methods that correspond
  192. * to methods in the Java object's class are returned as JavaMethod instances.
  193. */
  194. public ATMethod meta_grabMethod(ATSymbol methodName) throws InterpreterException {
  195. JavaMethod choices = Symbiosis.getMethods(wrappedObject_.getClass(), Reflection.upSelector(methodName), false);
  196. if (choices != null) {
  197. return choices;
  198. } else {
  199. return super.meta_grabMethod(methodName);
  200. }
  201. }
  202. /**
  203. * Querying a symbiotic Java object for its fields results in a table containing
  204. * both the 'native' Java fields and the fields of its AT symbiont
  205. */
  206. public ATTable meta_listFields() throws InterpreterException {
  207. // instance fields of the wrapped object's class
  208. JavaField[] jFields = Symbiosis.getAllFields(wrappedObject_, wrappedObject_.getClass());
  209. // fields of the AT symbiont
  210. ATObject[] symbiontFields = super.meta_listFields().asNativeTable().elements_;
  211. return NATTable.atValue(NATTable.collate(jFields, symbiontFields));
  212. }
  213. /**
  214. * Querying a symbiotic Java object for its methods results in a table containing
  215. * both all 'native' Java instance methods and the methods of its AT symbiont
  216. */
  217. public ATTable meta_listMethods() throws InterpreterException {
  218. // instance methods of the wrapped object's class
  219. JavaMethod[] jMethods = Symbiosis.getAllMethods(wrappedObject_.getClass(), false);
  220. // methods of the AT symbiont
  221. ATObject[] symbiontMethods = super.meta_listMethods().asNativeTable().elements_;
  222. return NATTable.atValue(NATTable.collate(jMethods, symbiontMethods));
  223. }
  224. public ATBoolean meta_isCloneOf(ATObject original) throws InterpreterException {
  225. return NATBoolean.atValue(this == original);
  226. }
  227. public NATText meta_print() throws InterpreterException {
  228. return NATText.atValue("<java:"+wrappedObject_.toString()+">");
  229. }
  230. /**
  231. * Passing a Java Object wrapper to another actor has the following effect:
  232. * - if the wrapped Java object is serializable, the symbiotic AmbientTalk object
  233. * is treated as by copy (i.e. as an isolate).
  234. * - if the wrapped Java object is not serializable, the symbiotic AmbientTalk object
  235. * is treated as by reference and a far reference will be passed instead.
  236. */
  237. public ATObject meta_pass() throws InterpreterException {
  238. if (wrappedObject_ instanceof Serializable) {
  239. return this;
  240. } else {
  241. return super.meta_pass();
  242. }
  243. }
  244. /**
  245. * If the wrapped object was serializable, we may be asked to resolve ourselves.
  246. */
  247. public ATObject meta_resolve() throws InterpreterException {
  248. if (wrappedObject_ instanceof Serializable) {
  249. return this;
  250. } else {
  251. return super.meta_resolve();
  252. }
  253. }
  254. // IMPLEMENTATION INTERFACE
  255. /**
  256. * A symbiotic Java object has all of the public non-static fields
  257. * of its Java class plus all of the fields defined in the AT symbiont.
  258. */
  259. protected boolean hasLocalField(ATSymbol atSelector) throws InterpreterException {
  260. return Symbiosis.hasField(wrappedObject_.getClass(), Reflection.upSelector(atSelector), false) ||
  261. super.hasLocalField(atSelector);
  262. }
  263. /**
  264. * A symbiotic Java object has all of the public non-static methods
  265. * of its Java class plus all of the methods defined in the AT symbiont.
  266. */
  267. protected boolean hasLocalMethod(ATSymbol atSelector) throws InterpreterException {
  268. return Symbiosis.hasMethod(wrappedObject_.getClass(), Reflection.upSelector(atSelector), false) ||
  269. super.hasLocalMethod(atSelector);
  270. }
  271. protected ATObject getLocalField(ATSymbol selector) throws InterpreterException {
  272. try {
  273. return Symbiosis.readField(wrappedObject_, wrappedObject_.getClass(), Reflection.upSelector(selector));
  274. } catch(XUndefinedSlot e) {
  275. return super.getLocalField(selector);
  276. }
  277. }
  278. protected ATMethod getLocalMethod(ATSymbol methodName) throws InterpreterException {
  279. JavaMethod choices = Symbiosis.getMethods(wrappedObject_.getClass(), Reflection.upSelector(methodName), false);
  280. if (choices != null) {
  281. return choices;
  282. } else {
  283. return super.getLocalMethod(methodName);
  284. }
  285. }
  286. protected void setLocalField(ATSymbol selector, ATObject value) throws InterpreterException {
  287. try {
  288. Symbiosis.writeField(wrappedObject_, wrappedObject_.getClass(), Reflection.upSelector(selector), value);
  289. } catch(XUndefinedSlot e) {
  290. super.setLocalField(selector, value);
  291. } catch(XUnassignableField e) {
  292. // field may have been final, in which case there 'is no such field'
  293. super.setLocalField(selector, value);
  294. }
  295. }
  296. }