1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * $Id: TestSeq.java,v 1.11 2004/02/16 22:25:10 minchau Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.xsltc.compiler;
  20. import java.util.Dictionary;
  21. import java.util.Vector;
  22. import com.sun.org.apache.bcel.internal.generic.GOTO_W;
  23. import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
  24. import com.sun.org.apache.bcel.internal.generic.InstructionList;
  25. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  26. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  27. /**
  28. * A test sequence is a sequence of patterns that
  29. *
  30. * (1) occured in templates in the same mode
  31. * (2) share the same kernel node type (e.g. A/B and C/C/B)
  32. * (3) may also contain patterns matching "*" and "node()"
  33. * (element sequence only) or matching "@*" (attribute
  34. * sequence only).
  35. *
  36. * A test sequence may have a default template, which will be
  37. * instantiated if none of the other patterns match.
  38. * @author Jacek Ambroziak
  39. * @author Santiago Pericas-Geertsen
  40. * @author Erwin Bolwidt <ejb@klomp.org>
  41. * @author Morten Jorgensen <morten.jorgensen@sun.com>
  42. */
  43. final class TestSeq {
  44. /**
  45. * Integer code for the kernel type of this test sequence
  46. */
  47. private int _kernelType;
  48. /**
  49. * Vector of all patterns in the test sequence. May include
  50. * patterns with "*", "@*" or "node()" kernel.
  51. */
  52. private Vector _patterns = null;
  53. /**
  54. * A reference to the Mode object.
  55. */
  56. private Mode _mode = null;
  57. /**
  58. * Default template for this test sequence
  59. */
  60. private Template _default = null;
  61. /**
  62. * Instruction list representing this test sequence.
  63. */
  64. private InstructionList _instructionList;
  65. /**
  66. * Cached handle to avoid compiling more than once.
  67. */
  68. private InstructionHandle _start = null;
  69. /**
  70. * Creates a new test sequence given a set of patterns and a mode.
  71. */
  72. public TestSeq(Vector patterns, Mode mode) {
  73. this(patterns, -2, mode);
  74. }
  75. public TestSeq(Vector patterns, int kernelType, Mode mode) {
  76. _patterns = patterns;
  77. _kernelType = kernelType;
  78. _mode = mode;
  79. }
  80. /**
  81. * Returns a string representation of this test sequence. Notice
  82. * that test sequences are mutable, so the value returned by this
  83. * method is different before and after calling reduce().
  84. */
  85. public String toString() {
  86. final int count = _patterns.size();
  87. final StringBuffer result = new StringBuffer();
  88. for (int i = 0; i < count; i++) {
  89. final LocationPathPattern pattern =
  90. (LocationPathPattern) _patterns.elementAt(i);
  91. if (i == 0) {
  92. result.append("Testseq for kernel " + _kernelType)
  93. .append('\n');
  94. }
  95. result.append(" pattern " + i + ": ")
  96. .append(pattern.toString())
  97. .append('\n');
  98. }
  99. return result.toString();
  100. }
  101. /**
  102. * Returns the instruction list for this test sequence
  103. */
  104. public InstructionList getInstructionList() {
  105. return _instructionList;
  106. }
  107. /**
  108. * Return the highest priority for a pattern in this test
  109. * sequence. This is either the priority of the first or
  110. * of the default pattern.
  111. */
  112. public double getPriority() {
  113. final Template template = (_patterns.size() == 0) ? _default
  114. : ((Pattern) _patterns.elementAt(0)).getTemplate();
  115. return template.getPriority();
  116. }
  117. /**
  118. * Returns the position of the highest priority pattern in
  119. * this test sequence.
  120. */
  121. public int getPosition() {
  122. final Template template = (_patterns.size() == 0) ? _default
  123. : ((Pattern) _patterns.elementAt(0)).getTemplate();
  124. return template.getPosition();
  125. }
  126. /**
  127. * Reduce the patterns in this test sequence. Creates a new
  128. * vector of patterns and sets the default pattern if it
  129. * finds a patterns that is fully reduced.
  130. */
  131. public void reduce() {
  132. final Vector newPatterns = new Vector();
  133. final int count = _patterns.size();
  134. for (int i = 0; i < count; i++) {
  135. final LocationPathPattern pattern =
  136. (LocationPathPattern)_patterns.elementAt(i);
  137. // Reduce this pattern
  138. pattern.reduceKernelPattern();
  139. // Is this pattern fully reduced?
  140. if (pattern.isWildcard()) {
  141. _default = pattern.getTemplate();
  142. break; // Ignore following patterns
  143. }
  144. else {
  145. newPatterns.addElement(pattern);
  146. }
  147. }
  148. _patterns = newPatterns;
  149. }
  150. /**
  151. * Returns, by reference, the templates that are included in
  152. * this test sequence. Note that a single template can occur
  153. * in several test sequences if its pattern is a union.
  154. */
  155. public void findTemplates(Dictionary templates) {
  156. if (_default != null) {
  157. templates.put(_default, this);
  158. }
  159. for (int i = 0; i < _patterns.size(); i++) {
  160. final LocationPathPattern pattern =
  161. (LocationPathPattern)_patterns.elementAt(i);
  162. templates.put(pattern.getTemplate(), this);
  163. }
  164. }
  165. /**
  166. * Get the instruction handle to a template's code. This is
  167. * used when a single template occurs in several test
  168. * sequences; that is, if its pattern is a union of patterns
  169. * (e.g. match="A/B | A/C").
  170. */
  171. private InstructionHandle getTemplateHandle(Template template) {
  172. return (InstructionHandle)_mode.getTemplateInstructionHandle(template);
  173. }
  174. /**
  175. * Returns pattern n in this test sequence
  176. */
  177. private LocationPathPattern getPattern(int n) {
  178. return (LocationPathPattern)_patterns.elementAt(n);
  179. }
  180. /**
  181. * Compile the code for this test sequence. Compile patterns
  182. * from highest to lowest priority. Note that since patterns
  183. * can be share by multiple test sequences, instruction lists
  184. * must be copied before backpatching.
  185. */
  186. public InstructionHandle compile(ClassGenerator classGen,
  187. MethodGenerator methodGen,
  188. InstructionHandle continuation)
  189. {
  190. // Returned cached value if already compiled
  191. if (_start != null) {
  192. return _start;
  193. }
  194. // If not patterns, then return handle for default template
  195. final int count = _patterns.size();
  196. if (count == 0) {
  197. return (_start = getTemplateHandle(_default));
  198. }
  199. // Init handle to jump when all patterns failed
  200. InstructionHandle fail = (_default == null) ? continuation
  201. : getTemplateHandle(_default);
  202. // Compile all patterns in reverse order
  203. for (int n = count - 1; n >= 0; n--) {
  204. final LocationPathPattern pattern = getPattern(n);
  205. final Template template = pattern.getTemplate();
  206. final InstructionList il = new InstructionList();
  207. // Patterns expect current node on top of stack
  208. il.append(methodGen.loadCurrentNode());
  209. // Apply the test-code compiled for the pattern
  210. InstructionList ilist = _mode.getInstructionList(pattern);
  211. if (ilist == null) {
  212. ilist = pattern.compile(classGen, methodGen);
  213. _mode.addInstructionList(pattern, ilist);
  214. }
  215. // Make a copy of the instruction list for backpatching
  216. InstructionList copyOfilist = ilist.copy();
  217. FlowList trueList = pattern.getTrueList();
  218. if (trueList != null) {
  219. trueList = trueList.copyAndRedirect(ilist, copyOfilist);
  220. }
  221. FlowList falseList = pattern.getFalseList();
  222. if (falseList != null) {
  223. falseList = falseList.copyAndRedirect(ilist, copyOfilist);
  224. }
  225. il.append(copyOfilist);
  226. // On success branch to the template code
  227. final InstructionHandle gtmpl = getTemplateHandle(template);
  228. final InstructionHandle success = il.append(new GOTO_W(gtmpl));
  229. if (trueList != null) {
  230. trueList.backPatch(success);
  231. }
  232. if (falseList != null) {
  233. falseList.backPatch(fail);
  234. }
  235. // Next pattern's 'fail' target is this pattern's first instruction
  236. fail = il.getStart();
  237. // Append existing instruction list to the end of this one
  238. if (_instructionList != null) {
  239. il.append(_instructionList);
  240. }
  241. // Set current instruction list to be this one
  242. _instructionList = il;
  243. }
  244. return (_start = fail);
  245. }
  246. }