AutoloadProcedure.java 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. package kawa.lang;
  2. import gnu.mapping.*;
  3. import gnu.expr.*;
  4. import java.io.*;
  5. import gnu.kawa.reflect.ClassMemberLocation;
  6. /**
  7. * Implement autoloading of Procedures.
  8. * A named class is loaded, and apply requests are forwarded to it.
  9. * @author Per Bothner
  10. */
  11. public class AutoloadProcedure extends Procedure implements Externalizable
  12. {
  13. /** The name of the class that defines the procedure.
  14. * It must be the name of a class in the CLASSPATH (for example:
  15. * "kawa.standard.list"), and the class must extend Procedure,
  16. * and have a default constructor.
  17. * If the Procedure is a ModuleBody, apply0() is applied,
  18. * and that is expected to define the Procedure in the global environment. */
  19. String className;
  20. Language language;
  21. /** The loaded procedure, or null if it has not yet been loaded. */
  22. Procedure loaded;
  23. public AutoloadProcedure ()
  24. {
  25. }
  26. public AutoloadProcedure (String name, String className)
  27. {
  28. super(name);
  29. this.className = className;
  30. }
  31. public AutoloadProcedure (String name, String className, Language language)
  32. {
  33. super(name);
  34. this.className = className;
  35. this.language = language;
  36. }
  37. public void print(java.io.PrintWriter ps)
  38. {
  39. ps.print ("#<procedure ");
  40. String name = getName();
  41. if (name != null)
  42. {
  43. ps.print (name);
  44. // ps.print (' ');
  45. }
  46. /*
  47. if (loaded != null)
  48. ps.print ("autoloaded");
  49. else
  50. {
  51. ps.print ("autoload ");
  52. ps.print (className);
  53. }
  54. */
  55. ps.print ('>');
  56. }
  57. private void throw_error (String prefix)
  58. {
  59. loaded = null;
  60. String name = getName();
  61. throw new RuntimeException (prefix + className
  62. + " while autoloading "
  63. + (name == null ? "" : name.toString ()));
  64. }
  65. static final Class classModuleBody = gnu.expr.ModuleBody.class;
  66. /** Load the class named in className. */
  67. void load ()
  68. {
  69. Object name = this.getSymbol();
  70. Language lang = this.language;
  71. if (lang == null)
  72. lang = Language.getDefaultLanguage();
  73. Environment env = lang.getLangEnvironment();
  74. // Should use something like isFunctionBound FIXME
  75. Symbol sym = (name instanceof Symbol ? (Symbol) name
  76. : env.getSymbol(name.toString()));
  77. try
  78. {
  79. Class procClass = Class.forName(className);
  80. if (classModuleBody.isAssignableFrom(procClass))
  81. {
  82. ModuleContext context = ModuleContext.getContext();
  83. Object mod = context.searchInstance(procClass);
  84. if (mod == null)
  85. {
  86. try
  87. {
  88. mod = procClass.getDeclaredField("$instance").get(null);
  89. }
  90. catch (NoSuchFieldException ex)
  91. {
  92. // Not a static module - create a new instance.
  93. mod = procClass.newInstance();
  94. }
  95. }
  96. ClassMemberLocation.defineAll(mod, lang, env);
  97. if (mod instanceof ModuleBody)
  98. ((ModuleBody)mod).run();
  99. Object value = env.getFunction(sym, null);
  100. if (value == null
  101. || !(value instanceof Procedure))
  102. throw_error
  103. ("invalid ModuleBody class - does not define " + name);
  104. loaded = (Procedure) value;
  105. }
  106. else
  107. {
  108. loaded = (Procedure) procClass.newInstance ();
  109. if (loaded == this)
  110. throw_error("circularity detected");
  111. if (name != null)
  112. {
  113. try
  114. {
  115. Object property = (lang.hasSeparateFunctionNamespace()
  116. ? EnvironmentKey.FUNCTION
  117. : null);
  118. env.put(sym, property, loaded);
  119. }
  120. catch (UnboundLocationException ex)
  121. {
  122. }
  123. }
  124. }
  125. if (name != null && loaded.getSymbol() == null)
  126. loaded.setSymbol(name);
  127. }
  128. catch (ClassNotFoundException ex)
  129. { throw_error ("failed to find class "); }
  130. catch (InstantiationException ex)
  131. { throw_error ("failed to instantiate class "); }
  132. catch (IllegalAccessException ex)
  133. { throw_error ("illegal access in class "); }
  134. }
  135. public Procedure getLoaded ()
  136. {
  137. if (loaded == null)
  138. load();
  139. return loaded;
  140. }
  141. public int numArgs ()
  142. {
  143. return getLoaded().numArgs();
  144. }
  145. public Object apply0 () throws Throwable
  146. {
  147. return getLoaded().apply0 ();
  148. }
  149. public Object apply1 (Object arg1) throws Throwable
  150. {
  151. return getLoaded().apply1 (arg1);
  152. }
  153. public Object apply2 (Object arg1,Object arg2) throws Throwable
  154. {
  155. return getLoaded().apply2 (arg1, arg2);
  156. }
  157. public Object apply3 (Object arg1, Object arg2, Object arg3) throws Throwable
  158. {
  159. return getLoaded().apply3 (arg1, arg2, arg3);
  160. }
  161. public Object apply4 (Object arg1, Object arg2,
  162. Object arg3, Object arg4) throws Throwable
  163. {
  164. return getLoaded().apply4 (arg1, arg2, arg3, arg4);
  165. }
  166. public Object applyN (Object[] args) throws Throwable
  167. {
  168. if (loaded == null)
  169. load ();
  170. if (loaded instanceof AutoloadProcedure)
  171. throw new InternalError("circularity in autoload of "+getName());
  172. return loaded.applyN (args);
  173. }
  174. public Procedure getSetter()
  175. {
  176. if (loaded == null)
  177. load ();
  178. if (loaded instanceof HasSetter)
  179. return loaded.getSetter();
  180. return super.getSetter();
  181. }
  182. public void writeExternal(ObjectOutput out) throws IOException
  183. {
  184. out.writeObject(getName());
  185. out.writeObject(className);
  186. }
  187. public void readExternal(ObjectInput in)
  188. throws IOException, ClassNotFoundException
  189. {
  190. setName((String) in.readObject());
  191. className = (String) in.readObject();
  192. }
  193. public Object getProperty(Object key, Object defaultValue)
  194. {
  195. Object value = super.getProperty(key, null);
  196. if (value != null)
  197. return value;
  198. return getLoaded().getProperty(key, defaultValue);
  199. }
  200. }