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