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    }