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