PageRenderTime 39ms CodeModel.GetById 20ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://ambienttalk.googlecode.com/
Java | 386 lines | 199 code | 41 blank | 146 comment | 21 complexity | 86c29b37a24fe177fc31d4abf3242ec0 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * JavaClass.java created on 3-nov-2006 at 10:54:38
  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.XArityMismatch;
 32import edu.vub.at.exceptions.XDuplicateSlot;
 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.ATContext;
 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.ATTable;
 43import edu.vub.at.objects.ATTypeTag;
 44import edu.vub.at.objects.coercion.NativeTypeTags;
 45import edu.vub.at.objects.grammar.ATSymbol;
 46import edu.vub.at.objects.mirrors.PrimitiveMethod;
 47import edu.vub.at.objects.mirrors.Reflection;
 48import edu.vub.at.objects.natives.NATBoolean;
 49import edu.vub.at.objects.natives.NATNumber;
 50import edu.vub.at.objects.natives.NATObject;
 51import edu.vub.at.objects.natives.NATTable;
 52import edu.vub.at.objects.natives.NATText;
 53import edu.vub.at.objects.natives.grammar.AGSymbol;
 54import edu.vub.at.util.logging.Logging;
 55import edu.vub.util.IdentityHashMap;
 56
 57import java.lang.ref.SoftReference;
 58
 59/**
 60 * A JavaClass instance represents a Java Class under symbiosis.
 61 * 
 62 * Java classes are treated as AmbientTalk 'singleton' objects:
 63 * 
 64 *  - cloning a Java class results in the same Java class instance
 65 *  - sending 'new' to a Java class invokes the constructor and returns a new instance of the class under symbiosis
 66 *  - all static fields and methods of the Java class are reflected under symbiosis as fields and methods of the AT object
 67 *  
 68 * A Java Class object that represents an interface can furthermore be used
 69 * as an AmbientTalk type. The type's name corresponds to the interface's full name.
 70 *  
 71 * JavaClass instances are pooled (on a per-actor basis): there should exist only one JavaClass instance
 72 * for each Java class loaded into the JVM. Because the JVM ensures that a Java class
 73 * can only be loaded once, we can use the Java class wrapped by the JavaClass instance
 74 * as a unique key to identify its corresponding JavaClass instance.
 75 *  
 76 * @author tvcutsem
 77 */
 78public final class JavaClass extends NATObject implements ATTypeTag {
 79	
 80	/**
 81	 * A thread-local hashmap pooling all of the JavaClass wrappers for
 82	 * the current actor, referring to them using SOFT references, such
 83	 * that unused wrappers can be GC-ed when running low on memory.
 84	 */
 85	private static final ThreadLocal _JAVACLASS_POOL_ = new ThreadLocal() {
 86        protected synchronized Object initialValue() {
 87            return new IdentityHashMap();
 88        }
 89	};
 90	
 91	/**
 92	 * Allocate a unique symbiont object for the given Java class.
 93	 */
 94	public static final JavaClass wrapperFor(Class c) {
 95		IdentityHashMap map = (IdentityHashMap) _JAVACLASS_POOL_.get();
 96		if (map.containsKey(c)) {
 97			SoftReference ref = (SoftReference) map.get(c);
 98			JavaClass cls = (JavaClass) ref.get();
 99			if (cls != null) {
100				return cls;
101			} else {
102				map.remove(c);
103				cls = new JavaClass(c);
104				map.put(c, new SoftReference(cls));
105				return cls;
106			}
107		} else {
108			JavaClass jc = new JavaClass(c);
109			map.put(c, new SoftReference(jc));
110			return jc;
111		}
112	}
113	
114	// primitive fields and method of a JavaClass wrapper
115	
116	private static final AGSymbol _PTS_NAME_ = AGSymbol.jAlloc("parentTypes");
117	private static final AGSymbol _TNM_NAME_ = AGSymbol.jAlloc("typeName");
118	
119	/** def isSubtypeOf(type) { nil } */
120	private static final PrimitiveMethod _PRIM_STP_ = new PrimitiveMethod(
121			AGSymbol.jAlloc("isSubtypeOf"), NATTable.atValue(new ATObject[] { AGSymbol.jAlloc("type")})) {
122      
123	    private static final long serialVersionUID = -6864350539143194204L;
124
125		public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException {
126			if (!arguments.base_length().equals(NATNumber.ONE)) {
127				throw new XArityMismatch("isSubtypeOf", 1, arguments.base_length().asNativeNumber().javaValue);
128			}
129			return ctx.base_lexicalScope().asJavaClassUnderSymbiosis().base_isSubtypeOf(arguments.base_at(NATNumber.ONE).asTypeTag());
130		}
131	};
132	
133	private final Class wrappedClass_;
134	
135	/**
136	 * A JavaClass wrapping a class c is an object that has the lexical scope as its lexical parent
137	 * and has NIL as its dynamic parent.
138	 * 
139	 * If the JavaClass wraps a Java interface type, JavaClass instances are
140	 * also types.
141	 */
142	private JavaClass(Class wrappedClass) {
143		super(wrappedClass.isInterface() ?
144			  new ATTypeTag[] { NativeTypeTags._TYPETAG_, NativeTypeTags._ISOLATE_ } :
145			  NATObject._NO_TYPETAGS_);
146		wrappedClass_ = wrappedClass;
147		
148		// add the two fields and one method needed for an ATTypeTag
149		if (wrappedClass.isInterface()) {
150			Class[] extendedInterfaces = wrappedClass_.getInterfaces();
151			ATObject[] types = new ATObject[extendedInterfaces.length];
152			for (int i = 0; i < extendedInterfaces.length; i++) {
153				types[i] = JavaClass.wrapperFor(extendedInterfaces[i]);
154			}
155			
156			try {
157				super.meta_defineField(_PTS_NAME_, NATTable.atValue(types));
158				super.meta_defineField(_TNM_NAME_, AGSymbol.jAlloc(wrappedClass_.getName()));
159				super.meta_addMethod(_PRIM_STP_);
160			} catch (InterpreterException e) {
161				Logging.Actor_LOG.fatal("Error while initializing Java Class as type tag: " + wrappedClass.getName(), e);
162			}
163		}
164	}
165	
166	/** return the class object denoted by this AmbientTalk symbiont */
167	public Class getWrappedClass() { return wrappedClass_; }
168	
169	public JavaClass asJavaClassUnderSymbiosis() throws XTypeMismatch { return this; }
170	
171    public ATBoolean base__opeql__opeql_(ATObject comparand) throws InterpreterException {
172        return NATBoolean.atValue(this.equals(comparand));
173    }
174	
175	public boolean equals(Object other) {
176		return ((other instanceof JavaClass) &&
177				(wrappedClass_.equals(((JavaClass) other).wrappedClass_)));
178	}
179        
180    /**
181     * Fields can be defined within a symbiotic Java class object. They are added
182     * to its AmbientTalk symbiont, but only if they do not clash with already
183     * existing field names.
184     */
185    public ATNil meta_defineField(ATSymbol name, ATObject value) throws InterpreterException {
186        if (Symbiosis.hasField(wrappedClass_, Reflection.upSelector(name), true)) {
187    	    throw new XDuplicateSlot(name);
188        } else {
189    	    return super.meta_defineField(name, value);
190        }
191    }
192    
193	/**
194	 * Symbiotic Java class objects are singletons.
195	 */
196	public ATObject meta_clone() throws InterpreterException { return this; }
197	
198	/**
199	 * aJavaClass.new(@args) == invoke a Java constructor
200	 * AmbientTalk objects can add a custom new method to the class in order to intercept
201	 * instance creation. The original instance can then be performed by invoking the old new(@args).
202	 * 
203	 * For example, imagine we want to extend the class java.lang.Point with a 3D coordinate, e.g. a 'z' field:
204	 * <tt>
205	 * def Point := jlobby.java.awt.Point;
206	 * def oldnew := Point.new;
207	 * def Point.new(x,y,z) { // 'override' the new method
208	 *   def point := oldnew(x,y); // invokes the Java constructor
209	 *   def point.z := z; // adds a field dynamically to the new JavaObject wrapper
210	 *   point; // important! new should return the newly created instance
211	 * }
212	 * def mypoint := Point.new(1,2,3);
213	 * </tt>
214	 */
215    public ATObject meta_newInstance(ATTable initargs) throws InterpreterException {
216    	return Symbiosis.symbioticInstanceCreation(wrappedClass_, initargs.asNativeTable().elements_);
217    }
218    
219    /**
220     * Methods can be added to a symbiotic Java class object provided they do not already
221     * exist in the Java class.
222     */
223    public ATNil meta_addMethod(ATMethod method) throws InterpreterException {
224        ATSymbol name = method.base_name();
225        if (Symbiosis.hasMethod(wrappedClass_, Reflection.upSelector(name), true)) {
226    	    throw new XDuplicateSlot(name);
227        } else {
228    	    return super.meta_addMethod(method);
229        }
230    }
231
232    /**
233     * Fields can be grabbed from a symbiotic Java class object. Fields that correspond
234     * to static fields in the Java class are returned as JavaField instances.
235     */
236    public ATField meta_grabField(ATSymbol fieldName) throws InterpreterException {
237        try {
238        	return new JavaField(null,
239        			             Symbiosis.getField(wrappedClass_, Reflection.upSelector(fieldName), true));
240        } catch(XUndefinedSlot e) {
241        	return super.meta_grabField(fieldName);
242        }
243    }
244
245    /**
246     * Methods can be grabbed from a symbiotic Java class object. Methods that correspond
247     * to static methods in the Java class are returned as JavaMethod instances.
248     */
249    public ATMethod meta_grabMethod(ATSymbol methodName) throws InterpreterException {
250        JavaMethod choices = Symbiosis.getMethods(wrappedClass_, Reflection.upSelector(methodName), true);
251        if (choices != null) {
252        	return choices;
253        } else {
254        	return super.meta_grabMethod(methodName);
255        }
256    }
257
258    /**
259     * Querying a symbiotic Java class object for its fields results in a table containing
260     * both 'native' static Java fields and the fields of its AT symbiont
261     */
262    public ATTable meta_listFields() throws InterpreterException {
263		// instance fields of the wrapped object's class
264		JavaField[] jFields = Symbiosis.getAllFields(null, wrappedClass_);
265        // fields of the AT symbiont
266    	ATObject[] symbiontFields = super.meta_listFields().asNativeTable().elements_;
267    	return NATTable.atValue(NATTable.collate(jFields, symbiontFields));
268    }
269
270    /**
271     * Querying a symbiotic Java class object for its methods results in a table containing
272     * both 'native' static Java methods and the methods of its AT symbiont
273     */
274    public ATTable meta_listMethods() throws InterpreterException {
275		// instance methods of the wrapped object's class
276		JavaMethod[] jMethods = Symbiosis.getAllMethods(wrappedClass_, true);
277        // methods of the AT symbiont
278		ATObject[] symbiontMethods = super.meta_listMethods().asNativeTable().elements_;
279		return NATTable.atValue(NATTable.collate(jMethods, symbiontMethods));
280    }
281
282	public ATBoolean meta_isCloneOf(ATObject original) throws InterpreterException {
283		return NATBoolean.atValue(this == original);
284	}
285	
286	public NATText meta_print() throws InterpreterException {
287		return NATText.atValue("<java:"+wrappedClass_.toString()+">");
288	}
289
290	/**
291	 * Java class wrappers may be passed by-copy since Java Class
292	 * objects are serializable. Upon deserialization however, the
293	 * class wrapper returned will be the one local to the receiving
294	 * actor.
295	 */
296	public ATObject meta_pass() throws InterpreterException {
297		return this;
298	}
299	
300	/**
301     * A Java Class object remains unique within an actor.
302     */
303    public ATObject meta_resolve() throws InterpreterException {
304    	return wrapperFor(wrappedClass_);
305    }
306    
307    /* ========================
308     * == ATTypeTag Interface ==
309     * ======================== */
310    
311    /**
312     * If this class represents an interface type, parentTypes
313     * are wrappers for all interfaces extended by this Java interface type
314     */
315	public ATTable base_superTypes() throws InterpreterException {
316		return super.impl_invokeAccessor(this, _PTS_NAME_, NATTable.EMPTY).asTable();
317	}
318
319	public ATSymbol base_typeName() throws InterpreterException {
320		return super.impl_invokeAccessor(this, _TNM_NAME_, NATTable.EMPTY).asSymbol();
321	}
322
323	/**
324	 * A Java interface type used as a type can only be a subtype of another
325	 * Java interface type used as a type, and only if this type is assignable
326	 * to the other type.
327	 */
328	public ATBoolean base_isSubtypeOf(ATTypeTag other) throws InterpreterException {
329		if (other instanceof JavaClass) {
330			JavaClass otherClass = (JavaClass) other;
331			// wrappedClass <: otherClass <=> otherClass >= wrappedClass
332			return NATBoolean.atValue(otherClass.wrappedClass_.isAssignableFrom(wrappedClass_));
333		} else {
334			return NATBoolean._FALSE_;
335		}
336	}
337	
338	// IMPLEMENTATION INTERFACE
339	
340    /**
341     * A symbiotic Java class object has all of the public static fields
342     * of its Java class plus all of the fields defined in the AT symbiont.
343     */
344	protected boolean hasLocalField(ATSymbol atSelector) throws InterpreterException {
345		return Symbiosis.hasField(wrappedClass_, Reflection.upSelector(atSelector), true) ||
346			   super.hasLocalField(atSelector);
347	}
348
349	/**
350     * A symbiotic Java class object has all of the public static methods
351     * of its Java class plus all of the methods defined in the AT symbiont.
352     */
353	protected boolean hasLocalMethod(ATSymbol atSelector) throws InterpreterException {
354		return Symbiosis.hasMethod(wrappedClass_, Reflection.upSelector(atSelector), true) ||
355		       super.hasLocalMethod(atSelector);
356	}
357	
358	protected ATObject getLocalField(ATSymbol selector) throws InterpreterException {
359		try {
360			return Symbiosis.readField(null, wrappedClass_, Reflection.upSelector(selector));
361		} catch(XUndefinedSlot e) {
362			return super.getLocalField(selector);
363		}
364	}
365	
366	protected ATMethod getLocalMethod(ATSymbol methodName) throws InterpreterException {
367        JavaMethod choices = Symbiosis.getMethods(wrappedClass_, Reflection.upSelector(methodName), true);
368        if (choices != null) {
369        	return choices;
370        } else {
371        	return super.getLocalMethod(methodName);
372        }
373	}
374	
375	protected void setLocalField(ATSymbol selector, ATObject value) throws InterpreterException {
376		try {
377			Symbiosis.writeField(null, wrappedClass_, Reflection.upSelector(selector), value);
378		} catch(XUndefinedSlot e) {
379			super.setLocalField(selector, value);
380		} catch(XUnassignableField e) {
381			// field may have been final, in which case there 'is no such field'
382			super.setLocalField(selector, value);
383		}
384	}
385	
386}