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