PageRenderTime 28ms CodeModel.GetById 13ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/at2dist091109/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 */
 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}