PageRenderTime 23ms CodeModel.GetById 2ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/interpreter/tags/reactive-pattern-matching/src/edu/vub/at/objects/symbiosis/JavaClass.java

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