PageRenderTime 62ms CodeModel.GetById 13ms app.highlight 43ms RepoModel.GetById 1ms app.codeStats 0ms

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