1 /*
2  * Copyright (c) 2007, 2017, 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 
24 package com.sun.org.apache.xalan.internal.xsltc.compiler;
25 
26 import com.sun.org.apache.bcel.internal.generic.ANEWARRAY;
27 import com.sun.org.apache.bcel.internal.generic.BasicType;
28 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
29 import com.sun.org.apache.bcel.internal.generic.FieldGen;
30 import com.sun.org.apache.bcel.internal.generic.GETFIELD;
31 import com.sun.org.apache.bcel.internal.generic.GETSTATIC;
32 import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
33 import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
34 import com.sun.org.apache.bcel.internal.generic.INVOKESTATIC;
35 import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
36 import com.sun.org.apache.bcel.internal.generic.ISTORE;
37 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
38 import com.sun.org.apache.bcel.internal.generic.InstructionList;
39 import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
40 import com.sun.org.apache.bcel.internal.generic.NEW;
41 import com.sun.org.apache.bcel.internal.generic.NEWARRAY;
42 import com.sun.org.apache.bcel.internal.generic.PUSH;
43 import com.sun.org.apache.bcel.internal.generic.PUTFIELD;
44 import com.sun.org.apache.bcel.internal.generic.PUTSTATIC;
45 import com.sun.org.apache.bcel.internal.generic.TargetLostException;
46 import com.sun.org.apache.bcel.internal.util.InstructionFinder;
47 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
48 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
49 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
50 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
51 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
52 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
53 import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
54 import com.sun.org.apache.xml.internal.dtm.DTM;
55 import com.sun.org.apache.xml.internal.utils.SystemIDResolver;
56 import java.util.ArrayList;
57 import java.util.HashMap;
58 import java.util.Iterator;
59 import java.util.List;
60 import java.util.Map;
61 import java.util.Properties;
62 import java.util.StringTokenizer;
63 
64 /**
65  * @author Jacek Ambroziak
66  * @author Santiago Pericas-Geertsen
67  * @author Morten Jorgensen
68  * @LastModified: Oct 2017
69  */
70 public final class Stylesheet extends SyntaxTreeNode {
71 
72     /**
73      * XSLT version defined in the stylesheet.
74      */
75     private String _version;
76 
77     /**
78      * Internal name of this stylesheet used as a key into the symbol table.
79      */
80     private QName _name;
81 
82     /**
83      * A URI that represents the system ID for this stylesheet.
84      */
85     private String _systemId;
86 
87     /**
88      * A reference to the parent stylesheet or null if topmost.
89      */
90     private Stylesheet _parentStylesheet;
91 
92     /**
93      * Contains global variables and parameters defined in the stylesheet.
94      */
95     private List<VariableBase> _globals = new ArrayList<>();
96 
97     /**
98      * Used to cache the result returned by <code>hasLocalParams()</code>.
99      */
100     private Boolean _hasLocalParams = null;
101 
102     /**
103      * The name of the class being generated.
104      */
105     private String _className;
106 
107     /**
108       * Contains all templates defined in this stylesheet
109       */
110     private final List<Template> _templates = new ArrayList<>();
111 
112     /**
113      * Used to cache result of <code>getAllValidTemplates()</code>. Only
114      * set in top-level stylesheets that include/import other stylesheets.
115      */
116     private List<Template> _allValidTemplates = null;
117 
118     /**
119      * Counter to generate unique mode suffixes.
120      */
121     private int _nextModeSerial = 1;
122 
123     /**
124      * Mapping between mode names and Mode instances.
125      */
126     private final Map<String, Mode> _modes = new HashMap<>();
127 
128     /**
129      * A reference to the default Mode object.
130      */
131     private Mode _defaultMode;
132 
133     /**
134      * Mapping between extension URIs and their prefixes.
135      */
136     private final Map<String, String> _extensions = new HashMap<>();
137 
138     /**
139      * Reference to the stylesheet from which this stylesheet was
140      * imported (if any).
141      */
142     public Stylesheet _importedFrom = null;
143 
144     /**
145      * Reference to the stylesheet from which this stylesheet was
146      * included (if any).
147      */
148     public Stylesheet _includedFrom = null;
149 
150     /**
151      * Array of all the stylesheets imported or included from this one.
152      */
153     private List<Stylesheet> _includedStylesheets = null;
154 
155     /**
156      * Import precendence for this stylesheet.
157      */
158     private int _importPrecedence = 1;
159 
160     /**
161      * Minimum precendence of any descendant stylesheet by inclusion or
162      * importation.
163      */
164     private int _minimumDescendantPrecedence = -1;
165 
166     /**
167      * Mapping between key names and Key objects (needed by Key/IdPattern).
168      */
169     private Map<String, Key> _keys = new HashMap<>();
170 
171     /**
172      * A reference to the SourceLoader set by the user (a URIResolver
173      * if the JAXP API is being used).
174      */
175     private SourceLoader _loader = null;
176 
177     /**
178      * Flag indicating if format-number() is called.
179      */
180     private boolean _numberFormattingUsed = false;
181 
182     /**
183      * Flag indicating if this is a simplified stylesheets. A template
184      * matching on "/" must be added in this case.
185      */
186     private boolean _simplified = false;
187 
188     /**
189      * Flag indicating if multi-document support is needed.
190      */
191     private boolean _multiDocument = false;
192 
193     /**
194      * Flag indicating if nodset() is called.
195      */
196     private boolean _callsNodeset = false;
197 
198     /**
199      * Flag indicating if id() is called.
200      */
201     private boolean _hasIdCall = false;
202 
203     /**
204      * Set to true to enable template inlining optimization.
205      * @see XSLTC#_templateInlining
206      */
207     private boolean _templateInlining = false;
208 
209     /**
210      * A reference to the last xsl:output object found in the styleshet.
211      */
212     private Output  _lastOutputElement = null;
213 
214     /**
215      * Output properties for this stylesheet.
216      */
217     private Properties _outputProperties = null;
218 
219     /**
220      * Output method for this stylesheet (must be set to one of
221      * the constants defined below).
222      */
223     private int _outputMethod = UNKNOWN_OUTPUT;
224 
225     // Output method constants
226     public static final int UNKNOWN_OUTPUT = 0;
227     public static final int XML_OUTPUT     = 1;
228     public static final int HTML_OUTPUT    = 2;
229     public static final int TEXT_OUTPUT    = 3;
230 
231     /**
232      * Return the output method
233      */
getOutputMethod()234     public int getOutputMethod() {
235         return _outputMethod;
236     }
237 
238     /**
239      * Check and set the output method
240      */
checkOutputMethod()241     private void checkOutputMethod() {
242         if (_lastOutputElement != null) {
243             String method = _lastOutputElement.getOutputMethod();
244             if (method != null) {
245                 if (method.equals("xml"))
246                     _outputMethod = XML_OUTPUT;
247                 else if (method.equals("html"))
248                     _outputMethod = HTML_OUTPUT;
249                 else if (method.equals("text"))
250                     _outputMethod = TEXT_OUTPUT;
251             }
252         }
253     }
254 
getTemplateInlining()255     public boolean getTemplateInlining() {
256         return _templateInlining;
257     }
258 
setTemplateInlining(boolean flag)259     public void setTemplateInlining(boolean flag) {
260         _templateInlining = flag;
261     }
262 
isSimplified()263     public boolean isSimplified() {
264         return(_simplified);
265     }
266 
setSimplified()267     public void setSimplified() {
268         _simplified = true;
269     }
270 
setHasIdCall(boolean flag)271     public void setHasIdCall(boolean flag) {
272         _hasIdCall = flag;
273     }
274 
setOutputProperty(String key, String value)275     public void setOutputProperty(String key, String value) {
276         if (_outputProperties == null) {
277             _outputProperties = new Properties();
278         }
279         _outputProperties.setProperty(key, value);
280     }
281 
setOutputProperties(Properties props)282     public void setOutputProperties(Properties props) {
283         _outputProperties = props;
284     }
285 
getOutputProperties()286     public Properties getOutputProperties() {
287         return _outputProperties;
288     }
289 
getLastOutputElement()290     public Output getLastOutputElement() {
291         return _lastOutputElement;
292     }
293 
setMultiDocument(boolean flag)294     public void setMultiDocument(boolean flag) {
295         _multiDocument = flag;
296     }
297 
isMultiDocument()298     public boolean isMultiDocument() {
299         return _multiDocument;
300     }
301 
setCallsNodeset(boolean flag)302     public void setCallsNodeset(boolean flag) {
303         if (flag) setMultiDocument(flag);
304         _callsNodeset = flag;
305     }
306 
callsNodeset()307     public boolean callsNodeset() {
308         return _callsNodeset;
309     }
310 
numberFormattingUsed()311     public void numberFormattingUsed() {
312         _numberFormattingUsed = true;
313         /*
314          * Fix for bug 23046, if the stylesheet is included, set the
315          * numberFormattingUsed flag to the parent stylesheet too.
316          * AbstractTranslet.addDecimalFormat() will be inlined once for the
317          * outer most stylesheet.
318          */
319         Stylesheet parent = getParentStylesheet();
320         if (null != parent) parent.numberFormattingUsed();
321     }
322 
setImportPrecedence(final int precedence)323     public void setImportPrecedence(final int precedence) {
324         // Set import precedence for this stylesheet
325         _importPrecedence = precedence;
326 
327         // Set import precedence for all included stylesheets
328         final Iterator<SyntaxTreeNode> elements = elements();
329         while (elements.hasNext()) {
330             SyntaxTreeNode child = elements.next();
331             if (child instanceof Include) {
332                 Stylesheet included = ((Include)child).getIncludedStylesheet();
333                 if (included != null && included._includedFrom == this) {
334                     included.setImportPrecedence(precedence);
335                 }
336             }
337         }
338 
339         // Set import precedence for the stylesheet that imported this one
340         if (_importedFrom != null) {
341             if (_importedFrom.getImportPrecedence() < precedence) {
342                 final Parser parser = getParser();
343                 final int nextPrecedence = parser.getNextImportPrecedence();
344                 _importedFrom.setImportPrecedence(nextPrecedence);
345             }
346         }
347         // Set import precedence for the stylesheet that included this one
348         else if (_includedFrom != null) {
349             if (_includedFrom.getImportPrecedence() != precedence)
350                 _includedFrom.setImportPrecedence(precedence);
351         }
352     }
353 
getImportPrecedence()354     public int getImportPrecedence() {
355         return _importPrecedence;
356     }
357 
358     /**
359      * Get the minimum of the precedence of this stylesheet, any stylesheet
360      * imported by this stylesheet and any include/import descendant of this
361      * stylesheet.
362      */
getMinimumDescendantPrecedence()363     public int getMinimumDescendantPrecedence() {
364         if (_minimumDescendantPrecedence == -1) {
365             // Start with precedence of current stylesheet as a basis.
366             int min = getImportPrecedence();
367 
368             // Recursively examine all imported/included stylesheets.
369             final int inclImpCount = (_includedStylesheets != null)
370                                           ? _includedStylesheets.size()
371                                           : 0;
372 
373             for (int i = 0; i < inclImpCount; i++) {
374                 int prec = (_includedStylesheets.get(i)).getMinimumDescendantPrecedence();
375 
376                 if (prec < min) {
377                     min = prec;
378                 }
379             }
380 
381             _minimumDescendantPrecedence = min;
382         }
383         return _minimumDescendantPrecedence;
384     }
385 
checkForLoop(String systemId)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 
setParser(Parser parser)398     public void setParser(Parser parser) {
399         super.setParser(parser);
400         _name = makeStylesheetName("__stylesheet_");
401     }
402 
setParentStylesheet(Stylesheet parent)403     public void setParentStylesheet(Stylesheet parent) {
404         _parentStylesheet = parent;
405     }
406 
getParentStylesheet()407     public Stylesheet getParentStylesheet() {
408         return _parentStylesheet;
409     }
410 
setImportingStylesheet(Stylesheet parent)411     public void setImportingStylesheet(Stylesheet parent) {
412         _importedFrom = parent;
413         parent.addIncludedStylesheet(this);
414     }
415 
setIncludingStylesheet(Stylesheet parent)416     public void setIncludingStylesheet(Stylesheet parent) {
417         _includedFrom = parent;
418         parent.addIncludedStylesheet(this);
419     }
420 
addIncludedStylesheet(Stylesheet child)421     public void addIncludedStylesheet(Stylesheet child) {
422         if (_includedStylesheets == null) {
423             _includedStylesheets = new ArrayList<>();
424         }
425         _includedStylesheets.add(child);
426     }
427 
setSystemId(String systemId)428     public void setSystemId(String systemId) {
429         if (systemId != null) {
430             _systemId = SystemIDResolver.getAbsoluteURI(systemId);
431         }
432     }
433 
getSystemId()434     public String getSystemId() {
435         return _systemId;
436     }
437 
setSourceLoader(SourceLoader loader)438     public void setSourceLoader(SourceLoader loader) {
439         _loader = loader;
440     }
441 
getSourceLoader()442     public SourceLoader getSourceLoader() {
443         return _loader;
444     }
445 
makeStylesheetName(String prefix)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      */
hasGlobals()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      */
hasLocalParams()462     public boolean hasLocalParams() {
463         if (_hasLocalParams == null) {
464            List<Template> templates = getAllValidTemplates();
465             final int n = templates.size();
466             for (int i = 0; i < n; i++) {
467                 final Template template = templates.get(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      */
addPrefixMapping(String prefix, String uri)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      */
extensionURI(String prefixes, SymbolTable stable)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 
isExtension(String uri)507     public boolean isExtension(String uri) {
508         return (_extensions.get(uri) != null);
509     }
510 
declareExtensionPrefixes(Parser parser)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      */
parseContents(Parser parser)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      */
parseOwnChildren(Parser parser)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 
processModes()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 
compileModes(ClassGenerator classGen)621     private void compileModes(ClassGenerator classGen) {
622         _defaultMode.compileApplyTemplates(classGen);
623         _modes.values().stream().forEach((mode) -> {
624             mode.compileApplyTemplates(classGen);
625         });
626     }
627 
getMode(QName modeName)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      */
typeCheck(SymbolTable stable)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 = _globals.get(i);
652             var.typeCheck(stable);
653         }
654         return typeCheckContents(stable);
655     }
656 
657     /**
658      * Translate the stylesheet into JVM bytecodes.
659      */
translate(ClassGenerator classGen, MethodGenerator methodGen)660     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
661         translate();
662     }
663 
addDOMField(ClassGenerator classGen)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      */
addStaticField(ClassGenerator classGen, String type, String name)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      */
translate()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.add(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      */
compileStaticInitializer(ClassGenerator classGen)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 List<String> 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 = namesIndex.get(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 List<String> 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 = namespaces.get(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      */
compileConstructor(ClassGenerator classGen, Output output)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      */
compileTopLevel(ClassGenerator classGen)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         List<SyntaxTreeNode> varDepElements = new ArrayList<>(_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.get(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        List<Whitespace.WhitespaceRule> whitespaceRules = new ArrayList<>();
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      */
resolveDependencies(List<SyntaxTreeNode> input)1118     private List<SyntaxTreeNode> resolveDependencies(List<SyntaxTreeNode> input) {
1119         List<SyntaxTreeNode> result = new ArrayList<>();
1120         while (input.size() > 0) {
1121             boolean changed = false;
1122             for (int i = 0; i < input.size(); ) {
1123                 final TopLevelElement vde = (TopLevelElement) input.get(i);
1124                 final List<SyntaxTreeNode> dep = vde.getDependencies();
1125                 if (dep == null || result.containsAll(dep)) {
1126                     result.add(vde);
1127                     input.remove(i);
1128                     changed = true;
1129                 }
1130                 else {
1131                     i++;
1132                 }
1133             }
1134 
1135             // If nothing was changed in this pass then we have a circular ref
1136             if (!changed) {
1137                 ErrorMsg err = new ErrorMsg(ErrorMsg.CIRCULAR_VARIABLE_ERR,
1138                                             input.toString(), this);
1139                 getParser().reportError(Constants.ERROR, err);
1140                 return(result);
1141             }
1142         }
1143 
1144         return result;
1145     }
1146 
1147     /**
1148      * Compile a buildKeys() method into the output class. Note that keys
1149      * for the input document are created in topLevel(), not in this method.
1150      * However, we still need this method to create keys for documents loaded
1151      * via the XPath document() function.
1152      */
compileBuildKeys(ClassGenerator classGen)1153     private String compileBuildKeys(ClassGenerator classGen) {
1154         final ConstantPoolGen cpg = classGen.getConstantPool();
1155 
1156         final com.sun.org.apache.bcel.internal.generic.Type[] argTypes = {
1157             Util.getJCRefType(DOM_INTF_SIG),
1158             Util.getJCRefType(NODE_ITERATOR_SIG),
1159             Util.getJCRefType(TRANSLET_OUTPUT_SIG),
1160             com.sun.org.apache.bcel.internal.generic.Type.INT
1161         };
1162 
1163         final String[] argNames = {
1164             DOCUMENT_PNAME, ITERATOR_PNAME, TRANSLET_OUTPUT_PNAME, "current"
1165         };
1166 
1167         final InstructionList il = new InstructionList();
1168 
1169         final MethodGenerator buildKeys =
1170             new MethodGenerator(ACC_PUBLIC,
1171                                 com.sun.org.apache.bcel.internal.generic.Type.VOID,
1172                                 argTypes, argNames,
1173                                 "buildKeys", _className, il,
1174                                 classGen.getConstantPool());
1175 
1176         buildKeys.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException");
1177 
1178         final Iterator<SyntaxTreeNode> elements = elements();
1179         while (elements.hasNext()) {
1180             // xsl:key
1181             final SyntaxTreeNode element = elements.next();
1182             if (element instanceof Key) {
1183                 final Key key = (Key)element;
1184                 key.translate(classGen, buildKeys);
1185                 _keys.put(key.getName(),key);
1186             }
1187         }
1188 
1189         il.append(RETURN);
1190 
1191         // Compute max locals + stack and add method to class
1192         buildKeys.stripAttributes(true);
1193         buildKeys.setMaxLocals();
1194         buildKeys.setMaxStack();
1195         buildKeys.removeNOPs();
1196 
1197         classGen.addMethod(buildKeys.getMethod());
1198 
1199         return("("+DOM_INTF_SIG+NODE_ITERATOR_SIG+TRANSLET_OUTPUT_SIG+"I)V");
1200     }
1201 
1202     /**
1203      * Compile transform() into the output class. This method is used to
1204      * initialize global variables and global parameters. The current node
1205      * is set to be the document's root node.
1206      */
compileTransform(ClassGenerator classGen)1207     private void compileTransform(ClassGenerator classGen) {
1208         final ConstantPoolGen cpg = classGen.getConstantPool();
1209 
1210         /*
1211          * Define the the method transform with the following signature:
1212          * void transform(DOM, NodeIterator, HandlerBase)
1213          */
1214         final com.sun.org.apache.bcel.internal.generic.Type[] argTypes =
1215             new com.sun.org.apache.bcel.internal.generic.Type[3];
1216         argTypes[0] = Util.getJCRefType(DOM_INTF_SIG);
1217         argTypes[1] = Util.getJCRefType(NODE_ITERATOR_SIG);
1218         argTypes[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG);
1219 
1220         final String[] argNames = new String[3];
1221         argNames[0] = DOCUMENT_PNAME;
1222         argNames[1] = ITERATOR_PNAME;
1223         argNames[2] = TRANSLET_OUTPUT_PNAME;
1224 
1225         final InstructionList il = new InstructionList();
1226         final MethodGenerator transf =
1227             new MethodGenerator(ACC_PUBLIC,
1228                                 com.sun.org.apache.bcel.internal.generic.Type.VOID,
1229                                 argTypes, argNames,
1230                                 "transform",
1231                                 _className,
1232                                 il,
1233                                 classGen.getConstantPool());
1234         transf.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException");
1235 
1236         // call resetPrefixIndex at the beginning of transform
1237         final int check = cpg.addMethodref(BASIS_LIBRARY_CLASS, "resetPrefixIndex", "()V");
1238         il.append(new INVOKESTATIC(check));
1239 
1240         // Define and initialize current with the root node
1241         final LocalVariableGen current =
1242             transf.addLocalVariable("current",
1243                                     com.sun.org.apache.bcel.internal.generic.Type.INT,
1244                                     null, null);
1245         final String applyTemplatesSig = classGen.getApplyTemplatesSig();
1246         final int applyTemplates = cpg.addMethodref(getClassName(),
1247                                                     "applyTemplates",
1248                                                     applyTemplatesSig);
1249         final int domField = cpg.addFieldref(getClassName(),
1250                                              DOM_FIELD,
1251                                              DOM_INTF_SIG);
1252 
1253         // push translet for PUTFIELD
1254         il.append(classGen.loadTranslet());
1255         // prepare appropriate DOM implementation
1256 
1257         if (isMultiDocument()) {
1258             il.append(new NEW(cpg.addClass(MULTI_DOM_CLASS)));
1259             il.append(DUP);
1260         }
1261 
1262         il.append(classGen.loadTranslet());
1263         il.append(transf.loadDOM());
1264         il.append(new INVOKEVIRTUAL(cpg.addMethodref(TRANSLET_CLASS,
1265                                                      "makeDOMAdapter",
1266                                                      "("+DOM_INTF_SIG+")"+
1267                                                      DOM_ADAPTER_SIG)));
1268         // DOMAdapter is on the stack
1269 
1270         if (isMultiDocument()) {
1271             final int init = cpg.addMethodref(MULTI_DOM_CLASS,
1272                                               "<init>",
1273                                               "("+DOM_INTF_SIG+")V");
1274             il.append(new INVOKESPECIAL(init));
1275             // MultiDOM is on the stack
1276         }
1277 
1278         //store to _dom variable
1279         il.append(new PUTFIELD(domField));
1280 
1281         // continue with globals initialization
1282         final int gitr = cpg.addInterfaceMethodref(DOM_INTF,
1283                                                         "getIterator",
1284                                                         "()"+NODE_ITERATOR_SIG);
1285         il.append(transf.loadDOM());
1286         il.append(new INVOKEINTERFACE(gitr, 1));
1287         il.append(transf.nextNode());
1288         current.setStart(il.append(new ISTORE(current.getIndex())));
1289 
1290         // Transfer the output settings to the output post-processor
1291         il.append(classGen.loadTranslet());
1292         il.append(transf.loadHandler());
1293         final int index = cpg.addMethodref(TRANSLET_CLASS,
1294                                            "transferOutputSettings",
1295                                            "("+OUTPUT_HANDLER_SIG+")V");
1296         il.append(new INVOKEVIRTUAL(index));
1297 
1298         /*
1299          * Compile buildKeys() method. Note that this method is not
1300          * invoked here as keys for the input document are now created
1301          * in topLevel(). However, this method is still needed by the
1302          * LoadDocument class.
1303          */
1304         final String keySig = compileBuildKeys(classGen);
1305         final int keyIdx = cpg.addMethodref(getClassName(),
1306                                                "buildKeys", keySig);
1307 
1308         // Look for top-level elements that need handling
1309         final Iterator<SyntaxTreeNode> toplevel = elements();
1310         if (_globals.size() > 0 || toplevel.hasNext()) {
1311             // Compile method for handling top-level elements
1312             final String topLevelSig = compileTopLevel(classGen);
1313             // Get a reference to that method
1314             final int topLevelIdx = cpg.addMethodref(getClassName(),
1315                                                      "topLevel",
1316                                                      topLevelSig);
1317             // Push all parameters on the stack and call topLevel()
1318             il.append(classGen.loadTranslet()); // The 'this' pointer
1319             il.append(classGen.loadTranslet());
1320             il.append(new GETFIELD(domField));  // The DOM reference
1321             il.append(transf.loadIterator());
1322             il.append(transf.loadHandler());    // The output handler
1323             il.append(new INVOKEVIRTUAL(topLevelIdx));
1324         }
1325 
1326         // start document
1327         il.append(transf.loadHandler());
1328         il.append(transf.startDocument());
1329 
1330         // push first arg for applyTemplates
1331         il.append(classGen.loadTranslet());
1332         // push translet for GETFIELD to get DOM arg
1333         il.append(classGen.loadTranslet());
1334         il.append(new GETFIELD(domField));
1335         // push remaining 2 args
1336         il.append(transf.loadIterator());
1337         il.append(transf.loadHandler());
1338         il.append(new INVOKEVIRTUAL(applyTemplates));
1339         // endDocument
1340         il.append(transf.loadHandler());
1341         il.append(transf.endDocument());
1342 
1343         il.append(RETURN);
1344 
1345         // Compute max locals + stack and add method to class
1346         classGen.addMethod(transf);
1347 
1348     }
1349 
1350     /**
1351      * Peephole optimization: Remove sequences of [ALOAD, POP].
1352      */
peepHoleOptimization(MethodGenerator methodGen)1353     private void peepHoleOptimization(MethodGenerator methodGen) {
1354         final String pattern = "`aload'`pop'`instruction'";
1355         final InstructionList il = methodGen.getInstructionList();
1356         final InstructionFinder find = new InstructionFinder(il);
1357         for(Iterator<InstructionHandle[]> iter=find.search(pattern); iter.hasNext(); ) {
1358             InstructionHandle[] match = iter.next();
1359             try {
1360                 il.delete(match[0], match[1]);
1361             }
1362             catch (TargetLostException e) {
1363                 // TODO: move target down into the list
1364             }
1365         }
1366     }
1367 
addParam(Param param)1368     public int addParam(Param param) {
1369         _globals.add(param);
1370         return _globals.size() - 1;
1371     }
1372 
addVariable(Variable global)1373     public int addVariable(Variable global) {
1374         _globals.add(global);
1375         return _globals.size() - 1;
1376     }
1377 
display(int indent)1378     public void display(int indent) {
1379         indent(indent);
1380         Util.println("Stylesheet");
1381         displayContents(indent + IndentIncrement);
1382     }
1383 
1384     // do we need this wrapper ?????
getNamespace(String prefix)1385     public String getNamespace(String prefix) {
1386         return lookupNamespace(prefix);
1387     }
1388 
getClassName()1389     public String getClassName() {
1390         return _className;
1391     }
1392 
getTemplates()1393     public List<Template> getTemplates() {
1394         return _templates;
1395     }
1396 
getAllValidTemplates()1397     public List<Template> getAllValidTemplates() {
1398         // Return templates if no imported/included stylesheets
1399         if (_includedStylesheets == null) {
1400             return _templates;
1401         }
1402 
1403         // Is returned value cached?
1404         if (_allValidTemplates == null) {
1405             List<Template> templates = new ArrayList<>();
1406             templates.addAll(_templates);
1407             for (Stylesheet included : _includedStylesheets) {
1408                 templates.addAll(included.getAllValidTemplates());
1409             }
1410             //templates.addAll(_templates);
1411 
1412             // Cache results in top-level stylesheet only
1413             if (_parentStylesheet != null) {
1414                 return templates;
1415             }
1416             _allValidTemplates = templates;
1417          }
1418 
1419         return _allValidTemplates;
1420     }
1421 
addTemplate(Template template)1422     protected void addTemplate(Template template) {
1423         _templates.add(template);
1424     }
1425 }
1426