1/*
2 * Copyright (c) 2007, 2016, 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: Stylesheet.java,v 1.5 2005/09/28 13:48:16 pvedula Exp $
22 */
23
24package com.sun.org.apache.xalan.internal.xsltc.compiler;
25
26import com.sun.org.apache.bcel.internal.generic.ANEWARRAY;
27import com.sun.org.apache.bcel.internal.generic.BasicType;
28import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
29import com.sun.org.apache.bcel.internal.generic.FieldGen;
30import com.sun.org.apache.bcel.internal.generic.GETFIELD;
31import com.sun.org.apache.bcel.internal.generic.GETSTATIC;
32import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
33import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
34import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
35import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
36import com.sun.org.apache.bcel.internal.generic.ISTORE;
37import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
38import com.sun.org.apache.bcel.internal.generic.InstructionList;
39import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
40import com.sun.org.apache.bcel.internal.generic.NEW;
41import com.sun.org.apache.bcel.internal.generic.NEWARRAY;
42import com.sun.org.apache.bcel.internal.generic.PUSH;
43import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
44import com.sun.org.apache.bcel.internal.generic.PUTSTATIC;
45import com.sun.org.apache.bcel.internal.generic.TargetLostException;
46import com.sun.org.apache.bcel.internal.util.InstructionFinder;
47import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
48import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
49import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
50import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
51import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
52import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
53import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
54import com.sun.org.apache.xml.internal.dtm.DTM;
55import com.sun.org.apache.xml.internal.utils.SystemIDResolver;
56import java.util.HashMap;
57import java.util.Iterator;
58import java.util.List;
59import java.util.Map;
60import java.util.Properties;
61import java.util.StringTokenizer;
62import java.util.Vector;
63
64/**
65 * @author Jacek Ambroziak
66 * @author Santiago Pericas-Geertsen
67 * @author Morten Jorgensen
68 */
69public final class Stylesheet extends SyntaxTreeNode {
70
71    /**
72     * XSLT version defined in the stylesheet.
73     */
74    private String _version;
75
76    /**
77     * Internal name of this stylesheet used as a key into the symbol table.
78     */
79    private QName _name;
80
81    /**
82     * A URI that represents the system ID for this stylesheet.
83     */
84    private String _systemId;
85
86    /**
87     * A reference to the parent stylesheet or null if topmost.
88     */
89    private Stylesheet _parentStylesheet;
90
91    /**
92     * Contains global variables and parameters defined in the stylesheet.
93     */
94    private Vector _globals = new Vector();
95
96    /**
97     * Used to cache the result returned by <code>hasLocalParams()</code>.
98     */
99    private Boolean _hasLocalParams = null;
100
101    /**
102     * The name of the class being generated.
103     */
104    private String _className;
105
106    /**
107      * Contains all templates defined in this stylesheet
108      */
109    private final Vector _templates = new Vector();
110
111    /**
112     * Used to cache result of <code>getAllValidTemplates()</code>. Only
113     * set in top-level stylesheets that include/import other stylesheets.
114     */
115    private Vector _allValidTemplates = null;
116
117    /**
118     * Counter to generate unique mode suffixes.
119     */
120    private int _nextModeSerial = 1;
121
122    /**
123     * Mapping between mode names and Mode instances.
124     */
125    private final Map<String, Mode> _modes = new HashMap<>();
126
127    /**
128     * A reference to the default Mode object.
129     */
130    private Mode _defaultMode;
131
132    /**
133     * Mapping between extension URIs and their prefixes.
134     */
135    private final Map<String, String> _extensions = new HashMap<>();
136
137    /**
138     * Reference to the stylesheet from which this stylesheet was
139     * imported (if any).
140     */
141    public Stylesheet _importedFrom = null;
142
143    /**
144     * Reference to the stylesheet from which this stylesheet was
145     * included (if any).
146     */
147    public Stylesheet _includedFrom = null;
148
149    /**
150     * Array of all the stylesheets imported or included from this one.
151     */
152    private Vector _includedStylesheets = null;
153
154    /**
155     * Import precendence for this stylesheet.
156     */
157    private int _importPrecedence = 1;
158
159    /**
160     * Minimum precendence of any descendant stylesheet by inclusion or
161     * importation.
162     */
163    private int _minimumDescendantPrecedence = -1;
164
165    /**
166     * Mapping between key names and Key objects (needed by Key/IdPattern).
167     */
168    private Map<String, Key> _keys = new HashMap<>();
169
170    /**
171     * A reference to the SourceLoader set by the user (a URIResolver
172     * if the JAXP API is being used).
173     */
174    private SourceLoader _loader = null;
175
176    /**
177     * Flag indicating if format-number() is called.
178     */
179    private boolean _numberFormattingUsed = false;
180
181    /**
182     * Flag indicating if this is a simplified stylesheets. A template
183     * matching on "/" must be added in this case.
184     */
185    private boolean _simplified = false;
186
187    /**
188     * Flag indicating if multi-document support is needed.
189     */
190    private boolean _multiDocument = false;
191
192    /**
193     * Flag indicating if nodset() is called.
194     */
195    private boolean _callsNodeset = false;
196
197    /**
198     * Flag indicating if id() is called.
199     */
200    private boolean _hasIdCall = false;
201
202    /**
203     * Set to true to enable template inlining optimization.
204     * @see XSLTC#_templateInlining
205     */
206    private boolean _templateInlining = false;
207
208    /**
209     * A reference to the last xsl:output object found in the styleshet.
210     */
211    private Output  _lastOutputElement = null;
212
213    /**
214     * Output properties for this stylesheet.
215     */
216    private Properties _outputProperties = null;
217
218    /**
219     * Output method for this stylesheet (must be set to one of
220     * the constants defined below).
221     */
222    private int _outputMethod = UNKNOWN_OUTPUT;
223
224    // Output method constants
225    public static final int UNKNOWN_OUTPUT = 0;
226    public static final int XML_OUTPUT     = 1;
227    public static final int HTML_OUTPUT    = 2;
228    public static final int TEXT_OUTPUT    = 3;
229
230    /**
231     * Return the output method
232     */
233    public int getOutputMethod() {
234        return _outputMethod;
235    }
236
237    /**
238     * Check and set the output method
239     */
240    private void checkOutputMethod() {
241        if (_lastOutputElement != null) {
242            String method = _lastOutputElement.getOutputMethod();
243            if (method != null) {
244                if (method.equals("xml"))
245                    _outputMethod = XML_OUTPUT;
246                else if (method.equals("html"))
247                    _outputMethod = HTML_OUTPUT;
248                else if (method.equals("text"))
249                    _outputMethod = TEXT_OUTPUT;
250            }
251        }
252    }
253
254    public boolean getTemplateInlining() {
255        return _templateInlining;
256    }
257
258    public void setTemplateInlining(boolean flag) {
259        _templateInlining = flag;
260    }
261
262    public boolean isSimplified() {
263        return(_simplified);
264    }
265
266    public void setSimplified() {
267        _simplified = true;
268    }
269
270    public void setHasIdCall(boolean flag) {
271        _hasIdCall = flag;
272    }
273
274    public void setOutputProperty(String key, String value) {
275        if (_outputProperties == null) {
276            _outputProperties = new Properties();
277        }
278        _outputProperties.setProperty(key, value);
279    }
280
281    public void setOutputProperties(Properties props) {
282        _outputProperties = props;
283    }
284
285    public Properties getOutputProperties() {
286        return _outputProperties;
287    }
288
289    public Output getLastOutputElement() {
290        return _lastOutputElement;
291    }
292
293    public void setMultiDocument(boolean flag) {
294        _multiDocument = flag;
295    }
296
297    public boolean isMultiDocument() {
298        return _multiDocument;
299    }
300
301    public void setCallsNodeset(boolean flag) {
302        if (flag) setMultiDocument(flag);
303        _callsNodeset = flag;
304    }
305
306    public boolean callsNodeset() {
307        return _callsNodeset;
308    }
309
310    public void numberFormattingUsed() {
311        _numberFormattingUsed = true;
312        /*
313         * Fix for bug 23046, if the stylesheet is included, set the
314         * numberFormattingUsed flag to the parent stylesheet too.
315         * AbstractTranslet.addDecimalFormat() will be inlined once for the
316         * outer most stylesheet.
317         */
318        Stylesheet parent = getParentStylesheet();
319        if (null != parent) parent.numberFormattingUsed();
320    }
321
322    public void setImportPrecedence(final int precedence) {
323        // Set import precedence for this stylesheet
324        _importPrecedence = precedence;
325
326        // Set import precedence for all included stylesheets
327        final Iterator<SyntaxTreeNode> elements = elements();
328        while (elements.hasNext()) {
329            SyntaxTreeNode child = elements.next();
330            if (child instanceof Include) {
331                Stylesheet included = ((Include)child).getIncludedStylesheet();
332                if (included != null && included._includedFrom == this) {
333                    included.setImportPrecedence(precedence);
334                }
335            }
336        }
337
338        // Set import precedence for the stylesheet that imported this one
339        if (_importedFrom != null) {
340            if (_importedFrom.getImportPrecedence() < precedence) {
341                final Parser parser = getParser();
342                final int nextPrecedence = parser.getNextImportPrecedence();
343                _importedFrom.setImportPrecedence(nextPrecedence);
344            }
345        }
346        // Set import precedence for the stylesheet that included this one
347        else if (_includedFrom != null) {
348            if (_includedFrom.getImportPrecedence() != precedence)
349                _includedFrom.setImportPrecedence(precedence);
350        }
351    }
352
353    public int getImportPrecedence() {
354        return _importPrecedence;
355    }
356
357    /**
358     * Get the minimum of the precedence of this stylesheet, any stylesheet
359     * imported by this stylesheet and any include/import descendant of this
360     * stylesheet.
361     */
362    public int getMinimumDescendantPrecedence() {
363        if (_minimumDescendantPrecedence == -1) {
364            // Start with precedence of current stylesheet as a basis.
365            int min = getImportPrecedence();
366
367            // Recursively examine all imported/included stylesheets.
368            final int inclImpCount = (_includedStylesheets != null)
369                                          ? _includedStylesheets.size()
370                                          : 0;
371
372            for (int i = 0; i < inclImpCount; i++) {
373                int prec = ((Stylesheet)_includedStylesheets.elementAt(i))
374                                              .getMinimumDescendantPrecedence();
375
376                if (prec < min) {
377                    min = prec;
378                }
379            }
380
381            _minimumDescendantPrecedence = min;
382        }
383        return _minimumDescendantPrecedence;
384    }
385
386    public boolean checkForLoop(String systemId) {
387        // Return true if this stylesheet includes/imports itself
388        if (_systemId != null && _systemId.equals(systemId)) {
389            return true;
390        }
391        // Then check with any stylesheets that included/imported this one
392        if (_parentStylesheet != null)
393            return _parentStylesheet.checkForLoop(systemId);
394        // Otherwise OK
395        return false;
396    }
397
398    public void setParser(Parser parser) {
399        super.setParser(parser);
400        _name = makeStylesheetName("__stylesheet_");
401    }
402
403    public void setParentStylesheet(Stylesheet parent) {
404        _parentStylesheet = parent;
405    }
406
407    public Stylesheet getParentStylesheet() {
408        return _parentStylesheet;
409    }
410
411    public void setImportingStylesheet(Stylesheet parent) {
412        _importedFrom = parent;
413        parent.addIncludedStylesheet(this);
414    }
415
416    public void setIncludingStylesheet(Stylesheet parent) {
417        _includedFrom = parent;
418        parent.addIncludedStylesheet(this);
419    }
420
421    public void addIncludedStylesheet(Stylesheet child) {
422        if (_includedStylesheets == null) {
423            _includedStylesheets = new Vector();
424        }
425        _includedStylesheets.addElement(child);
426    }
427
428    public void setSystemId(String systemId) {
429        if (systemId != null) {
430            _systemId = SystemIDResolver.getAbsoluteURI(systemId);
431        }
432    }
433
434    public String getSystemId() {
435        return _systemId;
436    }
437
438    public void setSourceLoader(SourceLoader loader) {
439        _loader = loader;
440    }
441
442    public SourceLoader getSourceLoader() {
443        return _loader;
444    }
445
446    private QName makeStylesheetName(String prefix) {
447        return getParser().getQName(prefix+getXSLTC().nextStylesheetSerial());
448    }
449
450    /**
451     * Returns true if this stylesheet has global vars or params.
452     */
453    public boolean hasGlobals() {
454        return _globals.size() > 0;
455    }
456
457    /**
458     * Returns true if at least one template in the stylesheet has params
459     * defined. Uses the variable <code>_hasLocalParams</code> to cache the
460     * result.
461     */
462    public boolean hasLocalParams() {
463        if (_hasLocalParams == null) {
464            Vector templates = getAllValidTemplates();
465            final int n = templates.size();
466            for (int i = 0; i < n; i++) {
467                final Template template = (Template)templates.elementAt(i);
468                if (template.hasParams()) {
469                    _hasLocalParams = Boolean.TRUE;
470                    return true;
471                }
472            }
473            _hasLocalParams = Boolean.FALSE;
474            return false;
475        }
476        else {
477            return _hasLocalParams.booleanValue();
478        }
479    }
480
481    /**
482     * Adds a single prefix mapping to this syntax tree node.
483     * @param prefix Namespace prefix.
484     * @param uri Namespace URI.
485     */
486    protected void addPrefixMapping(String prefix, String uri) {
487        if (prefix.equals(EMPTYSTRING) && uri.equals(XHTML_URI)) return;
488        super.addPrefixMapping(prefix, uri);
489    }
490
491    /**
492     * Store extension URIs
493     */
494    private void extensionURI(String prefixes, SymbolTable stable) {
495        if (prefixes != null) {
496            StringTokenizer tokens = new StringTokenizer(prefixes);
497            while (tokens.hasMoreTokens()) {
498                final String prefix = tokens.nextToken();
499                final String uri = lookupNamespace(prefix);
500                if (uri != null) {
501                    _extensions.put(uri, prefix);
502                }
503            }
504        }
505    }
506
507    public boolean isExtension(String uri) {
508        return (_extensions.get(uri) != null);
509    }
510
511    public void declareExtensionPrefixes(Parser parser) {
512        final SymbolTable stable = parser.getSymbolTable();
513        final String extensionPrefixes = getAttribute("extension-element-prefixes");
514        extensionURI(extensionPrefixes, stable);
515    }
516
517    /**
518     * Parse the version and uri fields of the stylesheet and add an
519     * entry to the symbol table mapping the name <tt>__stylesheet_</tt>
520     * to an instance of this class.
521     */
522    public void parseContents(Parser parser) {
523        final SymbolTable stable = parser.getSymbolTable();
524
525        /*
526        // Make sure the XSL version set in this stylesheet
527        if ((_version == null) || (_version.equals(EMPTYSTRING))) {
528            reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR,"version");
529        }
530        // Verify that the version is 1.0 and nothing else
531        else if (!_version.equals("1.0")) {
532            reportError(this, parser, ErrorMsg.XSL_VERSION_ERR, _version);
533        }
534        */
535
536        // Add the implicit mapping of 'xml' to the XML namespace URI
537        addPrefixMapping("xml", "http://www.w3.org/XML/1998/namespace");
538
539        // Report and error if more than one stylesheet defined
540        final Stylesheet sheet = stable.addStylesheet(_name, this);
541        if (sheet != null) {
542            // Error: more that one stylesheet defined
543            ErrorMsg err = new ErrorMsg(ErrorMsg.MULTIPLE_STYLESHEET_ERR,this);
544            parser.reportError(Constants.ERROR, err);
545        }
546
547        // If this is a simplified stylesheet we must create a template that
548        // grabs the root node of the input doc ( <xsl:template match="/"/> ).
549        // This template needs the current element (the one passed to this
550        // method) as its only child, so the Template class has a special
551        // method that handles this (parseSimplified()).
552        if (_simplified) {
553            stable.excludeURI(XSLT_URI);
554            Template template = new Template();
555            template.parseSimplified(this, parser);
556        }
557        // Parse the children of this node
558        else {
559            parseOwnChildren(parser);
560        }
561    }
562
563    /**
564     * Parse all direct children of the <xsl:stylesheet/> element.
565     */
566    public final void parseOwnChildren(Parser parser) {
567        final SymbolTable stable = parser.getSymbolTable();
568        final String excludePrefixes = getAttribute("exclude-result-prefixes");
569        final String extensionPrefixes = getAttribute("extension-element-prefixes");
570
571        // Exclude XSLT uri
572        stable.pushExcludedNamespacesContext();
573        stable.excludeURI(Constants.XSLT_URI);
574        stable.excludeNamespaces(excludePrefixes);
575        stable.excludeNamespaces(extensionPrefixes);
576
577        final List<SyntaxTreeNode> contents = getContents();
578        final int count = contents.size();
579
580        // We have to scan the stylesheet element's top-level elements for
581        // variables and/or parameters before we parse the other elements
582        for (int i = 0; i < count; i++) {
583            SyntaxTreeNode child = contents.get(i);
584            if ((child instanceof VariableBase) ||
585                (child instanceof NamespaceAlias)) {
586                parser.getSymbolTable().setCurrentNode(child);
587                child.parseContents(parser);
588            }
589        }
590
591        // Now go through all the other top-level elements...
592        for (int i = 0; i < count; i++) {
593            SyntaxTreeNode child = contents.get(i);
594            if (!(child instanceof VariableBase) &&
595                !(child instanceof NamespaceAlias)) {
596                parser.getSymbolTable().setCurrentNode(child);
597                child.parseContents(parser);
598            }
599
600            // All template code should be compiled as methods if the
601            // <xsl:apply-imports/> element was ever used in this stylesheet
602            if (!_templateInlining && (child instanceof Template)) {
603                Template template = (Template)child;
604                String name = "template$dot$" + template.getPosition();
605                template.setName(parser.getQName(name));
606            }
607        }
608
609        stable.popExcludedNamespacesContext();
610    }
611
612    public void processModes() {
613        if (_defaultMode == null)
614            _defaultMode = new Mode(null, this, Constants.EMPTYSTRING);
615        _defaultMode.processPatterns(_keys);
616        _modes.values().stream().forEach((mode) -> {
617            mode.processPatterns(_keys);
618        });
619    }
620
621    private void compileModes(ClassGenerator classGen) {
622        _defaultMode.compileApplyTemplates(classGen);
623        _modes.values().stream().forEach((mode) -> {
624            mode.compileApplyTemplates(classGen);
625        });
626    }
627
628    public Mode getMode(QName modeName) {
629        if (modeName == null) {
630            if (_defaultMode == null) {
631                _defaultMode = new Mode(null, this, Constants.EMPTYSTRING);
632            }
633            return _defaultMode;
634        }
635        else {
636            Mode mode = _modes.get(modeName.getStringRep());
637            if (mode == null) {
638                final String suffix = Integer.toString(_nextModeSerial++);
639                _modes.put(modeName.getStringRep(), mode = new Mode(modeName, this, suffix));
640            }
641            return mode;
642        }
643    }
644
645    /**
646     * Type check all the children of this node.
647     */
648    public Type typeCheck(SymbolTable stable) throws TypeCheckError {
649        final int count = _globals.size();
650        for (int i = 0; i < count; i++) {
651            final VariableBase var = (VariableBase)_globals.elementAt(i);
652            var.typeCheck(stable);
653        }
654        return typeCheckContents(stable);
655    }
656
657    /**
658     * Translate the stylesheet into JVM bytecodes.
659     */
660    public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
661        translate();
662    }
663
664    private void addDOMField(ClassGenerator classGen) {
665        final FieldGen fgen = new FieldGen(ACC_PUBLIC,
666                                           Util.getJCRefType(DOM_INTF_SIG),
667                                           DOM_FIELD,
668                                           classGen.getConstantPool());
669        classGen.addField(fgen.getField());
670    }
671
672    /**
673     * Add a static field
674     */
675    private void addStaticField(ClassGenerator classGen, String type,
676                                String name)
677    {
678        final FieldGen fgen = new FieldGen(ACC_PROTECTED|ACC_STATIC,
679                                           Util.getJCRefType(type),
680                                           name,
681                                           classGen.getConstantPool());
682        classGen.addField(fgen.getField());
683
684    }
685
686    /**
687     * Translate the stylesheet into JVM bytecodes.
688     */
689    public void translate() {
690        _className = getXSLTC().getClassName();
691
692        // Define a new class by extending TRANSLET_CLASS
693        final ClassGenerator classGen =
694            new ClassGenerator(_className,
695                               TRANSLET_CLASS,
696                               Constants.EMPTYSTRING,
697                               ACC_PUBLIC | ACC_SUPER,
698                               null, this);
699
700        addDOMField(classGen);
701
702        // Compile transform() to initialize parameters, globals & output
703        // and run the transformation
704        compileTransform(classGen);
705
706        // Translate all non-template elements and filter out all templates
707        final Iterator<SyntaxTreeNode> elements = elements();
708        while (elements.hasNext()) {
709            SyntaxTreeNode element = elements.next();
710            // xsl:template
711            if (element instanceof Template) {
712                // Separate templates by modes
713                final Template template = (Template)element;
714                //_templates.addElement(template);
715                getMode(template.getModeName()).addTemplate(template);
716            }
717            // xsl:attribute-set
718            else if (element instanceof AttributeSet) {
719                ((AttributeSet)element).translate(classGen, null);
720            }
721            else if (element instanceof Output) {
722                // save the element for later to pass to compileConstructor
723                Output output = (Output)element;
724                if (output.enabled()) _lastOutputElement = output;
725            }
726            else {
727                // Global variables and parameters are handled elsewhere.
728                // Other top-level non-template elements are ignored. Literal
729                // elements outside of templates will never be output.
730            }
731        }
732
733        checkOutputMethod();
734        processModes();
735        compileModes(classGen);
736        compileStaticInitializer(classGen);
737        compileConstructor(classGen, _lastOutputElement);
738
739        if (!getParser().errorsFound()) {
740            getXSLTC().dumpClass(classGen.getJavaClass());
741        }
742    }
743
744    /**
745     * Compile the namesArray, urisArray and typesArray into
746     * the static initializer. They are read-only from the
747     * translet. All translet instances can share a single
748     * copy of this informtion.
749     */
750    private void compileStaticInitializer(ClassGenerator classGen) {
751        final ConstantPoolGen cpg = classGen.getConstantPool();
752        final InstructionList il = new InstructionList();
753
754        final MethodGenerator staticConst =
755            new MethodGenerator(ACC_PUBLIC|ACC_STATIC,
756                                com.sun.org.apache.bcel.internal.generic.Type.VOID,
757                                null, null, "<clinit>",
758                                _className, il, cpg);
759
760        addStaticField(classGen, "[" + STRING_SIG, STATIC_NAMES_ARRAY_FIELD);
761        addStaticField(classGen, "[" + STRING_SIG, STATIC_URIS_ARRAY_FIELD);
762        addStaticField(classGen, "[I", STATIC_TYPES_ARRAY_FIELD);
763        addStaticField(classGen, "[" + STRING_SIG, STATIC_NAMESPACE_ARRAY_FIELD);
764        // Create fields of type char[] that will contain literal text from
765        // the stylesheet.
766        final int charDataFieldCount = getXSLTC().getCharacterDataCount();
767        for (int i = 0; i < charDataFieldCount; i++) {
768            addStaticField(classGen, STATIC_CHAR_DATA_FIELD_SIG,
769                           STATIC_CHAR_DATA_FIELD+i);
770        }
771
772        // Put the names array into the translet - used for dom/translet mapping
773        final Vector namesIndex = getXSLTC().getNamesIndex();
774        int size = namesIndex.size();
775        String[] namesArray = new String[size];
776        String[] urisArray = new String[size];
777        int[] typesArray = new int[size];
778
779        int index;
780        for (int i = 0; i < size; i++) {
781            String encodedName = (String)namesIndex.elementAt(i);
782            if ((index = encodedName.lastIndexOf(':')) > -1) {
783                urisArray[i] = encodedName.substring(0, index);
784            }
785
786            index = index + 1;
787            if (encodedName.charAt(index) == '@') {
788                typesArray[i] = DTM.ATTRIBUTE_NODE;
789                index++;
790            } else if (encodedName.charAt(index) == '?') {
791                typesArray[i] = DTM.NAMESPACE_NODE;
792                index++;
793            } else {
794                typesArray[i] = DTM.ELEMENT_NODE;
795            }
796
797            if (index == 0) {
798                namesArray[i] = encodedName;
799            }
800            else {
801                namesArray[i] = encodedName.substring(index);
802            }
803        }
804
805        staticConst.markChunkStart();
806        il.append(new PUSH(cpg, size));
807        il.append(new ANEWARRAY(cpg.addClass(STRING)));
808        int namesArrayRef = cpg.addFieldref(_className,
809                                            STATIC_NAMES_ARRAY_FIELD,
810                                            NAMES_INDEX_SIG);
811        il.append(new PUTSTATIC(namesArrayRef));
812        staticConst.markChunkEnd();
813
814        for (int i = 0; i < size; i++) {
815            final String name = namesArray[i];
816            staticConst.markChunkStart();
817            il.append(new GETSTATIC(namesArrayRef));
818            il.append(new PUSH(cpg, i));
819            il.append(new PUSH(cpg, name));
820            il.append(AASTORE);
821            staticConst.markChunkEnd();
822        }
823
824        staticConst.markChunkStart();
825        il.append(new PUSH(cpg, size));
826        il.append(new ANEWARRAY(cpg.addClass(STRING)));
827        int urisArrayRef = cpg.addFieldref(_className,
828                                           STATIC_URIS_ARRAY_FIELD,
829                                           URIS_INDEX_SIG);
830        il.append(new PUTSTATIC(urisArrayRef));
831        staticConst.markChunkEnd();
832
833        for (int i = 0; i < size; i++) {
834            final String uri = urisArray[i];
835            staticConst.markChunkStart();
836            il.append(new GETSTATIC(urisArrayRef));
837            il.append(new PUSH(cpg, i));
838            il.append(new PUSH(cpg, uri));
839            il.append(AASTORE);
840            staticConst.markChunkEnd();
841        }
842
843        staticConst.markChunkStart();
844        il.append(new PUSH(cpg, size));
845        il.append(new NEWARRAY(BasicType.INT));
846        int typesArrayRef = cpg.addFieldref(_className,
847                                            STATIC_TYPES_ARRAY_FIELD,
848                                            TYPES_INDEX_SIG);
849        il.append(new PUTSTATIC(typesArrayRef));
850        staticConst.markChunkEnd();
851
852        for (int i = 0; i < size; i++) {
853            final int nodeType = typesArray[i];
854            staticConst.markChunkStart();
855            il.append(new GETSTATIC(typesArrayRef));
856            il.append(new PUSH(cpg, i));
857            il.append(new PUSH(cpg, nodeType));
858            il.append(IASTORE);
859        }
860
861        // Put the namespace names array into the translet
862        final Vector namespaces = getXSLTC().getNamespaceIndex();
863        staticConst.markChunkStart();
864        il.append(new PUSH(cpg, namespaces.size()));
865        il.append(new ANEWARRAY(cpg.addClass(STRING)));
866        int namespaceArrayRef = cpg.addFieldref(_className,
867                                                STATIC_NAMESPACE_ARRAY_FIELD,
868                                                NAMESPACE_INDEX_SIG);
869        il.append(new PUTSTATIC(namespaceArrayRef));
870        staticConst.markChunkEnd();
871
872        for (int i = 0; i < namespaces.size(); i++) {
873            final String ns = (String)namespaces.elementAt(i);
874            staticConst.markChunkStart();
875            il.append(new GETSTATIC(namespaceArrayRef));
876            il.append(new PUSH(cpg, i));
877            il.append(new PUSH(cpg, ns));
878            il.append(AASTORE);
879            staticConst.markChunkEnd();
880        }
881
882        // Grab all the literal text in the stylesheet and put it in a char[]
883        final int charDataCount = getXSLTC().getCharacterDataCount();
884        final int toCharArray = cpg.addMethodref(STRING, "toCharArray", "()[C");
885        for (int i = 0; i < charDataCount; i++) {
886            staticConst.markChunkStart();
887            il.append(new PUSH(cpg, getXSLTC().getCharacterData(i)));
888            il.append(new INVOKEVIRTUAL(toCharArray));
889            il.append(new PUTSTATIC(cpg.addFieldref(_className,
890                                               STATIC_CHAR_DATA_FIELD+i,
891                                               STATIC_CHAR_DATA_FIELD_SIG)));
892            staticConst.markChunkEnd();
893        }
894
895        il.append(RETURN);
896
897        classGen.addMethod(staticConst);
898
899    }
900
901    /**
902     * Compile the translet's constructor
903     */
904    private void compileConstructor(ClassGenerator classGen, Output output) {
905
906        final ConstantPoolGen cpg = classGen.getConstantPool();
907        final InstructionList il = new InstructionList();
908
909        final MethodGenerator constructor =
910            new MethodGenerator(ACC_PUBLIC,
911                                com.sun.org.apache.bcel.internal.generic.Type.VOID,
912                                null, null, "<init>",
913                                _className, il, cpg);
914
915        // Call the constructor in the AbstractTranslet superclass
916        il.append(classGen.loadTranslet());
917        il.append(new INVOKESPECIAL(cpg.addMethodref(TRANSLET_CLASS,
918                                                     "<init>", "()V")));
919
920        constructor.markChunkStart();
921        il.append(classGen.loadTranslet());
922        il.append(new GETSTATIC(cpg.addFieldref(_className,
923                                                STATIC_NAMES_ARRAY_FIELD,
924                                                NAMES_INDEX_SIG)));
925        il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
926                                               NAMES_INDEX,
927                                               NAMES_INDEX_SIG)));
928
929        il.append(classGen.loadTranslet());
930        il.append(new GETSTATIC(cpg.addFieldref(_className,
931                                                STATIC_URIS_ARRAY_FIELD,
932                                                URIS_INDEX_SIG)));
933        il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
934                                               URIS_INDEX,
935                                               URIS_INDEX_SIG)));
936        constructor.markChunkEnd();
937
938        constructor.markChunkStart();
939        il.append(classGen.loadTranslet());
940        il.append(new GETSTATIC(cpg.addFieldref(_className,
941                                                STATIC_TYPES_ARRAY_FIELD,
942                                                TYPES_INDEX_SIG)));
943        il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
944                                               TYPES_INDEX,
945                                               TYPES_INDEX_SIG)));
946        constructor.markChunkEnd();
947
948        constructor.markChunkStart();
949        il.append(classGen.loadTranslet());
950        il.append(new GETSTATIC(cpg.addFieldref(_className,
951                                                STATIC_NAMESPACE_ARRAY_FIELD,
952                                                NAMESPACE_INDEX_SIG)));
953        il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
954                                               NAMESPACE_INDEX,
955                                               NAMESPACE_INDEX_SIG)));
956        constructor.markChunkEnd();
957
958        constructor.markChunkStart();
959        il.append(classGen.loadTranslet());
960        il.append(new PUSH(cpg, AbstractTranslet.CURRENT_TRANSLET_VERSION));
961        il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
962                                               TRANSLET_VERSION_INDEX,
963                                               TRANSLET_VERSION_INDEX_SIG)));
964        constructor.markChunkEnd();
965
966        if (_hasIdCall) {
967            constructor.markChunkStart();
968            il.append(classGen.loadTranslet());
969            il.append(new PUSH(cpg, Boolean.TRUE));
970            il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
971                                                   HASIDCALL_INDEX,
972                                                   HASIDCALL_INDEX_SIG)));
973            constructor.markChunkEnd();
974        }
975
976        // Compile in code to set the output configuration from <xsl:output>
977        if (output != null) {
978            // Set all the output settings files in the translet
979            constructor.markChunkStart();
980            output.translate(classGen, constructor);
981            constructor.markChunkEnd();
982        }
983
984        // Compile default decimal formatting symbols.
985        // This is an implicit, nameless xsl:decimal-format top-level element.
986        if (_numberFormattingUsed) {
987            constructor.markChunkStart();
988            DecimalFormatting.translateDefaultDFS(classGen, constructor);
989            constructor.markChunkEnd();
990        }
991
992        il.append(RETURN);
993
994        classGen.addMethod(constructor);
995    }
996
997    /**
998     * Compile a topLevel() method into the output class. This method is
999     * called from transform() to handle all non-template top-level elements.
1000     * Returns the signature of the topLevel() method.
1001     *
1002     * Global variables/params and keys are first sorted to resolve
1003     * dependencies between them. The XSLT 1.0 spec does not allow a key
1004     * to depend on a variable. However, for compatibility with Xalan
1005     * interpretive, that type of dependency is allowed. Note also that
1006     * the buildKeys() method is still generated as it is used by the
1007     * LoadDocument class, but it no longer called from transform().
1008     */
1009    private String compileTopLevel(ClassGenerator classGen) {
1010
1011        final ConstantPoolGen cpg = classGen.getConstantPool();
1012
1013        final com.sun.org.apache.bcel.internal.generic.Type[] argTypes = {
1014            Util.getJCRefType(DOM_INTF_SIG),
1015            Util.getJCRefType(NODE_ITERATOR_SIG),
1016            Util.getJCRefType(TRANSLET_OUTPUT_SIG)
1017        };
1018
1019        final String[] argNames = {
1020            DOCUMENT_PNAME, ITERATOR_PNAME, TRANSLET_OUTPUT_PNAME
1021        };
1022
1023        final InstructionList il = new InstructionList();
1024
1025        final MethodGenerator toplevel =
1026            new MethodGenerator(ACC_PUBLIC,
1027                                com.sun.org.apache.bcel.internal.generic.Type.VOID,
1028                                argTypes, argNames,
1029                                "topLevel", _className, il,
1030                                classGen.getConstantPool());
1031
1032        toplevel.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException");
1033
1034        // Define and initialize 'current' variable with the root node
1035        final LocalVariableGen current =
1036            toplevel.addLocalVariable("current",
1037                                      com.sun.org.apache.bcel.internal.generic.Type.INT,
1038                                      null, null);
1039
1040        final int setFilter = cpg.addInterfaceMethodref(DOM_INTF,
1041                               "setFilter",
1042                               "(Lcom/sun/org/apache/xalan/internal/xsltc/StripFilter;)V");
1043
1044        final int gitr = cpg.addInterfaceMethodref(DOM_INTF,
1045                                                        "getIterator",
1046                                                        "()"+NODE_ITERATOR_SIG);
1047        il.append(toplevel.loadDOM());
1048        il.append(new INVOKEINTERFACE(gitr, 1));
1049        il.append(toplevel.nextNode());
1050        current.setStart(il.append(new ISTORE(current.getIndex())));
1051
1052        // Create a new list containing variables/params + keys
1053        Vector varDepElements = new Vector(_globals);
1054        Iterator<SyntaxTreeNode> elements = elements();
1055        while (elements.hasNext()) {
1056            SyntaxTreeNode element = elements.next();
1057            if (element instanceof Key) {
1058                varDepElements.add(element);
1059            }
1060        }
1061
1062        // Determine a partial order for the variables/params and keys
1063        varDepElements = resolveDependencies(varDepElements);
1064
1065        // Translate vars/params and keys in the right order
1066        final int count = varDepElements.size();
1067        for (int i = 0; i < count; i++) {
1068            final TopLevelElement tle = (TopLevelElement) varDepElements.elementAt(i);
1069            tle.translate(classGen, toplevel);
1070            if (tle instanceof Key) {
1071                final Key key = (Key) tle;
1072                _keys.put(key.getName(), key);
1073            }
1074        }
1075
1076        // Compile code for other top-level elements
1077        Vector whitespaceRules = new Vector();
1078        elements = elements();
1079        while (elements.hasNext()) {
1080            SyntaxTreeNode element = elements.next();
1081            // xsl:decimal-format
1082            if (element instanceof DecimalFormatting) {
1083                ((DecimalFormatting)element).translate(classGen,toplevel);
1084            }
1085            // xsl:strip/preserve-space
1086            else if (element instanceof Whitespace) {
1087                whitespaceRules.addAll(((Whitespace)element).getRules());
1088            }
1089        }
1090
1091        // Translate all whitespace strip/preserve rules
1092        if (whitespaceRules.size() > 0) {
1093            Whitespace.translateRules(whitespaceRules,classGen);
1094        }
1095
1096        if (classGen.containsMethod(STRIP_SPACE, STRIP_SPACE_PARAMS) != null) {
1097            il.append(toplevel.loadDOM());
1098            il.append(classGen.loadTranslet());
1099            il.append(new INVOKEINTERFACE(setFilter, 2));
1100        }
1101
1102        il.append(RETURN);
1103
1104        // Compute max locals + stack and add method to class
1105        classGen.addMethod(toplevel);
1106
1107        return("("+DOM_INTF_SIG+NODE_ITERATOR_SIG+TRANSLET_OUTPUT_SIG+")V");
1108    }
1109
1110    /**
1111     * This method returns a vector with variables/params and keys in the
1112     * order in which they are to be compiled for initialization. The order
1113     * is determined by analyzing the dependencies between them. The XSLT 1.0
1114     * spec does not allow a key to depend on a variable. However, for
1115     * compatibility with Xalan interpretive, that type of dependency is
1116     * allowed and, therefore, consider to determine the partial order.
1117     */
1118    private Vector resolveDependencies(Vector input) {
1119        /* DEBUG CODE - INGORE
1120        for (int i = 0; i < input.size(); i++) {
1121            final TopLevelElement e = (TopLevelElement) input.elementAt(i);
1122            System.out.println("e = " + e + " depends on:");
1123            Vector dep = e.getDependencies();
1124            for (int j = 0; j < (dep != null ? dep.size() : 0); j++) {
1125                System.out.println("\t" + dep.elementAt(j));
1126            }
1127        }
1128        System.out.println("=================================");
1129        */
1130
1131        Vector result = new Vector();
1132        while (input.size() > 0) {
1133            boolean changed = false;
1134            for (int i = 0; i < input.size(); ) {
1135                final TopLevelElement vde = (TopLevelElement) input.elementAt(i);
1136                final Vector dep = vde.getDependencies();
1137                if (dep == null || result.containsAll(dep)) {
1138                    result.addElement(vde);
1139                    input.remove(i);
1140                    changed = true;
1141                }
1142                else {
1143                    i++;
1144                }
1145            }
1146
1147            // If nothing was changed in this pass then we have a circular ref
1148            if (!changed) {
1149                ErrorMsg err = new ErrorMsg(ErrorMsg.CIRCULAR_VARIABLE_ERR,
1150                                            input.toString(), this);
1151                getParser().reportError(Constants.ERROR, err);
1152                return(result);
1153            }
1154        }
1155
1156        /* DEBUG CODE - INGORE
1157        System.out.println("=================================");
1158        for (int i = 0; i < result.size(); i++) {
1159            final TopLevelElement e = (TopLevelElement) result.elementAt(i);
1160            System.out.println("e = " + e);
1161        }
1162        */
1163
1164        return result;
1165    }
1166
1167    /**
1168     * Compile a buildKeys() method into the output class. Note that keys
1169     * for the input document are created in topLevel(), not in this method.
1170     * However, we still need this method to create keys for documents loaded
1171     * via the XPath document() function.
1172     */
1173    private String compileBuildKeys(ClassGenerator classGen) {
1174        final ConstantPoolGen cpg = classGen.getConstantPool();
1175
1176        final com.sun.org.apache.bcel.internal.generic.Type[] argTypes = {
1177            Util.getJCRefType(DOM_INTF_SIG),
1178            Util.getJCRefType(NODE_ITERATOR_SIG),
1179            Util.getJCRefType(TRANSLET_OUTPUT_SIG),
1180            com.sun.org.apache.bcel.internal.generic.Type.INT
1181        };
1182
1183        final String[] argNames = {
1184            DOCUMENT_PNAME, ITERATOR_PNAME, TRANSLET_OUTPUT_PNAME, "current"
1185        };
1186
1187        final InstructionList il = new InstructionList();
1188
1189        final MethodGenerator buildKeys =
1190            new MethodGenerator(ACC_PUBLIC,
1191                                com.sun.org.apache.bcel.internal.generic.Type.VOID,
1192                                argTypes, argNames,
1193                                "buildKeys", _className, il,
1194                                classGen.getConstantPool());
1195
1196        buildKeys.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException");
1197
1198        final Iterator<SyntaxTreeNode> elements = elements();
1199        while (elements.hasNext()) {
1200            // xsl:key
1201            final SyntaxTreeNode element = elements.next();
1202            if (element instanceof Key) {
1203                final Key key = (Key)element;
1204                key.translate(classGen, buildKeys);
1205                _keys.put(key.getName(),key);
1206            }
1207        }
1208
1209        il.append(RETURN);
1210
1211        // Compute max locals + stack and add method to class
1212        buildKeys.stripAttributes(true);
1213        buildKeys.setMaxLocals();
1214        buildKeys.setMaxStack();
1215        buildKeys.removeNOPs();
1216
1217        classGen.addMethod(buildKeys.getMethod());
1218
1219        return("("+DOM_INTF_SIG+NODE_ITERATOR_SIG+TRANSLET_OUTPUT_SIG+"I)V");
1220    }
1221
1222    /**
1223     * Compile transform() into the output class. This method is used to
1224     * initialize global variables and global parameters. The current node
1225     * is set to be the document's root node.
1226     */
1227    private void compileTransform(ClassGenerator classGen) {
1228        final ConstantPoolGen cpg = classGen.getConstantPool();
1229
1230        /*
1231         * Define the the method transform with the following signature:
1232         * void transform(DOM, NodeIterator, HandlerBase)
1233         */
1234        final com.sun.org.apache.bcel.internal.generic.Type[] argTypes =
1235            new com.sun.org.apache.bcel.internal.generic.Type[3];
1236        argTypes[0] = Util.getJCRefType(DOM_INTF_SIG);
1237        argTypes[1] = Util.getJCRefType(NODE_ITERATOR_SIG);
1238        argTypes[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG);
1239
1240        final String[] argNames = new String[3];
1241        argNames[0] = DOCUMENT_PNAME;
1242        argNames[1] = ITERATOR_PNAME;
1243        argNames[2] = TRANSLET_OUTPUT_PNAME;
1244
1245        final InstructionList il = new InstructionList();
1246        final MethodGenerator transf =
1247            new MethodGenerator(ACC_PUBLIC,
1248                                com.sun.org.apache.bcel.internal.generic.Type.VOID,
1249                                argTypes, argNames,
1250                                "transform",
1251                                _className,
1252                                il,
1253                                classGen.getConstantPool());
1254        transf.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException");
1255
1256        // call resetPrefixIndex at the beginning of transform
1257        final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "resetPrefixIndex", "()V");
1258        il.append(new INVOKESTATIC(check));
1259
1260        // Define and initialize current with the root node
1261        final LocalVariableGen current =
1262            transf.addLocalVariable("current",
1263                                    com.sun.org.apache.bcel.internal.generic.Type.INT,
1264                                    null, null);
1265        final String applyTemplatesSig = classGen.getApplyTemplatesSig();
1266        final int applyTemplates = cpg.addMethodref(getClassName(),
1267                                                    "applyTemplates",
1268                                                    applyTemplatesSig);
1269        final int domField = cpg.addFieldref(getClassName(),
1270                                             DOM_FIELD,
1271                                             DOM_INTF_SIG);
1272
1273        // push translet for PUTFIELD
1274        il.append(classGen.loadTranslet());
1275        // prepare appropriate DOM implementation
1276
1277        if (isMultiDocument()) {
1278            il.append(new NEW(cpg.addClass(MULTI_DOM_CLASS)));
1279            il.append(DUP);
1280        }
1281
1282        il.append(classGen.loadTranslet());
1283        il.append(transf.loadDOM());
1284        il.append(new INVOKEVIRTUAL(cpg.addMethodref(TRANSLET_CLASS,
1285                                                     "makeDOMAdapter",
1286                                                     "("+DOM_INTF_SIG+")"+
1287                                                     DOM_ADAPTER_SIG)));
1288        // DOMAdapter is on the stack
1289
1290        if (isMultiDocument()) {
1291            final int init = cpg.addMethodref(MULTI_DOM_CLASS,
1292                                              "<init>",
1293                                              "("+DOM_INTF_SIG+")V");
1294            il.append(new INVOKESPECIAL(init));
1295            // MultiDOM is on the stack
1296        }
1297
1298        //store to _dom variable
1299        il.append(new PUTFIELD(domField));
1300
1301        // continue with globals initialization
1302        final int gitr = cpg.addInterfaceMethodref(DOM_INTF,
1303                                                        "getIterator",
1304                                                        "()"+NODE_ITERATOR_SIG);
1305        il.append(transf.loadDOM());
1306        il.append(new INVOKEINTERFACE(gitr, 1));
1307        il.append(transf.nextNode());
1308        current.setStart(il.append(new ISTORE(current.getIndex())));
1309
1310        // Transfer the output settings to the output post-processor
1311        il.append(classGen.loadTranslet());
1312        il.append(transf.loadHandler());
1313        final int index = cpg.addMethodref(TRANSLET_CLASS,
1314                                           "transferOutputSettings",
1315                                           "("+OUTPUT_HANDLER_SIG+")V");
1316        il.append(new INVOKEVIRTUAL(index));
1317
1318        /*
1319         * Compile buildKeys() method. Note that this method is not
1320         * invoked here as keys for the input document are now created
1321         * in topLevel(). However, this method is still needed by the
1322         * LoadDocument class.
1323         */
1324        final String keySig = compileBuildKeys(classGen);
1325        final int keyIdx = cpg.addMethodref(getClassName(),
1326                                               "buildKeys", keySig);
1327
1328        // Look for top-level elements that need handling
1329        final Iterator<SyntaxTreeNode> toplevel = elements();
1330        if (_globals.size() > 0 || toplevel.hasNext()) {
1331            // Compile method for handling top-level elements
1332            final String topLevelSig = compileTopLevel(classGen);
1333            // Get a reference to that method
1334            final int topLevelIdx = cpg.addMethodref(getClassName(),
1335                                                     "topLevel",
1336                                                     topLevelSig);
1337            // Push all parameters on the stack and call topLevel()
1338            il.append(classGen.loadTranslet()); // The 'this' pointer
1339            il.append(classGen.loadTranslet());
1340            il.append(new GETFIELD(domField));  // The DOM reference
1341            il.append(transf.loadIterator());
1342            il.append(transf.loadHandler());    // The output handler
1343            il.append(new INVOKEVIRTUAL(topLevelIdx));
1344        }
1345
1346        // start document
1347        il.append(transf.loadHandler());
1348        il.append(transf.startDocument());
1349
1350        // push first arg for applyTemplates
1351        il.append(classGen.loadTranslet());
1352        // push translet for GETFIELD to get DOM arg
1353        il.append(classGen.loadTranslet());
1354        il.append(new GETFIELD(domField));
1355        // push remaining 2 args
1356        il.append(transf.loadIterator());
1357        il.append(transf.loadHandler());
1358        il.append(new INVOKEVIRTUAL(applyTemplates));
1359        // endDocument
1360        il.append(transf.loadHandler());
1361        il.append(transf.endDocument());
1362
1363        il.append(RETURN);
1364
1365        // Compute max locals + stack and add method to class
1366        classGen.addMethod(transf);
1367
1368    }
1369
1370    /**
1371     * Peephole optimization: Remove sequences of [ALOAD, POP].
1372     */
1373    private void peepHoleOptimization(MethodGenerator methodGen) {
1374        final String pattern = "`aload'`pop'`instruction'";
1375        final InstructionList il = methodGen.getInstructionList();
1376        final InstructionFinder find = new InstructionFinder(il);
1377        for(Iterator iter=find.search(pattern); iter.hasNext(); ) {
1378            InstructionHandle[] match = (InstructionHandle[])iter.next();
1379            try {
1380                il.delete(match[0], match[1]);
1381            }
1382            catch (TargetLostException e) {
1383                // TODO: move target down into the list
1384            }
1385        }
1386    }
1387
1388    public int addParam(Param param) {
1389        _globals.addElement(param);
1390        return _globals.size() - 1;
1391    }
1392
1393    public int addVariable(Variable global) {
1394        _globals.addElement(global);
1395        return _globals.size() - 1;
1396    }
1397
1398    public void display(int indent) {
1399        indent(indent);
1400        Util.println("Stylesheet");
1401        displayContents(indent + IndentIncrement);
1402    }
1403
1404    // do we need this wrapper ?????
1405    public String getNamespace(String prefix) {
1406        return lookupNamespace(prefix);
1407    }
1408
1409    public String getClassName() {
1410        return _className;
1411    }
1412
1413    public Vector getTemplates() {
1414        return _templates;
1415    }
1416
1417    public Vector getAllValidTemplates() {
1418        // Return templates if no imported/included stylesheets
1419        if (_includedStylesheets == null) {
1420            return _templates;
1421        }
1422
1423        // Is returned value cached?
1424        if (_allValidTemplates == null) {
1425           Vector templates = new Vector();
1426           templates.addAll(_templates);
1427            int size = _includedStylesheets.size();
1428            for (int i = 0; i < size; i++) {
1429                Stylesheet included =(Stylesheet)_includedStylesheets.elementAt(i);
1430                templates.addAll(included.getAllValidTemplates());
1431            }
1432            //templates.addAll(_templates);
1433
1434            // Cache results in top-level stylesheet only
1435            if (_parentStylesheet != null) {
1436                return templates;
1437            }
1438            _allValidTemplates = templates;
1439         }
1440
1441        return _allValidTemplates;
1442    }
1443
1444    protected void addTemplate(Template template) {
1445        _templates.addElement(template);
1446    }
1447}
1448