PageRenderTime 49ms CodeModel.GetById 31ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

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

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