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