NodeConstructor.java 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // Copyright (c) 2001, 2003 Per M.A. Bothner and Brainfood Inc.
  2. // This is free software; for terms and warranty disclaimer see ./COPYING.
  3. package gnu.kawa.xml;
  4. import gnu.bytecode.*;
  5. import gnu.mapping.*;
  6. import gnu.expr.*;
  7. import gnu.xml.*;
  8. import gnu.lists.*;
  9. import java.util.List;
  10. /* #ifdef use:java.lang.invoke */
  11. import java.lang.invoke.*;
  12. /* #endif */
  13. public abstract class NodeConstructor extends MethodProc
  14. implements Inlineable
  15. {
  16. private static final int STRING_IS_TEXT = 8;
  17. protected int options;
  18. public abstract void compileToNode (ApplyExp exp, Compilation comp,
  19. ConsumerTarget target);
  20. /** If true, top-level strings are treated as text nodes.
  21. * This means don't separate them with spaces when printing as XML.
  22. */
  23. public boolean getStringIsText() {
  24. return (options & STRING_IS_TEXT) != 0;
  25. }
  26. public void setStringIsText(boolean stringIsText) {
  27. if (stringIsText)
  28. options |= STRING_IS_TEXT;
  29. else
  30. options &= ~STRING_IS_TEXT;
  31. }
  32. //protected boolean stringIsText;
  33. public static XMLFilter pushNodeConsumer (Consumer out)
  34. {
  35. if (out instanceof XMLFilter)
  36. return (XMLFilter) out;
  37. else
  38. return new XMLFilter(new NodeTree());
  39. }
  40. public static void popNodeConsumer (Consumer saved, Consumer current)
  41. {
  42. if (saved != current)
  43. saved.writeObject(current instanceof XMLFilter
  44. ? (Object) KNode.make((NodeTree) ((XMLFilter) current).out)
  45. : (Object) current);
  46. }
  47. public static XMLFilter pushNodeContext (CallContext ctx)
  48. {
  49. Consumer out = ctx.consumer;
  50. if (out instanceof XMLFilter)
  51. return (XMLFilter) out;
  52. else
  53. {
  54. // FIXME: It would be more efficinet to just do:
  55. // filter = new XMLFilter(out);
  56. // There is at least one problem "JOINER" isn't handled properly;
  57. // it's not obvious how to ensure we get the right whitespace.
  58. XMLFilter filter = new XMLFilter(new NodeTree());
  59. ctx.consumer = filter;
  60. return filter;
  61. }
  62. }
  63. public static void popNodeContext (Consumer saved, CallContext ctx)
  64. {
  65. Object current = ctx.consumer;
  66. if (saved != current)
  67. {
  68. if (current instanceof XMLFilter)
  69. current = KNode.make((NodeTree) ((XMLFilter) current).out);
  70. saved.writeObject(current);
  71. ctx.consumer = saved;
  72. }
  73. }
  74. public static void compileChild (Expression arg, boolean stringIsText,
  75. Compilation comp, ConsumerTarget target) {
  76. if (arg instanceof ApplyExp) {
  77. ApplyExp app = (ApplyExp) arg;
  78. Expression func = app.getFunction();
  79. Object proc = func.valueIfConstant();
  80. // Don't call compileToNode if child is a MakeText, because
  81. // XQuery's rules for space-separating computed text nodes are
  82. // non-trivial and context-dependent.
  83. if (proc instanceof NodeConstructor
  84. && ! (proc instanceof MakeText)) {
  85. ((NodeConstructor) proc).compileToNode(app, comp, target);
  86. return;
  87. }
  88. }
  89. CodeAttr code = comp.getCode();
  90. if (arg instanceof QuoteExp) {
  91. Object value = ((QuoteExp) arg).getValue();
  92. if (value instanceof FString) {
  93. code.emitLoad(target.getConsumerVariable());
  94. code.emitPushString(value.toString());
  95. code.emitInvoke(Compilation.typeConsumer
  96. .getDeclaredMethod("write",
  97. new Type[] { Type.javalangStringType }));
  98. return;
  99. }
  100. }
  101. arg.compileWithPosition(comp, Target.pushObject);
  102. code.emitLoad(target.getConsumerVariable());
  103. code.emitInvokeStatic(ClassType.make("gnu.kawa.xml.NodeConstructor")
  104. .getDeclaredMethod(stringIsText ? "writeContentS"
  105. : "writeContent", 2));
  106. }
  107. /** Compile an expression using a fresh NodeTree.
  108. * Compare with ConsumerTarget.compileUsingConsumer, but creates a NodeTree.
  109. */
  110. public static void compileUsingNodeTree(Expression exp,
  111. Compilation comp, Target target)
  112. {
  113. Method makeMethod = typeNodeConstructor.getDeclaredMethod("makeNode", 0);
  114. Method makeKNodeMethod = typeNodeConstructor.getDeclaredMethod("finishNode", 1);
  115. ConsumerTarget.compileUsingConsumer(exp, comp, target,
  116. makeMethod, makeKNodeMethod);
  117. }
  118. public static XMLFilter makeNode ()
  119. {
  120. return new XMLFilter(new NodeTree());
  121. }
  122. public static KNode finishNode (XMLFilter filter)
  123. {
  124. return KNode.make((NodeTree) filter.out);
  125. }
  126. public boolean isSideEffectFree() { return true; }
  127. public void compile (ApplyExp exp, Compilation comp, Target target)
  128. {
  129. if (target instanceof IgnoreTarget)
  130. ApplyExp.compile(exp, comp, target);
  131. else if (! (target instanceof ConsumerTarget))
  132. compileUsingNodeTree(exp, comp, target);
  133. else
  134. {
  135. ConsumerTarget ctarget = (ConsumerTarget) target;
  136. Variable cvar = ctarget.getConsumerVariable();
  137. Type ctype = cvar.getType();
  138. if (ctype.isSubtype(typeXMLFilter))
  139. compileToNode(exp, comp, ctarget);
  140. else
  141. {
  142. Expression[] args = exp.getArgs();
  143. int nargs = args.length;
  144. CodeAttr code = comp.getCode();
  145. Scope scope = code.pushScope();
  146. Variable xvar
  147. = scope.addVariable(code, typeXMLFilter, null);
  148. if (ctarget.isContextTarget())
  149. {
  150. comp.loadCallContext();
  151. code.emitInvokeStatic(pushNodeContextMethod);
  152. }
  153. else
  154. {
  155. code.emitLoad(cvar);
  156. code.emitInvokeStatic(pushNodeConsumerMethod);
  157. }
  158. code.emitStore(xvar);
  159. code.emitTryStart(true, Type.void_type);
  160. ConsumerTarget xtarget = new ConsumerTarget(xvar);
  161. compileToNode(exp, comp, xtarget);
  162. code.emitFinallyStart();
  163. code.emitLoad(cvar);
  164. if (ctarget.isContextTarget())
  165. {
  166. comp.loadCallContext();
  167. code.emitInvokeStatic(popNodeContextMethod);
  168. }
  169. else
  170. {
  171. code.emitLoad(xvar);
  172. code.emitInvokeStatic(popNodeConsumerMethod);
  173. }
  174. code.emitFinallyEnd();
  175. code.emitTryCatchEnd();
  176. code.popScope();
  177. }
  178. }
  179. }
  180. public Type getReturnType (Expression[] args)
  181. {
  182. return Compilation.typeObject;
  183. }
  184. public static void writeContentS(Object arg, Consumer out) {
  185. if (arg instanceof CharSequence && ! (arg instanceof UnescapedData)) {
  186. CharSequence carg = (CharSequence) arg;
  187. out.write(carg, 0, carg.length());
  188. }
  189. else
  190. writeContent(arg, out);
  191. }
  192. public static void writeContent(Object arg, Consumer out) {
  193. if (arg instanceof List && ! (arg instanceof CharSequence)) {
  194. for (Object e : (List) arg) {
  195. writeContent1(e, out);
  196. }
  197. }
  198. else
  199. writeContent1(arg, out);
  200. }
  201. protected static void writeContent1(Object arg, Consumer out) {
  202. if (arg instanceof Consumable)
  203. ((Consumable) arg).consume(out);
  204. else
  205. Values.writeValues(arg, out);
  206. }
  207. static final ClassType typeXMLFilter
  208. = ClassType.make("gnu.xml.XMLFilter");
  209. static final ClassType typeKNode
  210. = ClassType.make("gnu.kawa.xml.KNode");
  211. static final ClassType typeNodeConstructor
  212. = ClassType.make("gnu.kawa.xml.NodeConstructor");
  213. static final Method pushNodeContextMethod
  214. = typeNodeConstructor.getDeclaredMethod("pushNodeContext", 1);
  215. static final Method popNodeContextMethod
  216. = typeNodeConstructor.getDeclaredMethod("popNodeContext", 2);
  217. static final Method pushNodeConsumerMethod
  218. = typeNodeConstructor.getDeclaredMethod("pushNodeConsumer", 1);
  219. static final Method popNodeConsumerMethod
  220. = typeNodeConstructor.getDeclaredMethod("popNodeConsumer", 2);
  221. }