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: Mode.java,v 1.31 2004/02/16 22:24:29 minchau Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.xsltc.compiler;
  20. import java.util.Enumeration;
  21. import java.util.Hashtable;
  22. import java.util.Iterator;
  23. import java.util.Vector;
  24. import com.sun.org.apache.bcel.internal.generic.Instruction;
  25. import com.sun.org.apache.bcel.internal.generic.BranchHandle;
  26. import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  27. import com.sun.org.apache.bcel.internal.generic.DUP;
  28. import com.sun.org.apache.bcel.internal.generic.GOTO_W;
  29. import com.sun.org.apache.bcel.internal.generic.IFLT;
  30. import com.sun.org.apache.bcel.internal.generic.ILOAD;
  31. import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  32. import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  33. import com.sun.org.apache.bcel.internal.generic.ISTORE;
  34. import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
  35. import com.sun.org.apache.bcel.internal.generic.InstructionList;
  36. import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  37. import com.sun.org.apache.bcel.internal.generic.SWITCH;
  38. import com.sun.org.apache.bcel.internal.generic.TargetLostException;
  39. import com.sun.org.apache.bcel.internal.util.InstructionFinder;
  40. import com.sun.org.apache.xalan.internal.xsltc.DOM;
  41. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  42. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  43. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NamedMethodGenerator;
  44. import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  45. import com.sun.org.apache.xalan.internal.xsltc.dom.Axis;
  46. import com.sun.org.apache.xml.internal.dtm.DTM;
  47. /**
  48. * Mode gathers all the templates belonging to a given mode;
  49. * it is responsible for generating an appropriate
  50. * applyTemplates + (mode name) method in the translet.
  51. *
  52. * @author Jacek Ambroziak
  53. * @author Santiago Pericas-Geertsen
  54. * @author Morten Jorgensen
  55. * @author Erwin Bolwidt <ejb@klomp.org>
  56. * @author G. Todd Miller
  57. */
  58. final class Mode implements Constants {
  59. /**
  60. * The name of this mode as defined in the stylesheet.
  61. */
  62. private final QName _name;
  63. /**
  64. * A reference to the stylesheet object that owns this mode.
  65. */
  66. private final Stylesheet _stylesheet;
  67. /**
  68. * The name of the method in which this mode is compiled.
  69. */
  70. private final String _methodName;
  71. /**
  72. * A vector of all the templates in this mode.
  73. */
  74. private Vector _templates;
  75. /**
  76. * Group for patterns with node()-type kernel and child axis.
  77. */
  78. private Vector _childNodeGroup = null;
  79. /**
  80. * Test sequence for patterns with node()-type kernel and child axis.
  81. */
  82. private TestSeq _childNodeTestSeq = null;
  83. /**
  84. * Group for patterns with node()-type kernel and attribute axis.
  85. */
  86. private Vector _attribNodeGroup = null;
  87. /**
  88. * Test sequence for patterns with node()-type kernel and attribute axis.
  89. */
  90. private TestSeq _attribNodeTestSeq = null;
  91. /**
  92. * Group for patterns with id() or key()-type kernel.
  93. */
  94. private Vector _idxGroup = null;
  95. /**
  96. * Test sequence for patterns with id() or key()-type kernel.
  97. */
  98. private TestSeq _idxTestSeq = null;
  99. /**
  100. * Group for patterns with any other kernel type.
  101. */
  102. private Vector[] _patternGroups;
  103. /**
  104. * Test sequence for patterns with any other kernel type.
  105. */
  106. private TestSeq[] _testSeq;
  107. /**
  108. * A mapping between patterns and instruction lists used by
  109. * test sequences to avoid compiling the same pattern multiple
  110. * times. Note that patterns whose kernels are "*", "node()"
  111. * and "@*" can between shared by test sequences.
  112. */
  113. private Hashtable _preCompiled = new Hashtable();
  114. /**
  115. * A mapping between templates and test sequences.
  116. */
  117. private Hashtable _neededTemplates = new Hashtable();
  118. /**
  119. * A mapping between named templates and Mode objects.
  120. */
  121. private Hashtable _namedTemplates = new Hashtable();
  122. /**
  123. * A mapping between templates and instruction handles.
  124. */
  125. private Hashtable _templateIHs = new Hashtable();
  126. /**
  127. * A mapping between templates and instruction lists.
  128. */
  129. private Hashtable _templateILs = new Hashtable();
  130. /**
  131. * A reference to the pattern matching the root node.
  132. */
  133. private LocationPathPattern _rootPattern = null;
  134. /**
  135. * Stores ranges of template precendences for the compilation
  136. * of apply-imports (a Hashtable for historical reasons).
  137. */
  138. private Hashtable _importLevels = null;
  139. /**
  140. * A mapping between key names and keys.
  141. */
  142. private Hashtable _keys = null;
  143. /**
  144. * Variable index for the current node used in code generation.
  145. */
  146. private int _currentIndex;
  147. /**
  148. * Creates a new Mode.
  149. *
  150. * @param name A textual representation of the mode's QName
  151. * @param stylesheet The Stylesheet in which the mode occured
  152. * @param suffix A suffix to append to the method name for this mode
  153. * (normally a sequence number - still in a String).
  154. */
  155. public Mode(QName name, Stylesheet stylesheet, String suffix) {
  156. _name = name;
  157. _stylesheet = stylesheet;
  158. _methodName = APPLY_TEMPLATES + suffix;
  159. _templates = new Vector();
  160. _patternGroups = new Vector[32];
  161. }
  162. /**
  163. * Returns the name of the method (_not_ function) that will be
  164. * compiled for this mode. Normally takes the form 'applyTemplates()'
  165. * or * 'applyTemplates2()'.
  166. *
  167. * @return Method name for this mode
  168. */
  169. public String functionName() {
  170. return _methodName;
  171. }
  172. public String functionName(int min, int max) {
  173. if (_importLevels == null) {
  174. _importLevels = new Hashtable();
  175. }
  176. _importLevels.put(new Integer(max), new Integer(min));
  177. return _methodName + '_' + max;
  178. }
  179. /**
  180. * Add a pre-compiled pattern to this mode.
  181. */
  182. public void addInstructionList(Pattern pattern,
  183. InstructionList ilist)
  184. {
  185. _preCompiled.put(pattern, ilist);
  186. }
  187. /**
  188. * Get the instruction list for a pre-compiled pattern. Used by
  189. * test sequences to avoid compiling patterns more than once.
  190. */
  191. public InstructionList getInstructionList(Pattern pattern) {
  192. return (InstructionList) _preCompiled.get(pattern);
  193. }
  194. /**
  195. * Shortcut to get the class compiled for this mode (will be inlined).
  196. */
  197. private String getClassName() {
  198. return _stylesheet.getClassName();
  199. }
  200. public Stylesheet getStylesheet() {
  201. return _stylesheet;
  202. }
  203. public void addTemplate(Template template) {
  204. _templates.addElement(template);
  205. }
  206. private Vector quicksort(Vector templates, int p, int r) {
  207. if (p < r) {
  208. final int q = partition(templates, p, r);
  209. quicksort(templates, p, q);
  210. quicksort(templates, q + 1, r);
  211. }
  212. return templates;
  213. }
  214. private int partition(Vector templates, int p, int r) {
  215. final Template x = (Template)templates.elementAt(p);
  216. int i = p - 1;
  217. int j = r + 1;
  218. while (true) {
  219. while (x.compareTo((Template)templates.elementAt(--j)) > 0);
  220. while (x.compareTo((Template)templates.elementAt(++i)) < 0);
  221. if (i < j) {
  222. templates.set(j, templates.set(i, templates.elementAt(j)));
  223. }
  224. else {
  225. return j;
  226. }
  227. }
  228. }
  229. /**
  230. * Process all the test patterns in this mode
  231. */
  232. public void processPatterns(Hashtable keys) {
  233. _keys = keys;
  234. /*
  235. System.out.println("Before Sort " + _name);
  236. for (int i = 0; i < _templates.size(); i++) {
  237. System.out.println("name = " + ((Template)_templates.elementAt(i)).getName());
  238. System.out.println("pattern = " + ((Template)_templates.elementAt(i)).getPattern());
  239. System.out.println("priority = " + ((Template)_templates.elementAt(i)).getPriority());
  240. System.out.println("position = " + ((Template)_templates.elementAt(i)).getPosition());
  241. }
  242. */
  243. _templates = quicksort(_templates, 0, _templates.size() - 1);
  244. /*
  245. System.out.println("\n After Sort " + _name);
  246. for (int i = 0; i < _templates.size(); i++) {
  247. System.out.println("name = " + ((Template)_templates.elementAt(i)).getName());
  248. System.out.println("pattern = " + ((Template)_templates.elementAt(i)).getPattern());
  249. System.out.println("priority = " + ((Template)_templates.elementAt(i)).getPriority());
  250. System.out.println("position = " + ((Template)_templates.elementAt(i)).getPosition());
  251. }
  252. */
  253. // Traverse all templates
  254. final Enumeration templates = _templates.elements();
  255. while (templates.hasMoreElements()) {
  256. // Get the next template
  257. final Template template = (Template)templates.nextElement();
  258. /*
  259. * Add this template to a table of named templates if it has a name.
  260. * If there are multiple templates with the same name, all but one
  261. * (the one with highest priority) will be disabled.
  262. */
  263. if (template.isNamed() && !template.disabled()) {
  264. _namedTemplates.put(template, this);
  265. }
  266. // Add this template to a test sequence if it has a pattern
  267. final Pattern pattern = template.getPattern();
  268. if (pattern != null) {
  269. flattenAlternative(pattern, template, keys);
  270. }
  271. }
  272. prepareTestSequences();
  273. }
  274. /**
  275. * This method will break up alternative patterns (ie. unions of patterns,
  276. * such as match="A/B | C/B") and add the basic patterns to their
  277. * respective pattern groups.
  278. */
  279. private void flattenAlternative(Pattern pattern,
  280. Template template,
  281. Hashtable keys) {
  282. // Patterns on type id() and key() are special since they do not have
  283. // any kernel node type (it can be anything as long as the node is in
  284. // the id's or key's index).
  285. if (pattern instanceof IdKeyPattern) {
  286. final IdKeyPattern idkey = (IdKeyPattern)pattern;
  287. idkey.setTemplate(template);
  288. if (_idxGroup == null) _idxGroup = new Vector();
  289. _idxGroup.add(pattern);
  290. }
  291. // Alternative patterns are broken up and re-processed recursively
  292. else if (pattern instanceof AlternativePattern) {
  293. final AlternativePattern alt = (AlternativePattern)pattern;
  294. flattenAlternative(alt.getLeft(), template, keys);
  295. flattenAlternative(alt.getRight(), template, keys);
  296. }
  297. // Finally we have a pattern that can be added to a test sequence!
  298. else if (pattern instanceof LocationPathPattern) {
  299. final LocationPathPattern lpp = (LocationPathPattern)pattern;
  300. lpp.setTemplate(template);
  301. addPatternToGroup(lpp);
  302. }
  303. }
  304. /**
  305. * Group patterns by NodeTests of their last Step
  306. * Keep them sorted by priority within group
  307. */
  308. private void addPatternToGroup(final LocationPathPattern lpp) {
  309. // id() and key()-type patterns do not have a kernel type
  310. if (lpp instanceof IdKeyPattern) {
  311. addPattern(-1, lpp);
  312. }
  313. // Otherwise get the kernel pattern from the LPP
  314. else {
  315. // kernel pattern is the last (maybe only) Step
  316. final StepPattern kernel = lpp.getKernelPattern();
  317. if (kernel != null) {
  318. addPattern(kernel.getNodeType(), lpp);
  319. }
  320. else if (_rootPattern == null ||
  321. lpp.noSmallerThan(_rootPattern)) {
  322. _rootPattern = lpp;
  323. }
  324. }
  325. }
  326. /**
  327. * Adds a pattern to a pattern group
  328. */
  329. private void addPattern(int kernelType, LocationPathPattern pattern) {
  330. // Make sure the array of pattern groups is long enough
  331. final int oldLength = _patternGroups.length;
  332. if (kernelType >= oldLength) {
  333. Vector[] newGroups = new Vector[kernelType * 2];
  334. System.arraycopy(_patternGroups, 0, newGroups, 0, oldLength);
  335. _patternGroups = newGroups;
  336. }
  337. // Find the vector to put this pattern into
  338. Vector patterns;
  339. if (kernelType == DOM.NO_TYPE) {
  340. if (pattern.getAxis() == Axis.ATTRIBUTE) {
  341. patterns = (_attribNodeGroup == null) ?
  342. (_attribNodeGroup = new Vector(2)) : _attribNodeGroup;
  343. }
  344. else {
  345. patterns = (_childNodeGroup == null) ?
  346. (_childNodeGroup = new Vector(2)) : _childNodeGroup;
  347. }
  348. }
  349. else {
  350. patterns = (_patternGroups[kernelType] == null) ?
  351. (_patternGroups[kernelType] = new Vector(2)) :
  352. _patternGroups[kernelType];
  353. }
  354. if (patterns.size() == 0) {
  355. patterns.addElement(pattern);
  356. }
  357. else {
  358. boolean inserted = false;
  359. for (int i = 0; i < patterns.size(); i++) {
  360. final LocationPathPattern lppToCompare =
  361. (LocationPathPattern)patterns.elementAt(i);
  362. if (pattern.noSmallerThan(lppToCompare)) {
  363. inserted = true;
  364. patterns.insertElementAt(pattern, i);
  365. break;
  366. }
  367. }
  368. if (inserted == false) {
  369. patterns.addElement(pattern);
  370. }
  371. }
  372. }
  373. /**
  374. * Complete test sequences of a given type by adding all patterns
  375. * from a given group.
  376. */
  377. private void completeTestSequences(int nodeType, Vector patterns) {
  378. if (patterns != null) {
  379. if (_patternGroups[nodeType] == null) {
  380. _patternGroups[nodeType] = patterns;
  381. }
  382. else {
  383. final int m = patterns.size();
  384. for (int j = 0; j < m; j++) {
  385. addPattern(nodeType,
  386. (LocationPathPattern) patterns.elementAt(j));
  387. }
  388. }
  389. }
  390. }
  391. /**
  392. * Build test sequences. The first step is to complete the test sequences
  393. * by including patterns of "*" and "node()" kernel to all element test
  394. * sequences, and of "@*" to all attribute test sequences.
  395. */
  396. private void prepareTestSequences() {
  397. final Vector starGroup = _patternGroups[DTM.ELEMENT_NODE];
  398. final Vector atStarGroup = _patternGroups[DTM.ATTRIBUTE_NODE];
  399. // Complete test sequence for "text()" with "child::node()"
  400. completeTestSequences(DTM.TEXT_NODE, _childNodeGroup);
  401. // Complete test sequence for "*" with "child::node()"
  402. completeTestSequences(DTM.ELEMENT_NODE, _childNodeGroup);
  403. // Complete test sequence for "pi()" with "child::node()"
  404. completeTestSequences(DTM.PROCESSING_INSTRUCTION_NODE, _childNodeGroup);
  405. // Complete test sequence for "comment()" with "child::node()"
  406. completeTestSequences(DTM.COMMENT_NODE, _childNodeGroup);
  407. // Complete test sequence for "@*" with "attribute::node()"
  408. completeTestSequences(DTM.ATTRIBUTE_NODE, _attribNodeGroup);
  409. final Vector names = _stylesheet.getXSLTC().getNamesIndex();
  410. if (starGroup != null || atStarGroup != null ||
  411. _childNodeGroup != null || _attribNodeGroup != null)
  412. {
  413. final int n = _patternGroups.length;
  414. // Complete test sequence for user-defined types
  415. for (int i = DTM.NTYPES; i < n; i++) {
  416. if (_patternGroups[i] == null) continue;
  417. final String name = (String) names.elementAt(i - DTM.NTYPES);
  418. if (isAttributeName(name)) {
  419. // If an attribute then copy "@*" to its test sequence
  420. completeTestSequences(i, atStarGroup);
  421. // And also copy "attribute::node()" to its test sequence
  422. completeTestSequences(i, _attribNodeGroup);
  423. }
  424. else {
  425. // If an element then copy "*" to its test sequence
  426. completeTestSequences(i, starGroup);
  427. // And also copy "child::node()" to its test sequence
  428. completeTestSequences(i, _childNodeGroup);
  429. }
  430. }
  431. }
  432. _testSeq = new TestSeq[DTM.NTYPES + names.size()];
  433. final int n = _patternGroups.length;
  434. for (int i = 0; i < n; i++) {
  435. final Vector patterns = _patternGroups[i];
  436. if (patterns != null) {
  437. final TestSeq testSeq = new TestSeq(patterns, i, this);
  438. // System.out.println("testSeq[" + i + "] = " + testSeq);
  439. testSeq.reduce();
  440. _testSeq[i] = testSeq;
  441. testSeq.findTemplates(_neededTemplates);
  442. }
  443. }
  444. if (_childNodeGroup != null && _childNodeGroup.size() > 0) {
  445. _childNodeTestSeq = new TestSeq(_childNodeGroup, -1, this);
  446. _childNodeTestSeq.reduce();
  447. _childNodeTestSeq.findTemplates(_neededTemplates);
  448. }
  449. /*
  450. if (_attribNodeGroup != null && _attribNodeGroup.size() > 0) {
  451. _attribNodeTestSeq = new TestSeq(_attribNodeGroup, -1, this);
  452. _attribNodeTestSeq.reduce();
  453. _attribNodeTestSeq.findTemplates(_neededTemplates);
  454. }
  455. */
  456. if (_idxGroup != null && _idxGroup.size() > 0) {
  457. _idxTestSeq = new TestSeq(_idxGroup, this);
  458. _idxTestSeq.reduce();
  459. _idxTestSeq.findTemplates(_neededTemplates);
  460. }
  461. if (_rootPattern != null) {
  462. // doesn't matter what is 'put', only key matters
  463. _neededTemplates.put(_rootPattern.getTemplate(), this);
  464. }
  465. }
  466. private void compileNamedTemplate(Template template,
  467. ClassGenerator classGen) {
  468. final ConstantPoolGen cpg = classGen.getConstantPool();
  469. final InstructionList il = new InstructionList();
  470. String methodName = Util.escape(template.getName().toString());
  471. int numParams = 0;
  472. if (template.isSimpleNamedTemplate()) {
  473. Vector parameters = template.getParameters();
  474. numParams = parameters.size();
  475. }
  476. // Initialize the types and names arrays for the NamedMethodGenerator.
  477. com.sun.org.apache.bcel.internal.generic.Type[] types =
  478. new com.sun.org.apache.bcel.internal.generic.Type[4 + numParams];
  479. String[] names = new String[4 + numParams];
  480. types[0] = Util.getJCRefType(DOM_INTF_SIG);
  481. types[1] = Util.getJCRefType(NODE_ITERATOR_SIG);
  482. types[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG);
  483. types[3] = com.sun.org.apache.bcel.internal.generic.Type.INT;
  484. names[0] = DOCUMENT_PNAME;
  485. names[1] = ITERATOR_PNAME;
  486. names[2] = TRANSLET_OUTPUT_PNAME;
  487. names[3] = NODE_PNAME;
  488. // For simple named templates, the signature of the generated method
  489. // is not fixed. It depends on the number of parameters declared in the
  490. // template.
  491. for (int i = 4; i < 4 + numParams; i++) {
  492. types[i] = Util.getJCRefType(OBJECT_SIG);
  493. names[i] = "param" + String.valueOf(i-4);
  494. }
  495. NamedMethodGenerator methodGen =
  496. new NamedMethodGenerator(ACC_PUBLIC,
  497. com.sun.org.apache.bcel.internal.generic.Type.VOID,
  498. types, names, methodName,
  499. getClassName(), il, cpg);
  500. il.append(template.compile(classGen, methodGen));
  501. il.append(RETURN);
  502. methodGen.stripAttributes(true);
  503. methodGen.setMaxLocals();
  504. methodGen.setMaxStack();
  505. methodGen.removeNOPs();
  506. classGen.addMethod(methodGen.getMethod());
  507. }
  508. private void compileTemplates(ClassGenerator classGen,
  509. MethodGenerator methodGen,
  510. InstructionHandle next)
  511. {
  512. Enumeration templates = _namedTemplates.keys();
  513. while (templates.hasMoreElements()) {
  514. final Template template = (Template)templates.nextElement();
  515. compileNamedTemplate(template, classGen);
  516. }
  517. templates = _neededTemplates.keys();
  518. while (templates.hasMoreElements()) {
  519. final Template template = (Template)templates.nextElement();
  520. if (template.hasContents()) {
  521. // !!! TODO templates both named and matched
  522. InstructionList til = template.compile(classGen, methodGen);
  523. til.append(new GOTO_W(next));
  524. _templateILs.put(template, til);
  525. _templateIHs.put(template, til.getStart());
  526. }
  527. else {
  528. // empty template
  529. _templateIHs.put(template, next);
  530. }
  531. }
  532. }
  533. private void appendTemplateCode(InstructionList body) {
  534. final Enumeration templates = _neededTemplates.keys();
  535. while (templates.hasMoreElements()) {
  536. final Object iList =
  537. _templateILs.get(templates.nextElement());
  538. if (iList != null) {
  539. body.append((InstructionList)iList);
  540. }
  541. }
  542. }
  543. private void appendTestSequences(InstructionList body) {
  544. final int n = _testSeq.length;
  545. for (int i = 0; i < n; i++) {
  546. final TestSeq testSeq = _testSeq[i];
  547. if (testSeq != null) {
  548. InstructionList il = testSeq.getInstructionList();
  549. if (il != null)
  550. body.append(il);
  551. // else trivial TestSeq
  552. }
  553. }
  554. }
  555. public static void compileGetChildren(ClassGenerator classGen,
  556. MethodGenerator methodGen,
  557. int node) {
  558. final ConstantPoolGen cpg = classGen.getConstantPool();
  559. final InstructionList il = methodGen.getInstructionList();
  560. final int git = cpg.addInterfaceMethodref(DOM_INTF,
  561. GET_CHILDREN,
  562. GET_CHILDREN_SIG);
  563. il.append(methodGen.loadDOM());
  564. il.append(new ILOAD(node));
  565. il.append(new INVOKEINTERFACE(git, 2));
  566. }
  567. /**
  568. * Compiles the default handling for DOM elements: traverse all children
  569. */
  570. private InstructionList compileDefaultRecursion(ClassGenerator classGen,
  571. MethodGenerator methodGen,
  572. InstructionHandle next) {
  573. final ConstantPoolGen cpg = classGen.getConstantPool();
  574. final InstructionList il = new InstructionList();
  575. final String applyTemplatesSig = classGen.getApplyTemplatesSig();
  576. final int git = cpg.addInterfaceMethodref(DOM_INTF,
  577. GET_CHILDREN,
  578. GET_CHILDREN_SIG);
  579. final int applyTemplates = cpg.addMethodref(getClassName(),
  580. functionName(),
  581. applyTemplatesSig);
  582. il.append(classGen.loadTranslet());
  583. il.append(methodGen.loadDOM());
  584. il.append(methodGen.loadDOM());
  585. il.append(new ILOAD(_currentIndex));
  586. il.append(new INVOKEINTERFACE(git, 2));
  587. il.append(methodGen.loadHandler());
  588. il.append(new INVOKEVIRTUAL(applyTemplates));
  589. il.append(new GOTO_W(next));
  590. return il;
  591. }
  592. /**
  593. * Compiles the default action for DOM text nodes and attribute nodes:
  594. * output the node's text value
  595. */
  596. private InstructionList compileDefaultText(ClassGenerator classGen,
  597. MethodGenerator methodGen,
  598. InstructionHandle next) {
  599. final ConstantPoolGen cpg = classGen.getConstantPool();
  600. final InstructionList il = new InstructionList();
  601. final int chars = cpg.addInterfaceMethodref(DOM_INTF,
  602. CHARACTERS,
  603. CHARACTERS_SIG);
  604. il.append(methodGen.loadDOM());
  605. il.append(new ILOAD(_currentIndex));
  606. il.append(methodGen.loadHandler());
  607. il.append(new INVOKEINTERFACE(chars, 3));
  608. il.append(new GOTO_W(next));
  609. return il;
  610. }
  611. private InstructionList compileNamespaces(ClassGenerator classGen,
  612. MethodGenerator methodGen,
  613. boolean[] isNamespace,
  614. boolean[] isAttribute,
  615. boolean attrFlag,
  616. InstructionHandle defaultTarget) {
  617. final XSLTC xsltc = classGen.getParser().getXSLTC();
  618. final ConstantPoolGen cpg = classGen.getConstantPool();
  619. // Append switch() statement - namespace test dispatch loop
  620. final Vector namespaces = xsltc.getNamespaceIndex();
  621. final Vector names = xsltc.getNamesIndex();
  622. final int namespaceCount = namespaces.size() + 1;
  623. final int namesCount = names.size();
  624. final InstructionList il = new InstructionList();
  625. final int[] types = new int[namespaceCount];
  626. final InstructionHandle[] targets = new InstructionHandle[types.length];
  627. if (namespaceCount > 0) {
  628. boolean compiled = false;
  629. // Initialize targets for namespace() switch statement
  630. for (int i = 0; i < namespaceCount; i++) {
  631. targets[i] = defaultTarget;
  632. types[i] = i;
  633. }
  634. // Add test sequences for known namespace types
  635. for (int i = DTM.NTYPES; i < (DTM.NTYPES+namesCount); i++) {
  636. if ((isNamespace[i]) && (isAttribute[i] == attrFlag)) {
  637. String name = (String)names.elementAt(i-DTM.NTYPES);
  638. String namespace = name.substring(0,name.lastIndexOf(':'));
  639. final int type = xsltc.registerNamespace(namespace);
  640. if ((i < _testSeq.length) &&
  641. (_testSeq[i] != null)) {
  642. targets[type] =
  643. (_testSeq[i]).compile(classGen,
  644. methodGen,
  645. defaultTarget);
  646. compiled = true;
  647. }
  648. }
  649. }
  650. // Return "null" if no test sequences were compiled
  651. if (!compiled) return(null);
  652. // Append first code in applyTemplates() - get type of current node
  653. final int getNS = cpg.addInterfaceMethodref(DOM_INTF,
  654. "getNamespaceType",
  655. "(I)I");
  656. il.append(methodGen.loadDOM());
  657. il.append(new ILOAD(_currentIndex));
  658. il.append(new INVOKEINTERFACE(getNS, 2));
  659. il.append(new SWITCH(types, targets, defaultTarget));
  660. return(il);
  661. }
  662. else {
  663. return(null);
  664. }
  665. }
  666. /**
  667. * Compiles the applyTemplates() method and adds it to the translet.
  668. * This is the main dispatch method.
  669. */
  670. public void compileApplyTemplates(ClassGenerator classGen) {
  671. final XSLTC xsltc = classGen.getParser().getXSLTC();
  672. final ConstantPoolGen cpg = classGen.getConstantPool();
  673. final Vector names = xsltc.getNamesIndex();
  674. // Create the applyTemplates() method
  675. final com.sun.org.apache.bcel.internal.generic.Type[] argTypes =
  676. new com.sun.org.apache.bcel.internal.generic.Type[3];
  677. argTypes[0] = Util.getJCRefType(DOM_INTF_SIG);
  678. argTypes[1] = Util.getJCRefType(NODE_ITERATOR_SIG);
  679. argTypes[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG);
  680. final String[] argNames = new String[3];
  681. argNames[0] = DOCUMENT_PNAME;
  682. argNames[1] = ITERATOR_PNAME;
  683. argNames[2] = TRANSLET_OUTPUT_PNAME;
  684. final InstructionList mainIL = new InstructionList();
  685. final MethodGenerator methodGen =
  686. new MethodGenerator(ACC_PUBLIC | ACC_FINAL,
  687. com.sun.org.apache.bcel.internal.generic.Type.VOID,
  688. argTypes, argNames, functionName(),
  689. getClassName(), mainIL,
  690. classGen.getConstantPool());
  691. methodGen.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException");
  692. // Create a local variable to hold the current node
  693. final LocalVariableGen current;
  694. current = methodGen.addLocalVariable2("current",
  695. com.sun.org.apache.bcel.internal.generic.Type.INT,
  696. mainIL.getEnd());
  697. _currentIndex = current.getIndex();
  698. // Create the "body" instruction list that will eventually hold the
  699. // code for the entire method (other ILs will be appended).
  700. final InstructionList body = new InstructionList();
  701. body.append(NOP);
  702. // Create an instruction list that contains the default next-node
  703. // iteration
  704. final InstructionList ilLoop = new InstructionList();
  705. ilLoop.append(methodGen.loadIterator());
  706. ilLoop.append(methodGen.nextNode());
  707. ilLoop.append(DUP);
  708. ilLoop.append(new ISTORE(_currentIndex));
  709. // The body of this code can get very large - large than can be handled
  710. // by a single IFNE(body.getStart()) instruction - need workaround:
  711. final BranchHandle ifeq = ilLoop.append(new IFLT(null));
  712. final BranchHandle loop = ilLoop.append(new GOTO_W(null));
  713. ifeq.setTarget(ilLoop.append(RETURN)); // applyTemplates() ends here!
  714. final InstructionHandle ihLoop = ilLoop.getStart();
  715. // Compile default handling of elements (traverse children)
  716. InstructionList ilRecurse =
  717. compileDefaultRecursion(classGen, methodGen, ihLoop);
  718. InstructionHandle ihRecurse = ilRecurse.getStart();
  719. // Compile default handling of text/attribute nodes (output text)
  720. InstructionList ilText =
  721. compileDefaultText(classGen, methodGen, ihLoop);
  722. InstructionHandle ihText = ilText.getStart();
  723. // Distinguish attribute/element/namespace tests for further processing
  724. final int[] types = new int[DTM.NTYPES + names.size()];
  725. for (int i = 0; i < types.length; i++) {
  726. types[i] = i;
  727. }
  728. // Initialize isAttribute[] and isNamespace[] arrays
  729. final boolean[] isAttribute = new boolean[types.length];
  730. final boolean[] isNamespace = new boolean[types.length];
  731. for (int i = 0; i < names.size(); i++) {
  732. final String name = (String)names.elementAt(i);
  733. isAttribute[i + DTM.NTYPES] = isAttributeName(name);
  734. isNamespace[i + DTM.NTYPES] = isNamespaceName(name);
  735. }
  736. // Compile all templates - regardless of pattern type
  737. compileTemplates(classGen, methodGen, ihLoop);
  738. // Handle template with explicit "*" pattern
  739. final TestSeq elemTest = _testSeq[DTM.ELEMENT_NODE];
  740. InstructionHandle ihElem = ihRecurse;
  741. if (elemTest != null)
  742. ihElem = elemTest.compile(classGen, methodGen, ihRecurse);
  743. // Handle template with explicit "@*" pattern
  744. final TestSeq attrTest = _testSeq[DTM.ATTRIBUTE_NODE];
  745. InstructionHandle ihAttr = ihText;
  746. if (attrTest != null)
  747. ihAttr = attrTest.compile(classGen, methodGen, ihAttr);
  748. // Do tests for id() and key() patterns first
  749. InstructionList ilKey = null;
  750. if (_idxTestSeq != null) {
  751. loop.setTarget(_idxTestSeq.compile(classGen, methodGen, body.getStart()));
  752. ilKey = _idxTestSeq.getInstructionList();
  753. }
  754. else {
  755. loop.setTarget(body.getStart());
  756. }
  757. // If there is a match on node() we need to replace ihElem
  758. // and ihText if the priority of node() is higher
  759. if (_childNodeTestSeq != null) {
  760. // Compare priorities of node() and "*"
  761. double nodePrio = _childNodeTestSeq.getPriority();
  762. int nodePos = _childNodeTestSeq.getPosition();
  763. double elemPrio = (0 - Double.MAX_VALUE);
  764. int elemPos = Integer.MIN_VALUE;
  765. if (elemTest != null) {
  766. elemPrio = elemTest.getPriority();
  767. elemPos = elemTest.getPosition();
  768. }
  769. if (elemPrio == Double.NaN || elemPrio < nodePrio ||
  770. (elemPrio == nodePrio && elemPos < nodePos))
  771. {
  772. ihElem = _childNodeTestSeq.compile(classGen, methodGen, ihLoop);
  773. }
  774. // Compare priorities of node() and text()
  775. final TestSeq textTest = _testSeq[DTM.TEXT_NODE];
  776. double textPrio = (0 - Double.MAX_VALUE);
  777. int textPos = Integer.MIN_VALUE;
  778. if (textTest != null) {
  779. textPrio = textTest.getPriority();
  780. textPos = textTest.getPosition();
  781. }
  782. if (textPrio == Double.NaN || textPrio < nodePrio ||
  783. (textPrio == nodePrio && textPos < nodePos))
  784. {
  785. ihText = _childNodeTestSeq.compile(classGen, methodGen, ihLoop);
  786. _testSeq[DTM.TEXT_NODE] = _childNodeTestSeq;
  787. }
  788. }
  789. // Handle templates with "ns:*" pattern
  790. InstructionHandle elemNamespaceHandle = ihElem;
  791. InstructionList nsElem = compileNamespaces(classGen, methodGen,
  792. isNamespace, isAttribute,
  793. false, ihElem);
  794. if (nsElem != null) elemNamespaceHandle = nsElem.getStart();
  795. // Handle templates with "ns:@*" pattern
  796. InstructionHandle attrNamespaceHandle = ihAttr;
  797. InstructionList nsAttr = compileNamespaces(classGen, methodGen,
  798. isNamespace, isAttribute,
  799. true, ihAttr);
  800. if (nsAttr != null) attrNamespaceHandle = nsAttr.getStart();
  801. // Handle templates with "ns:elem" or "ns:@attr" pattern
  802. final InstructionHandle[] targets = new InstructionHandle[types.length];
  803. for (int i = DTM.NTYPES; i < targets.length; i++) {
  804. final TestSeq testSeq = _testSeq[i];
  805. // Jump straight to namespace tests ?
  806. if (isNamespace[i]) {
  807. if (isAttribute[i])
  808. targets[i] = attrNamespaceHandle;
  809. else
  810. targets[i] = elemNamespaceHandle;
  811. }
  812. // Test first, then jump to namespace tests
  813. else if (testSeq != null) {
  814. if (isAttribute[i])
  815. targets[i] = testSeq.compile(classGen, methodGen,
  816. attrNamespaceHandle);
  817. else
  818. targets[i] = testSeq.compile(classGen, methodGen,
  819. elemNamespaceHandle);
  820. }
  821. else {
  822. targets[i] = ihLoop;
  823. }
  824. }
  825. // Handle pattern with match on root node - default: traverse children
  826. targets[DTM.ROOT_NODE] = _rootPattern != null
  827. ? getTemplateInstructionHandle(_rootPattern.getTemplate())
  828. : ihRecurse;
  829. // Handle pattern with match on root node - default: traverse children
  830. targets[DTM.DOCUMENT_NODE] = _rootPattern != null
  831. ? getTemplateInstructionHandle(_rootPattern.getTemplate())
  832. : ihRecurse;
  833. // Handle any pattern with match on text nodes - default: output text
  834. targets[DTM.TEXT_NODE] = _testSeq[DTM.TEXT_NODE] != null
  835. ? _testSeq[DTM.TEXT_NODE].compile(classGen, methodGen, ihText)
  836. : ihText;
  837. // This DOM-type is not in use - default: process next node
  838. targets[DTM.NAMESPACE_NODE] = ihLoop;
  839. // Match unknown element in DOM - default: check for namespace match
  840. targets[DTM.ELEMENT_NODE] = elemNamespaceHandle;
  841. // Match unknown attribute in DOM - default: check for namespace match
  842. targets[DTM.ATTRIBUTE_NODE] = attrNamespaceHandle;
  843. // Match on processing instruction - default: process next node
  844. InstructionHandle ihPI = ihLoop;
  845. if (_childNodeTestSeq != null) ihPI = ihElem;
  846. if (_testSeq[DTM.PROCESSING_INSTRUCTION_NODE] != null)
  847. targets[DTM.PROCESSING_INSTRUCTION_NODE] =
  848. _testSeq[DTM.PROCESSING_INSTRUCTION_NODE].
  849. compile(classGen, methodGen, ihPI);
  850. else
  851. targets[DTM.PROCESSING_INSTRUCTION_NODE] = ihPI;
  852. // Match on comments - default: process next node
  853. InstructionHandle ihComment = ihLoop;
  854. if (_childNodeTestSeq != null) ihComment = ihElem;
  855. targets[DTM.COMMENT_NODE] = _testSeq[DTM.COMMENT_NODE] != null
  856. ? _testSeq[DTM.COMMENT_NODE].compile(classGen, methodGen, ihComment)
  857. : ihComment;
  858. // This DOM-type is not in use - default: process next node
  859. targets[DTM.CDATA_SECTION_NODE] = ihLoop;
  860. // This DOM-type is not in use - default: process next node
  861. targets[DTM.DOCUMENT_FRAGMENT_NODE] = ihLoop;
  862. // This DOM-type is not in use - default: process next node
  863. targets[DTM.DOCUMENT_TYPE_NODE] = ihLoop;
  864. // This DOM-type is not in use - default: process next node
  865. targets[DTM.ENTITY_NODE] = ihLoop;
  866. // This DOM-type is not in use - default: process next node
  867. targets[DTM.ENTITY_REFERENCE_NODE] = ihLoop;
  868. // This DOM-type is not in use - default: process next node
  869. targets[DTM.NOTATION_NODE] = ihLoop;
  870. // Now compile test sequences for various match patterns:
  871. for (int i = DTM.NTYPES; i < targets.length; i++) {
  872. final TestSeq testSeq = _testSeq[i];
  873. // Jump straight to namespace tests ?
  874. if ((testSeq == null) || (isNamespace[i])) {
  875. if (isAttribute[i])
  876. targets[i] = attrNamespaceHandle;
  877. else
  878. targets[i] = elemNamespaceHandle;
  879. }
  880. // Match on node type
  881. else {
  882. if (isAttribute[i])
  883. targets[i] = testSeq.compile(classGen, methodGen,
  884. attrNamespaceHandle);
  885. else
  886. targets[i] = testSeq.compile(classGen, methodGen,
  887. elemNamespaceHandle);
  888. }
  889. }
  890. if (ilKey != null) body.insert(ilKey);
  891. // Append first code in applyTemplates() - get type of current node
  892. final int getType = cpg.addInterfaceMethodref(DOM_INTF,
  893. "getExpandedTypeID",
  894. "(I)I");
  895. body.append(methodGen.loadDOM());
  896. body.append(new ILOAD(_currentIndex));
  897. body.append(new INVOKEINTERFACE(getType, 2));
  898. // Append switch() statement - main dispatch loop in applyTemplates()
  899. InstructionHandle disp = body.append(new SWITCH(types, targets, ihLoop));
  900. // Append all the "case:" statements
  901. appendTestSequences(body);
  902. // Append the actual template code
  903. appendTemplateCode(body);
  904. // Append NS:* node tests (if any)
  905. if (nsElem != null) body.append(nsElem);
  906. // Append NS:@* node tests (if any)
  907. if (nsAttr != null) body.append(nsAttr);
  908. // Append default action for element and root nodes
  909. body.append(ilRecurse);
  910. // Append default action for text and attribute nodes
  911. body.append(ilText);
  912. // putting together constituent instruction lists
  913. mainIL.append(new GOTO_W(ihLoop));
  914. mainIL.append(body);
  915. // fall through to ilLoop
  916. mainIL.append(ilLoop);
  917. peepHoleOptimization(methodGen);
  918. methodGen.stripAttributes(true);
  919. methodGen.setMaxLocals();
  920. methodGen.setMaxStack();
  921. methodGen.removeNOPs();
  922. classGen.addMethod(methodGen.getMethod());
  923. // Compile method(s) for <xsl:apply-imports/> for this mode
  924. if (_importLevels != null) {
  925. Enumeration levels = _importLevels.keys();
  926. while (levels.hasMoreElements()) {
  927. Integer max = (Integer)levels.nextElement();
  928. Integer min = (Integer)_importLevels.get(max);
  929. compileApplyImports(classGen, min.intValue(), max.intValue());
  930. }
  931. }
  932. }
  933. private void compileTemplateCalls(ClassGenerator classGen,
  934. MethodGenerator methodGen,
  935. InstructionHandle next, int min, int max){
  936. Enumeration templates = _neededTemplates.keys();
  937. while (templates.hasMoreElements()) {
  938. final Template template = (Template)templates.nextElement();
  939. final int prec = template.getImportPrecedence();
  940. if ((prec >= min) && (prec < max)) {
  941. if (template.hasContents()) {
  942. InstructionList til = template.compile(classGen, methodGen);
  943. til.append(new GOTO_W(next));
  944. _templateILs.put(template, til);
  945. _templateIHs.put(template, til.getStart());
  946. }
  947. else {
  948. // empty template
  949. _templateIHs.put(template, next);
  950. }
  951. }
  952. }
  953. }
  954. public void compileApplyImports(ClassGenerator classGen, int min, int max) {
  955. final XSLTC xsltc = classGen.getParser().getXSLTC();
  956. final ConstantPoolGen cpg = classGen.getConstantPool();
  957. final Vector names = xsltc.getNamesIndex();
  958. // Clear some datastructures
  959. _namedTemplates = new Hashtable();
  960. _neededTemplates = new Hashtable();
  961. _templateIHs = new Hashtable();
  962. _templateILs = new Hashtable();
  963. _patternGroups = new Vector[32];
  964. _rootPattern = null;
  965. // IMPORTANT: Save orignal & complete set of templates!!!!
  966. Vector oldTemplates = _templates;
  967. // Gather templates that are within the scope of this import
  968. _templates = new Vector();
  969. final Enumeration templates = oldTemplates.elements();
  970. while (templates.hasMoreElements()) {
  971. final Template template = (Template)templates.nextElement();
  972. final int prec = template.getImportPrecedence();
  973. if ((prec >= min) && (prec < max)) addTemplate(template);
  974. }
  975. // Process all patterns from those templates
  976. processPatterns(_keys);
  977. // Create the applyTemplates() method
  978. final com.sun.org.apache.bcel.internal.generic.Type[] argTypes =
  979. new com.sun.org.apache.bcel.internal.generic.Type[3];
  980. argTypes[0] = Util.getJCRefType(DOM_INTF_SIG);
  981. argTypes[1] = Util.getJCRefType(NODE_ITERATOR_SIG);
  982. argTypes[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG);
  983. final String[] argNames = new String[3];
  984. argNames[0] = DOCUMENT_PNAME;
  985. argNames[1] = ITERATOR_PNAME;
  986. argNames[2] = TRANSLET_OUTPUT_PNAME;
  987. final InstructionList mainIL = new InstructionList();
  988. final MethodGenerator methodGen =
  989. new MethodGenerator(ACC_PUBLIC | ACC_FINAL,
  990. com.sun.org.apache.bcel.internal.generic.Type.VOID,
  991. argTypes, argNames, functionName()+'_'+max,
  992. getClassName(), mainIL,
  993. classGen.getConstantPool());
  994. methodGen.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException");
  995. // Create the local variable to hold the current node
  996. final LocalVariableGen current;
  997. current = methodGen.addLocalVariable2("current",
  998. com.sun.org.apache.bcel.internal.generic.Type.INT,
  999. mainIL.getEnd());
  1000. _currentIndex = current.getIndex();
  1001. // Create the "body" instruction list that will eventually hold the
  1002. // code for the entire method (other ILs will be appended).
  1003. final InstructionList body = new InstructionList();
  1004. body.append(NOP);
  1005. // Create an instruction list that contains the default next-node
  1006. // iteration
  1007. final InstructionList ilLoop = new InstructionList();
  1008. ilLoop.append(methodGen.loadIterator());
  1009. ilLoop.append(methodGen.nextNode());
  1010. ilLoop.append(DUP);
  1011. ilLoop.append(new ISTORE(_currentIndex));
  1012. // The body of this code can get very large - large than can be handled
  1013. // by a single IFNE(body.getStart()) instruction - need workaround:
  1014. final BranchHandle ifeq = ilLoop.append(new IFLT(null));
  1015. final BranchHandle loop = ilLoop.append(new GOTO_W(null));
  1016. ifeq.setTarget(ilLoop.append(RETURN)); // applyTemplates() ends here!
  1017. final InstructionHandle ihLoop = ilLoop.getStart();
  1018. // Compile default handling of elements (traverse children)
  1019. InstructionList ilRecurse =
  1020. compileDefaultRecursion(classGen, methodGen, ihLoop);
  1021. InstructionHandle ihRecurse = ilRecurse.getStart();
  1022. // Compile default handling of text/attribute nodes (output text)
  1023. InstructionList ilText =
  1024. compileDefaultText(classGen, methodGen, ihLoop);
  1025. InstructionHandle ihText = ilText.getStart();
  1026. // Distinguish attribute/element/namespace tests for further processing
  1027. final int[] types = new int[DTM.NTYPES + names.size()];
  1028. for (int i = 0; i < types.length; i++) {
  1029. types[i] = i;
  1030. }
  1031. final boolean[] isAttribute = new boolean[types.length];
  1032. final boolean[] isNamespace = new boolean[types.length];
  1033. for (int i = 0; i < names.size(); i++) {
  1034. final String name = (String)names.elementAt(i);
  1035. isAttribute[i+DTM.NTYPES] = isAttributeName(name);
  1036. isNamespace[i+DTM.NTYPES] = isNamespaceName(name);
  1037. }
  1038. // Compile all templates - regardless of pattern type
  1039. compileTemplateCalls(classGen, methodGen, ihLoop, min, max);
  1040. // Handle template with explicit "*" pattern
  1041. final TestSeq elemTest = _testSeq[DTM.ELEMENT_NODE];
  1042. InstructionHandle ihElem = ihRecurse;
  1043. if (elemTest != null) {
  1044. ihElem = elemTest.compile(classGen, methodGen, ihLoop);
  1045. }
  1046. // Handle template with explicit "@*" pattern
  1047. final TestSeq attrTest = _testSeq[DTM.ATTRIBUTE_NODE];
  1048. InstructionHandle ihAttr = ihLoop;
  1049. if (attrTest != null) {
  1050. ihAttr = attrTest.compile(classGen, methodGen, ihAttr);
  1051. }
  1052. // Do tests for id() and key() patterns first
  1053. InstructionList ilKey = null;
  1054. if (_idxTestSeq != null) {
  1055. loop.setTarget(_idxTestSeq.compile(classGen, methodGen, body.getStart()));
  1056. ilKey = _idxTestSeq.getInstructionList();
  1057. }
  1058. else {
  1059. loop.setTarget(body.getStart());
  1060. }
  1061. // If there is a match on node() we need to replace ihElem
  1062. // and ihText if the priority of node() is higher
  1063. if (_childNodeTestSeq != null) {
  1064. // Compare priorities of node() and "*"
  1065. double nodePrio = _childNodeTestSeq.getPriority();
  1066. int nodePos = _childNodeTestSeq.getPosition();
  1067. double elemPrio = (0 - Double.MAX_VALUE);
  1068. int elemPos = Integer.MIN_VALUE;
  1069. if (elemTest != null) {
  1070. elemPrio = elemTest.getPriority();
  1071. elemPos = elemTest.getPosition();
  1072. }
  1073. if (elemPrio == Double.NaN || elemPrio < nodePrio ||
  1074. (elemPrio == nodePrio && elemPos < nodePos))
  1075. {
  1076. ihElem = _childNodeTestSeq.compile(classGen, methodGen, ihLoop);
  1077. }
  1078. // Compare priorities of node() and text()
  1079. final TestSeq textTest = _testSeq[DTM.TEXT_NODE];
  1080. double textPrio = (0 - Double.MAX_VALUE);
  1081. int textPos = Integer.MIN_VALUE;
  1082. if (textTest != null) {
  1083. textPrio = textTest.getPriority();
  1084. textPos = textTest.getPosition();
  1085. }
  1086. if (textPrio == Double.NaN || textPrio < nodePrio ||
  1087. (textPrio == nodePrio && textPos < nodePos))
  1088. {
  1089. ihText = _childNodeTestSeq.compile(classGen, methodGen, ihLoop);
  1090. _testSeq[DTM.TEXT_NODE] = _childNodeTestSeq;
  1091. }
  1092. }
  1093. // Handle templates with "ns:*" pattern
  1094. InstructionHandle elemNamespaceHandle = ihElem;
  1095. InstructionList nsElem = compileNamespaces(classGen, methodGen,
  1096. isNamespace, isAttribute,
  1097. false, ihElem);
  1098. if (nsElem != null) elemNamespaceHandle = nsElem.getStart();
  1099. // Handle templates with "ns:@*" pattern
  1100. InstructionList nsAttr = compileNamespaces(classGen, methodGen,
  1101. isNamespace, isAttribute,
  1102. true, ihAttr);
  1103. InstructionHandle attrNamespaceHandle = ihAttr;
  1104. if (nsAttr != null) attrNamespaceHandle = nsAttr.getStart();
  1105. // Handle templates with "ns:elem" or "ns:@attr" pattern
  1106. final InstructionHandle[] targets = new InstructionHandle[types.length];
  1107. for (int i = DTM.NTYPES; i < targets.length; i++) {
  1108. final TestSeq testSeq = _testSeq[i];
  1109. // Jump straight to namespace tests ?
  1110. if (isNamespace[i]) {
  1111. if (isAttribute[i])
  1112. targets[i] = attrNamespaceHandle;
  1113. else
  1114. targets[i] = elemNamespaceHandle;
  1115. }
  1116. // Test first, then jump to namespace tests
  1117. else if (testSeq != null) {
  1118. if (isAttribute[i])
  1119. targets[i] = testSeq.compile(classGen, methodGen,
  1120. attrNamespaceHandle);
  1121. else
  1122. targets[i] = testSeq.compile(classGen, methodGen,
  1123. elemNamespaceHandle);
  1124. }
  1125. else {
  1126. targets[i] = ihLoop;
  1127. }
  1128. }
  1129. // Handle pattern with match on root node - default: traverse children
  1130. targets[DTM.ROOT_NODE] = _rootPattern != null
  1131. ? getTemplateInstructionHandle(_rootPattern.getTemplate())
  1132. : ihRecurse;
  1133. // Handle pattern with match on root node - default: traverse children
  1134. targets[DTM.DOCUMENT_NODE] = _rootPattern != null
  1135. ? getTemplateInstructionHandle(_rootPattern.getTemplate())
  1136. : ihRecurse; // %HZ%: Was ihLoop in XSLTC_DTM branch
  1137. // Handle any pattern with match on text nodes - default: loop
  1138. targets[DTM.TEXT_NODE] = _testSeq[DTM.TEXT_NODE] != null
  1139. ? _testSeq[DTM.TEXT_NODE].compile(classGen, methodGen, ihText)
  1140. : ihText;
  1141. // This DOM-type is not in use - default: process next node
  1142. targets[DTM.NAMESPACE_NODE] = ihLoop;
  1143. // Match unknown element in DOM - default: check for namespace match
  1144. targets[DTM.ELEMENT_NODE] = elemNamespaceHandle;
  1145. // Match unknown attribute in DOM - default: check for namespace match
  1146. targets[DTM.ATTRIBUTE_NODE] = attrNamespaceHandle;
  1147. // Match on processing instruction - default: loop
  1148. InstructionHandle ihPI = ihLoop;
  1149. if (_childNodeTestSeq != null) ihPI = ihElem;
  1150. if (_testSeq[DTM.PROCESSING_INSTRUCTION_NODE] != null) {
  1151. targets[DTM.PROCESSING_INSTRUCTION_NODE] =
  1152. _testSeq[DTM.PROCESSING_INSTRUCTION_NODE].
  1153. compile(classGen, methodGen, ihPI);
  1154. }
  1155. else {
  1156. targets[DTM.PROCESSING_INSTRUCTION_NODE] = ihPI;
  1157. }
  1158. // Match on comments - default: process next node
  1159. InstructionHandle ihComment = ihLoop;
  1160. if (_childNodeTestSeq != null) ihComment = ihElem;
  1161. targets[DTM.COMMENT_NODE] = _testSeq[DTM.COMMENT_NODE] != null
  1162. ? _testSeq[DTM.COMMENT_NODE].compile(classGen, methodGen, ihComment)
  1163. : ihComment;
  1164. // This DOM-type is not in use - default: process next node
  1165. targets[DTM.CDATA_SECTION_NODE] = ihLoop;
  1166. // This DOM-type is not in use - default: process next node
  1167. targets[DTM.DOCUMENT_FRAGMENT_NODE] = ihLoop;
  1168. // This DOM-type is not in use - default: process next node
  1169. targets[DTM.DOCUMENT_TYPE_NODE] = ihLoop;
  1170. // This DOM-type is not in use - default: process next node
  1171. targets[DTM.ENTITY_NODE] = ihLoop;
  1172. // This DOM-type is not in use - default: process next node
  1173. targets[DTM.ENTITY_REFERENCE_NODE] = ihLoop;
  1174. // This DOM-type is not in use - default: process next node
  1175. targets[DTM.NOTATION_NODE] = ihLoop;
  1176. // Now compile test sequences for various match patterns:
  1177. for (int i = DTM.NTYPES; i < targets.length; i++) {
  1178. final TestSeq testSeq = _testSeq[i];
  1179. // Jump straight to namespace tests ?
  1180. if ((testSeq == null) || (isNamespace[i])) {
  1181. if (isAttribute[i])
  1182. targets[i] = attrNamespaceHandle;
  1183. else
  1184. targets[i] = elemNamespaceHandle;
  1185. }
  1186. // Match on node type
  1187. else {
  1188. if (isAttribute[i])
  1189. targets[i] = testSeq.compile(classGen, methodGen,
  1190. attrNamespaceHandle);
  1191. else
  1192. targets[i] = testSeq.compile(classGen, methodGen,
  1193. elemNamespaceHandle);
  1194. }
  1195. }
  1196. if (ilKey != null) body.insert(ilKey);
  1197. // Append first code in applyTemplates() - get type of current node
  1198. final int getType = cpg.addInterfaceMethodref(DOM_INTF,
  1199. "getExpandedTypeID",
  1200. "(I)I");
  1201. body.append(methodGen.loadDOM());
  1202. body.append(new ILOAD(_currentIndex));
  1203. body.append(new INVOKEINTERFACE(getType, 2));
  1204. // Append switch() statement - main dispatch loop in applyTemplates()
  1205. InstructionHandle disp = body.append(new SWITCH(types,targets,ihLoop));
  1206. // Append all the "case:" statements
  1207. appendTestSequences(body);
  1208. // Append the actual template code
  1209. appendTemplateCode(body);
  1210. // Append NS:* node tests (if any)
  1211. if (nsElem != null) body.append(nsElem);
  1212. // Append NS:@* node tests (if any)
  1213. if (nsAttr != null) body.append(nsAttr);
  1214. // Append default action for element and root nodes
  1215. body.append(ilRecurse);
  1216. // Append default action for text and attribute nodes
  1217. body.append(ilText);
  1218. // putting together constituent instruction lists
  1219. mainIL.append(new GOTO_W(ihLoop));
  1220. mainIL.append(body);
  1221. // fall through to ilLoop
  1222. mainIL.append(ilLoop);
  1223. peepHoleOptimization(methodGen);
  1224. methodGen.stripAttributes(true);
  1225. methodGen.setMaxLocals();
  1226. methodGen.setMaxStack();
  1227. methodGen.removeNOPs();
  1228. classGen.addMethod(methodGen.getMethod());
  1229. // Restore original (complete) set of templates for this transformation
  1230. _templates = oldTemplates;
  1231. }
  1232. /**
  1233. * Peephole optimization.
  1234. */
  1235. private void peepHoleOptimization(MethodGenerator methodGen) {
  1236. InstructionList il = methodGen.getInstructionList();
  1237. InstructionFinder find = new InstructionFinder(il);
  1238. InstructionHandle ih;
  1239. String pattern;
  1240. // LoadInstruction, POP => (removed)
  1241. pattern = "LoadInstruction POP";
  1242. for (Iterator iter = find.search(pattern); iter.hasNext();) {
  1243. InstructionHandle[] match = (InstructionHandle[]) iter.next();
  1244. try {
  1245. if (!match[0].hasTargeters() && !match[1].hasTargeters()) {
  1246. il.delete(match[0], match[1]);
  1247. }
  1248. }
  1249. catch (TargetLostException e) {
  1250. // TODO: move target down into the list
  1251. }
  1252. }
  1253. // ILOAD_N, ILOAD_N, SWAP, ISTORE_N => ILOAD_N
  1254. pattern = "ILOAD ILOAD SWAP ISTORE";
  1255. for (Iterator iter = find.search(pattern); iter.hasNext();) {
  1256. InstructionHandle[] match = (InstructionHandle[]) iter.next();
  1257. try {
  1258. com.sun.org.apache.bcel.internal.generic.ILOAD iload1 =
  1259. (com.sun.org.apache.bcel.internal.generic.ILOAD) match[0].getInstruction();
  1260. com.sun.org.apache.bcel.internal.generic.ILOAD iload2 =
  1261. (com.sun.org.apache.bcel.internal.generic.ILOAD) match[1].getInstruction();
  1262. com.sun.org.apache.bcel.internal.generic.ISTORE istore =
  1263. (com.sun.org.apache.bcel.internal.generic.ISTORE) match[3].getInstruction();
  1264. if (!match[1].hasTargeters() &&
  1265. !match[2].hasTargeters() &&
  1266. !match[3].hasTargeters() &&
  1267. iload1.getIndex() == iload2.getIndex() &&
  1268. iload2.getIndex() == istore.getIndex())
  1269. {
  1270. il.delete(match[1], match[3]);
  1271. }
  1272. }
  1273. catch (TargetLostException e) {
  1274. // TODO: move target down into the list
  1275. }
  1276. }
  1277. // LoadInstruction_N, LoadInstruction_M, SWAP => LoadInstruction_M, LoadInstruction_N
  1278. pattern = "LoadInstruction LoadInstruction SWAP";
  1279. for (Iterator iter = find.search(pattern); iter.hasNext();) {
  1280. InstructionHandle[] match = (InstructionHandle[])iter.next();
  1281. try {
  1282. if (!match[0].hasTargeters() &&
  1283. !match[1].hasTargeters() &&
  1284. !match[2].hasTargeters())
  1285. {
  1286. Instruction load_m = match[1].getInstruction();
  1287. il.insert(match[0], load_m);
  1288. il.delete(match[1], match[2]);
  1289. }
  1290. }
  1291. catch (TargetLostException e) {
  1292. // TODO: move target down into the list
  1293. }
  1294. }
  1295. // ALOAD_N ALOAD_N => ALOAD_N DUP
  1296. pattern = "ALOAD ALOAD";
  1297. for (Iterator iter = find.search(pattern); iter.hasNext();) {
  1298. InstructionHandle[] match = (InstructionHandle[])iter.next();
  1299. try {
  1300. if (!match[1].hasTargeters()) {
  1301. com.sun.org.apache.bcel.internal.generic.ALOAD aload1 =
  1302. (com.sun.org.apache.bcel.internal.generic.ALOAD) match[0].getInstruction();
  1303. com.sun.org.apache.bcel.internal.generic.ALOAD aload2 =
  1304. (com.sun.org.apache.bcel.internal.generic.ALOAD) match[1].getInstruction();
  1305. if (aload1.getIndex() == aload2.getIndex()) {
  1306. il.insert(match[1], new DUP());
  1307. il.delete(match[1]);
  1308. }
  1309. }
  1310. }
  1311. catch (TargetLostException e) {
  1312. // TODO: move target down into the list
  1313. }
  1314. }
  1315. }
  1316. public InstructionHandle getTemplateInstructionHandle(Template template) {
  1317. return (InstructionHandle)_templateIHs.get(template);
  1318. }
  1319. /**
  1320. * Auxiliary method to determine if a qname is an attribute.
  1321. */
  1322. private static boolean isAttributeName(String qname) {
  1323. final int col = qname.lastIndexOf(':') + 1;
  1324. return (qname.charAt(col) == '@');
  1325. }
  1326. /**
  1327. * Auxiliary method to determine if a qname is a namespace
  1328. * qualified "*".
  1329. */
  1330. private static boolean isNamespaceName(String qname) {
  1331. final int col = qname.lastIndexOf(':');
  1332. return (col > -1 && qname.charAt(qname.length()-1) == '*');
  1333. }
  1334. }