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