KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > style > XSLStylesheet


1 package net.sf.saxon.style;
2
3 import net.sf.saxon.Configuration;
4 import net.sf.saxon.PreparedStylesheet;
5 import net.sf.saxon.event.SaxonOutputKeys;
6 import net.sf.saxon.value.Whitespace;
7 import net.sf.saxon.expr.ComputedExpression;
8 import net.sf.saxon.expr.Expression;
9 import net.sf.saxon.functions.*;
10 import net.sf.saxon.instruct.Executable;
11 import net.sf.saxon.instruct.LocationMap;
12 import net.sf.saxon.om.*;
13 import net.sf.saxon.query.XQueryFunction;
14 import net.sf.saxon.query.XQueryFunctionLibrary;
15 import net.sf.saxon.sort.CodepointCollator;
16 import net.sf.saxon.trans.*;
17 import net.sf.saxon.type.SchemaException;
18 import net.sf.saxon.type.Type;
19
20 import java.util.*;
21
22 /**
23  * An xsl:stylesheet or xsl:transform element in the stylesheet. <br>
24  * Note this element represents a stylesheet module, not necessarily
25  * the whole stylesheet.
26  */

27
28 public class XSLStylesheet extends StyleElement {
29
30     Executable exec = new Executable();
31
32     // the Location Map keeps track of modules and line numbers of expressions and instructions
33
private LocationMap locationMap = new LocationMap();
34
35     // index of global variables and parameters, by fingerprint
36
// (overridden variables are excluded).
37
// Used at compile-time only, except for debugging
38
private HashMap globalVariableIndex = new HashMap(20);
39
40     // the name pool used for names that will be needed at run-time, notably
41
// the names used in XPath expressions and patterns, but also key names, parameter names, etc
42
private NamePool targetNamePool;
43
44     // true if this stylesheet was included by xsl:include, false if it is the
45
// principal stylesheet or if it was imported
46
private boolean wasIncluded = false;
47
48     // the import precedence for top-level elements in this stylesheet
49
private int precedence = 0;
50
51     // the lowest precedence of any stylesheet imported by this one
52
private int minImportPrecedence = 0;
53
54     // the StyleSheet that included or imported this one; null for the principal stylesheet
55
private XSLStylesheet importer = null;
56
57     // the PreparedStylesheet object used to load this stylesheet
58
private PreparedStylesheet stylesheet;
59
60     // the top-level elements in this logical stylesheet (after include/import)
61
private List topLevel;
62
63     // table of named templates. Key is the integer fingerprint of the template name;
64
// value is the XSLTemplate object in the source stylesheet.
65
private HashMap templateIndex = new HashMap(20);
66
67     // the value of the inputTypeAnnotations attribute on this module, combined with the values
68
// on all imported/included modules. This is a combination of the bit-significant values
69
// ANNOTATION_STRIP and ANNOTATION_PRESERVE.
70
private int inputAnnotations = 0;
71     public static final int ANNOTATION_STRIP = 1;
72     public static final int ANNOTATION_PRESERVE = 2;
73
74     // table of imported schemas
75
private HashSet schemaIndex = new HashSet(10);
76
77     // table of functions imported from XQuery library modules
78
private XQueryFunctionLibrary queryFunctions;
79
80     // function library for external Java functions
81
private FunctionLibrary javaFunctions;
82
83     // media type (MIME type) of principal output
84
//private String mediaType;
85

86     // namespace aliases. This information is needed at compile-time only
87
private int numberOfAliases = 0;
88     private ArrayList namespaceAliasList = new ArrayList(5);
89     private short[] aliasSCodes;
90     private int[] aliasNCodes;
91
92     // count of the maximum number of local variables in xsl:template match patterns
93
private int largestPatternStackFrame = 0;
94
95     // default validation
96
private int defaultValidation = Validation.STRIP;
97
98     // library of functions that are in-scope for XPath expressions in this stylesheet
99
private FunctionLibraryList functionLibrary;
100
101     /**
102      * Create link to the owning PreparedStylesheet object
103      */

104
105     public void setPreparedStylesheet(PreparedStylesheet sheet) {
106         Configuration config = sheet.getConfiguration();
107         stylesheet = sheet;
108         targetNamePool = sheet.getTargetNamePool();
109         exec.setConfiguration(config);
110         exec.setRuleManager(new RuleManager());
111         exec.setLocationMap(locationMap);
112         exec.setHostLanguage(Configuration.XSLT);
113
114         functionLibrary = new FunctionLibraryList();
115         functionLibrary.addFunctionLibrary(new SystemFunctionLibrary(SystemFunctionLibrary.FULL_XSLT));
116         functionLibrary.addFunctionLibrary(new StylesheetFunctionLibrary(this, true));
117         functionLibrary.addFunctionLibrary(config.getVendorFunctionLibrary());
118         functionLibrary.addFunctionLibrary(new ConstructorFunctionLibrary(config));
119         queryFunctions = new XQueryFunctionLibrary(config, false);
120         functionLibrary.addFunctionLibrary(queryFunctions);
121         if (config.isAllowExternalFunctions()) {
122             javaFunctions = config.getExtensionBinder();
123             functionLibrary.addFunctionLibrary(javaFunctions);
124         }
125         functionLibrary.addFunctionLibrary(new StylesheetFunctionLibrary(this, false));
126
127     }
128
129     /**
130      * Get the owning PreparedStylesheet object
131      */

132
133     public PreparedStylesheet getPreparedStylesheet() {
134         if (importer != null) {
135             return importer.getPreparedStylesheet();
136         }
137         return stylesheet;
138     }
139
140     /**
141      * Get the run-time Executable object
142      */

143
144     public Executable getExecutable() {
145         return exec;
146     }
147
148     /**
149      * Get the function library. Available only on the principal stylesheet module
150      */

151
152     public FunctionLibrary getFunctionLibrary() {
153         return functionLibrary;
154     }
155
156     /**
157      * Get the locationMap object
158      */

159
160     public LocationMap getLocationMap() {
161         return locationMap;
162     }
163
164     /**
165      * Get the namepool to be used at run-time, this namepool holds the names used in
166      * all XPath expressions and patterns
167      */

168
169     public NamePool getTargetNamePool() {
170         return targetNamePool;
171     }
172
173     /**
174      * Get the RuleManager which handles template rules
175      */

176
177     public RuleManager getRuleManager() {
178         return exec.getRuleManager();
179     }
180
181     /**
182      * Get the rules determining which nodes are to be stripped from the tree
183      */

184
185     protected Mode getStripperRules() {
186         if (exec.getStripperRules() == null) {
187             exec.setStripperRules(new Mode(Mode.STRIPPER_MODE));
188         }
189         return exec.getStripperRules();
190     }
191
192     /**
193      * Determine whether this stylesheet does any whitespace stripping
194      */

195
196     public boolean stripsWhitespace() {
197         for (int i = 0; i < topLevel.size(); i++) {
198             NodeInfo s = (NodeInfo) topLevel.get(i);
199             if (s.getFingerprint() == StandardNames.XSL_STRIP_SPACE) {
200                 return true;
201             }
202         }
203         return false;
204     }
205
206     /**
207      * Get the KeyManager which handles key definitions
208      */

209
210     public KeyManager getKeyManager() {
211         if (exec.getKeyManager() == null) {
212             exec.setKeyManager(new KeyManager(getConfiguration()));
213         }
214         return exec.getKeyManager();
215     }
216
217     /**
218      * Get the DecimalFormatManager which handles decimal-format definitions
219      */

220
221     public DecimalFormatManager getDecimalFormatManager() {
222         if (exec.getDecimalFormatManager() == null) {
223             exec.setDecimalFormatManager(new DecimalFormatManager());
224         }
225         return exec.getDecimalFormatManager();
226     }
227
228     /**
229      * Register a named collation (actually a Comparator)
230      */

231
232     public void setCollation(String JavaDoc name, Comparator collation, boolean isDefault) {
233         if (exec.getCollationTable() == null) {
234             exec.setCollationTable(new HashMap(20));
235         }
236         exec.getCollationTable().put(name, collation);
237         if (isDefault) {
238             exec.setDefaultCollationName(name);
239         }
240     }
241
242     /**
243      * Find a named collation. Note this method should only be used at compile-time, before declarations
244      * have been pre-processed. After that time, use getCollation().
245      * @param name identifies the name of the collation required; null indicates that the default
246      * collation is required
247      * @return null if the collation is not found
248      */

249
250     protected Comparator findCollation(String JavaDoc name) {
251
252         if (name.equals(NamespaceConstant.CODEPOINT_COLLATION_URI)) {
253             return CodepointCollator.getInstance();
254         }
255
256         // First try to find it in the table
257

258         Comparator c = null;
259         if (name == null) {
260             name = exec.getDefaultCollationName();
261         }
262         if (exec.getCollationTable() != null) {
263             c = (Comparator) exec.getCollationTable().get(name);
264         }
265         if (c != null) return c;
266
267         // At compile-time, the collation might not yet be in the table. So search for it
268

269         XSLStylesheet stylesheet = getPrincipalStylesheet();
270         List toplevel = stylesheet.getTopLevel();
271
272         // search for a matching collation name, starting at the end in case of duplicates.
273
// this also ensures we get the one with highest import precedence.
274
for (int i = toplevel.size() - 1; i >= 0; i--) {
275             if (toplevel.get(i) instanceof SaxonCollation) {
276                 SaxonCollation t = (SaxonCollation) toplevel.get(i);
277                 if (name == null && t.isDefaultCollation()) {
278                     exec.setDefaultCollationName(t.getCollationName());
279                     return t.getCollator();
280                 } else if (t.getCollationName().equals(name)) {
281                     return t.getCollator();
282                 }
283             }
284         }
285
286         // if it's not defined in the stylesheet, it might be a standard URI
287

288         if (name == null) {
289             return null;
290         }
291
292         Configuration config = getConfiguration();
293         return config.getCollationURIResolver().resolve(name, getBaseURI(), config);
294     }
295
296     /**
297      * Get the name of the default collation
298      */

299
300     public String JavaDoc getDefaultCollationName() {
301         return exec.getDefaultCollationName();
302     }
303
304     /**
305      * Get a character map, identified by the fingerprint of its name.
306      * Search backwards through the stylesheet.
307      * @param fingerprint The fingerprint of the character map name,
308      * in the target namepool.
309      * @return the identified character map, or null if not found
310      */

311
312     public XSLCharacterMap getCharacterMap(int fingerprint) {
313         for (int i = topLevel.size() - 1; i >= 0; i--) {
314             if (topLevel.get(i) instanceof XSLCharacterMap) {
315                 XSLCharacterMap t = (XSLCharacterMap) topLevel.get(i);
316                 if (t.getCharacterMapFingerprint() == fingerprint) {
317                     return t;
318                 }
319             }
320         }
321         return null;
322     }
323
324     /**
325      * Set the import precedence of this stylesheet
326      */

327
328     public void setPrecedence(int prec) {
329         precedence = prec;
330     }
331
332     /**
333      * Get the import precedence of this stylesheet
334      */

335
336     public int getPrecedence() {
337         if (wasIncluded) return importer.getPrecedence();
338         return precedence;
339     }
340
341     /**
342      * Get the minimum import precedence of this stylesheet, that is, the lowest precedence
343      * of any stylesheet imported by this one
344      */

345
346     public int getMinImportPrecedence() {
347         return minImportPrecedence;
348     }
349
350     /**
351      * Set the minimum import precedence of this stylesheet, that is, the lowest precedence
352      * of any stylesheet imported by this one
353      */

354
355     public void setMinImportPrecedence(int precedence) {
356         minImportPrecedence = precedence;
357     }
358
359     /**
360      * Set the StyleSheet that included or imported this one.
361      */

362
363     public void setImporter(XSLStylesheet importer) {
364         this.importer = importer;
365     }
366
367     /**
368      * Get the StyleSheet that included or imported this one.
369      * @return null if this is the principal stylesheet
370      */

371
372     public XSLStylesheet getImporter() {
373         return importer;
374     }
375
376     /**
377      * Indicate that this stylesheet was included (by its "importer") using an xsl:include
378      * statement as distinct from xsl:import
379      */

380
381     public void setWasIncluded() {
382         wasIncluded = true;
383     }
384
385     /**
386      * Get the top level elements in this stylesheet, after applying include/import
387      */

388
389     public List getTopLevel() {
390         return topLevel;
391     }
392
393     /**
394      * Allocate a slot number for a global variable or parameter
395      */

396
397     public int allocateGlobalSlot(int fingerprint) {
398         return exec.getGlobalVariableMap().allocateSlotNumber(fingerprint);
399     }
400
401     /**
402      * Ensure there is enough space for local variables or parameters when evaluating the match pattern of
403      * template rules
404      */

405
406     public void allocatePatternSlots(int n) {
407         if (n > largestPatternStackFrame) {
408             largestPatternStackFrame = n;
409         }
410     }
411
412     /**
413      * Prepare the attributes on the stylesheet element
414      */

415
416     public void prepareAttributes() throws XPathException {
417
418         String JavaDoc inputTypeAnnotationsAtt = null;
419         AttributeCollection atts = getAttributeList();
420         for (int a = 0; a < atts.getLength(); a++) {
421
422             int nc = atts.getNameCode(a);
423             String JavaDoc f = getNamePool().getClarkName(nc);
424             if (f == StandardNames.VERSION) {
425                 processVersionAttribute(f);
426             } else if (f == StandardNames.ID) {
427                 //
428
} else if (f == StandardNames.EXTENSION_ELEMENT_PREFIXES) {
429                 //
430
} else if (f == StandardNames.EXCLUDE_RESULT_PREFIXES) {
431                 //
432
} else if (f == StandardNames.DEFAULT_VALIDATION) {
433                 defaultValidation = Validation.getCode(atts.getValue(a));
434                 if (defaultValidation == Validation.INVALID) {
435                     compileError("Invalid value for default-validation attribute. " +
436                             "Permitted values are (strict, lax, preserve, strip)", "XTSE0020");
437                 }
438             } else if (f == StandardNames.INPUT_TYPE_ANNOTATIONS) {
439                 inputTypeAnnotationsAtt = atts.getValue("", f);
440             } else {
441                 checkUnknownAttribute(nc);
442             }
443         }
444         if (version == null) {
445             reportAbsence("version");
446         }
447
448         if (inputTypeAnnotationsAtt != null) {
449             if (inputTypeAnnotationsAtt.equals("strip")) {
450                 setInputTypeAnnotations(ANNOTATION_STRIP);
451             } else if (inputTypeAnnotationsAtt.equals("preserve")) {
452                 setInputTypeAnnotations(ANNOTATION_PRESERVE);
453             } else if (inputTypeAnnotationsAtt.equals("unspecified")) {
454                 //
455
} else {
456                 compileError("Invalid value for input-type-annotations attribute. " +
457                              "Permitted values are (strip, preserve, unspecified)", "XTSE0020");
458             }
459         }
460     }
461
462     /**
463      * Get the value of the default validation attribute
464      */

465
466     public int getDefaultValidation() {
467         return defaultValidation;
468     }
469
470
471     /**
472      * Get the value of the input-type-annotations attribute, for this module alone.
473      * The value is an or-ed combination of the two bits
474      * {@link #ANNOTATION_STRIP} and {@link #ANNOTATION_PRESERVE}
475      */

476
477     public int getInputTypeAnnotationsAttribute() throws XPathException {
478         String JavaDoc inputTypeAnnotationsAtt = getAttributeValue(StandardNames.INPUT_TYPE_ANNOTATIONS);
479         if (inputTypeAnnotationsAtt != null) {
480             if (inputTypeAnnotationsAtt.equals("strip")) {
481                 setInputTypeAnnotations(ANNOTATION_STRIP);
482             } else if (inputTypeAnnotationsAtt.equals("preserve")) {
483                 setInputTypeAnnotations(ANNOTATION_PRESERVE);
484             } else if (inputTypeAnnotationsAtt.equals("unspecified")) {
485                 //
486
} else {
487                 compileError("Invalid value for input-type-annotations attribute. " +
488                              "Permitted values are (strip, preserve, unspecified)", "XTSE0020");
489             }
490         }
491         return inputAnnotations;
492     }
493
494
495     /**
496      * Get the value of the input-type-annotations attribute, for this module combined with that
497      * of all included/imported modules. The value is an or-ed combination of the two bits
498      * {@link #ANNOTATION_STRIP} and {@link #ANNOTATION_PRESERVE}
499      */

500
501     public int getInputTypeAnnotations() {
502         return inputAnnotations;
503     }
504
505     /**
506      * Set the value of the input-type-annotations attribute, for this module combined with that
507      * of all included/imported modules. The value is an or-ed combination of the two bits
508      * {@link #ANNOTATION_STRIP} and {@link #ANNOTATION_PRESERVE}
509      */

510
511     public void setInputTypeAnnotations(int annotations) throws XPathException {
512         inputAnnotations |= annotations;
513         if (inputAnnotations == (ANNOTATION_STRIP | ANNOTATION_PRESERVE)) {
514             compileError("One stylesheet module specifies input-type-annotations='strip', " +
515                     "another specifies input-type-annotations='preserve'", "XTSE0265");
516         }
517     }
518
519     /**
520      * Get the declared namespace alias for a given namespace URI code if there is one.
521      * If there is more than one, we get the last.
522      * @param uriCode The code of the uri used in the stylesheet.
523      * @return The namespace code to be used (prefix in top half, uri in bottom half): return -1
524      * if no alias is defined
525      */

526
527     protected int getNamespaceAlias(short uriCode) {
528
529         // if there are several matches, the last in stylesheet takes priority;
530
// but the list is in reverse stylesheet order
531
for (int i = 0; i < numberOfAliases; i++) {
532             if (uriCode == aliasSCodes[i]) {
533                 return aliasNCodes[i];
534             }
535         }
536         return uriCode;
537     }
538
539     /**
540      * Determine if a namespace is included in the result-prefix of a namespace-alias
541      */

542
543     protected boolean isAliasResultNamespace(short uriCode) {
544         for (int i = 0; i < numberOfAliases; i++) {
545             if (uriCode == (aliasNCodes[i] & 0xffff)) {
546                 return true;
547             }
548         }
549         return false;
550     }
551
552     /**
553      * Validate this element
554      */

555
556     public void validate() throws XPathException {
557         if (validationError != null) {
558             compileError(validationError);
559         }
560         if (!(getParent() instanceof DocumentInfo)) {
561             compileError(getDisplayName() + " must be the outermost element", "XTSE0010");
562         }
563
564         AxisIterator kids = iterateAxis(Axis.CHILD);
565         while(true) {
566             NodeInfo curr = (NodeInfo)kids.next();
567             if (curr == null) break;
568             if (curr.getNodeKind() == Type.TEXT ||
569                     curr instanceof XSLTemplate ||
570                     curr instanceof XSLImport ||
571                     curr instanceof XSLInclude ||
572                     curr instanceof XSLAttributeSet ||
573                     curr instanceof XSLCharacterMap ||
574                     curr instanceof XSLDecimalFormat ||
575                     curr instanceof XSLFunction ||
576                     curr instanceof XSLImportSchema ||
577                     curr instanceof XSLKey ||
578                     curr instanceof XSLNamespaceAlias ||
579                     curr instanceof XSLOutput ||
580                     curr instanceof XSLParam ||
581                     curr instanceof XSLPreserveSpace ||
582                     curr instanceof XSLVariable ||
583                     curr instanceof XSLParam ||
584                     curr instanceof DataElement) {
585                 // all is well
586
} else if (!NamespaceConstant.XSLT.equals(curr.getURI()) && !"".equals(curr.getURI())) {
587                 // elements in other namespaces are allowed and ignored
588
} else if (curr instanceof AbsentExtensionElement && ((StyleElement)curr).forwardsCompatibleModeIsEnabled()) {
589                 // this is OK: an unknown XSLT element is allowed in forwards compatibility mode
590
} else {
591                 ((StyleElement)curr).compileError("Element " + curr.getDisplayName() +
592                         " must not appear directly within " + getDisplayName(), "XTSE0010");
593             }
594         }
595     }
596
597     /**
598      * Preprocess does all the processing possible before the source document is available.
599      * It is done once per stylesheet, so the stylesheet can be reused for multiple source
600      * documents. The method is called only on the XSLStylesheet element representing the
601      * principal stylesheet module
602      */

603
604     public void preprocess() throws XPathException {
605
606         // process any xsl:include and xsl:import elements
607

608         spliceIncludes();
609
610         // build indexes for selected top-level elements
611

612         buildIndexes();
613
614         // process the attributes of every node in the tree
615

616         processAllAttributes();
617
618         // collect any namespace aliases
619

620         collectNamespaceAliases();
621
622         // fix up references from XPath expressions to variables and functions, for static typing
623

624         for (int i = 0; i < topLevel.size(); i++) {
625             Object JavaDoc node = topLevel.get(i);
626             if (node instanceof StyleElement) {
627                 ((StyleElement) node).fixupReferences();
628             }
629         }
630
631         // Validate the whole logical style sheet (i.e. with included and imported sheets)
632

633         validate();
634         for (int i = 0; i < topLevel.size(); i++) {
635             Object JavaDoc node = topLevel.get(i);
636             if (node instanceof StyleElement) {
637                 ((StyleElement) node).validateSubtree();
638             }
639         }
640     }
641
642     /**
643      * Process xsl:include and xsl:import elements.
644      */

645
646     public void spliceIncludes() throws XPathException {
647
648         boolean foundNonImport = false;
649         topLevel = new ArrayList(50);
650         minImportPrecedence = precedence;
651         StyleElement previousElement = this;
652
653         AxisIterator kids = iterateAxis(Axis.CHILD);
654
655         while (true) {
656             NodeInfo child = (NodeInfo) kids.next();
657             if (child == null) {
658                 break;
659             }
660             if (child.getNodeKind() == Type.TEXT) {
661                 // in an embedded stylesheet, white space nodes may still be there
662
if (!Whitespace.isWhite(child.getStringValueCS())) {
663                     previousElement.compileError(
664                             "No character data is allowed between top-level elements", "XTSE0010");
665                 }
666
667             } else if (child instanceof DataElement) {
668                 foundNonImport = true;
669             } else {
670                 previousElement = (StyleElement) child;
671                 if (child instanceof XSLGeneralIncorporate) {
672                     XSLGeneralIncorporate xslinc = (XSLGeneralIncorporate) child;
673                     xslinc.processAttributes();
674
675                     if (xslinc.isImport()) {
676                         if (foundNonImport) {
677                             xslinc.compileError("xsl:import elements must come first", "XTSE0010");
678                         }
679                     } else {
680                         foundNonImport = true;
681                     }
682
683                     // get the included stylesheet. This follows the URL, builds a tree, and splices
684
// in any indirectly-included stylesheets.
685

686                     XSLStylesheet inc =
687                             xslinc.getIncludedStylesheet(this, precedence);
688                     if (inc == null) return; // error has been reported
689

690                     // after processing the imported stylesheet and any others it brought in,
691
// adjust the import precedence of this stylesheet if necessary
692

693                     if (xslinc.isImport()) {
694                         precedence = inc.getPrecedence() + 1;
695                     } else {
696                         precedence = inc.getPrecedence();
697                         inc.setMinImportPrecedence(minImportPrecedence);
698                         inc.setWasIncluded();
699                     }
700
701                     // copy the top-level elements of the included stylesheet into the top level of this
702
// stylesheet. Normally we add these elements at the end, in order, but if the precedence
703
// of an element is less than the precedence of the previous element, we promote it.
704
// This implements the requirement in the spec that when xsl:include is used to
705
// include a stylesheet, any xsl:import elements in the included document are moved
706
// up in the including document to after any xsl:import elements in the including
707
// document.
708

709                     List incchildren = inc.topLevel;
710                     for (int j = 0; j < incchildren.size(); j++) {
711                         StyleElement elem = (StyleElement) incchildren.get(j);
712                         int last = topLevel.size() - 1;
713                         if (last < 0 || elem.getPrecedence() >= ((StyleElement) topLevel.get(last)).getPrecedence()) {
714                             topLevel.add(elem);
715                         } else {
716                             while (last >= 0 && elem.getPrecedence() < ((StyleElement) topLevel.get(last)).getPrecedence()) {
717                                 last--;
718                             }
719                             topLevel.add(last + 1, elem);
720                         }
721                     }
722                 } else {
723                     foundNonImport = true;
724                     topLevel.add(child);
725                 }
726             }
727         }
728     }
729
730     /**
731      * Build indexes for selected top-level declarations
732      */

733
734     private void buildIndexes() throws XPathException {
735     // Scan the declarations in reverse order
736
for (int i = topLevel.size() - 1; i >= 0; i--) {
737             Object JavaDoc node = topLevel.get(i);
738             if (node instanceof XSLTemplate) {
739                 indexNamedTemplate((XSLTemplate) node);
740             } else if (node instanceof XSLVariableDeclaration) {
741                 indexVariableDeclaration((XSLVariableDeclaration) node);
742             } else if (node instanceof XSLNamespaceAlias) {
743                 namespaceAliasList.add(node);
744                 numberOfAliases++;
745             } else if (node instanceof XSLImportSchema) {
746                 try {
747                     ((XSLImportSchema) node).readSchema();
748                 } catch (SchemaException e) {
749                     throw StaticError.makeStaticError(e);
750                 }
751             } else if (node instanceof XSLDecimalFormat) {
752                 ((XSLDecimalFormat) node).register();
753             } else if (node instanceof SaxonImportQuery) {
754                 ((SaxonImportQuery) node).importModule();
755             }
756         }
757     }
758
759     /**
760      * Index a global xsl:variable or xsl:param element
761      * @param var The XSLVariable or XSLParam element
762      * @throws XPathException
763      */

764
765     private void indexVariableDeclaration(XSLVariableDeclaration var) throws XPathException {
766         int fingerprint = var.getVariableFingerprint();
767         //System.err.println("fingerprint = " + fingerprint);
768
if (fingerprint != -1) {
769             Integer JavaDoc key = new Integer JavaDoc(fingerprint);
770             // see if there is already a global variable with this precedence
771
XSLVariableDeclaration other = (XSLVariableDeclaration) globalVariableIndex.get(key);
772             if (other == null) {
773                 // this is the first
774
globalVariableIndex.put(key, var);
775             } else {
776                 // check the precedences
777
int thisPrecedence = var.getPrecedence();
778                 int otherPrecedence = other.getPrecedence();
779                 if (thisPrecedence == otherPrecedence) {
780                     var.compileError("Duplicate global variable declaration (see line " +
781                             other.getLineNumber() + " of " + other.getSystemId() + ')', "XTSE0630");
782                 } else if (thisPrecedence < otherPrecedence) {
783                     var.setRedundant();
784                 } else {
785                     // can't happen, but we'll play safe
786
other.setRedundant();
787                     globalVariableIndex.put(key, var);
788                 }
789             }
790         }
791     }
792
793     /**
794      * Add a named template to the index
795      * @param template The Template object
796      * @throws XPathException
797      */

798     private void indexNamedTemplate(XSLTemplate template) throws XPathException {
799         int fingerprint = template.getTemplateFingerprint();
800         if (fingerprint != -1) {
801             Integer JavaDoc key = new Integer JavaDoc(fingerprint);
802             // see if there is already a named template with this precedence
803
XSLTemplate other = (XSLTemplate) templateIndex.get(key);
804             if (other == null) {
805                 // this is the first
806
templateIndex.put(key, template);
807             } else {
808                 // check the precedences
809
int thisPrecedence = template.getPrecedence();
810                 int otherPrecedence = other.getPrecedence();
811                 if (thisPrecedence == otherPrecedence) {
812                     template.compileError("Duplicate named template (see line " +
813                             other.getLineNumber() + " of " + other.getSystemId() + ')', "XTSE0660");
814                 } else if (thisPrecedence < otherPrecedence) {
815                     //template.setRedundantNamedTemplate();
816
} else {
817                     // can't happen, but we'll play safe
818
//other.setRedundantNamedTemplate();
819
templateIndex.put(key, template);
820                 }
821             }
822             exec.getNamedTemplateTable().put(key, template.getCompiledTemplate());
823         }
824     }
825
826     /**
827      * Collect any namespace aliases
828      */

829
830     private void collectNamespaceAliases() throws XPathException {
831         aliasSCodes = new short[numberOfAliases];
832         aliasNCodes = new int[numberOfAliases];
833         int precedenceBoundary = 0;
834         int currentPrecedence = -1;
835         // Note that we are processing the list in reverse stylesheet order,
836
// that is, highest precedence first.
837
for (int i = 0; i < numberOfAliases; i++) {
838             XSLNamespaceAlias xna = (XSLNamespaceAlias) namespaceAliasList.get(i);
839             short scode = xna.getStylesheetURICode();
840             int ncode = xna.getResultNamespaceCode();
841             int prec = xna.getPrecedence();
842
843             // check that there isn't a conflict with another xsl:namespace-alias
844
// at the same precedence
845

846             if (currentPrecedence != prec) {
847                 currentPrecedence = prec;
848                 precedenceBoundary = i;
849             }
850
851             for (int j = precedenceBoundary; j < i; j++) {
852                 if (scode == aliasSCodes[j]) {
853                     if ((ncode & 0xffff) != (aliasNCodes[j] & 0xffff)) {
854                         xna.compileError("More than one alias is defined for the same namespace prefix", "XTSE0810");
855                     }
856                 }
857             }
858
859             aliasSCodes[i] = scode;
860             aliasNCodes[i] = ncode;
861         }
862         namespaceAliasList = null; // throw it in the garbage
863
}
864
865     protected boolean hasNamespaceAliases() {
866         return numberOfAliases > 0;
867     }
868
869     /**
870      * Process the attributes of every node in the stylesheet
871      */

872
873     public void processAllAttributes() throws XPathException {
874         prepareAttributes();
875         if (topLevel == null) return; // can happen if xsl:stylesheet appears in the wrong place
876
for (int i = 0; i < topLevel.size(); i++) {
877             Object JavaDoc s = topLevel.get(i);
878             if (s instanceof StyleElement) {
879                 try {
880                     ((StyleElement) s).processAllAttributes();
881                 } catch (XPathException err) {
882                     ((StyleElement) s).compileError(err);
883                 }
884             }
885         }
886     }
887
888     /**
889      * Get the global variable or parameter with a given fingerprint (taking
890      * precedence rules into account)
891      */

892
893     public XSLVariableDeclaration getGlobalVariable(int fingerprint) {
894         return (XSLVariableDeclaration) globalVariableIndex.get(new Integer JavaDoc(fingerprint));
895     }
896
897     /**
898      * Create an output properties object representing the xsl:output elements in the stylesheet.
899      * @param fingerprint The name of the output format required. If set to -1, gathers
900      * information for the unnamed output format
901      * @return the Properties object containing the details of the specified output format
902      * @throws XPathException if a named output format does not exist in
903      * the stylesheet
904      */

905
906     public Properties gatherOutputProperties(int fingerprint) throws XPathException {
907         boolean found = (fingerprint == -1);
908         Properties details = new Properties();
909         HashMap precedences = new HashMap(10);
910         for (int i = topLevel.size()-1; i >= 0; i--) {
911             Object JavaDoc s = topLevel.get(i);
912             if (s instanceof XSLOutput) {
913                 XSLOutput xo = (XSLOutput) s;
914                 if (xo.getOutputFingerprint() == fingerprint) {
915                     found = true;
916                     xo.gatherOutputProperties(details, precedences);
917                 }
918             }
919         }
920         if (!found) {
921             throw new StaticError("Requested output format has not been defined");
922         }
923         return details;
924     }
925
926     /**
927      * Declare an imported XQuery function
928      */

929
930     protected void declareXQueryFunction(XQueryFunction function) throws XPathException {
931         queryFunctions.declareFunction(function);
932     }
933
934     /**
935      * Declare a URI that maps to a Java class containing extension functions
936      */

937
938     protected void declareJavaClass(String JavaDoc uri, Class JavaDoc theClass) {
939         if (javaFunctions instanceof JavaExtensionLibrary) {
940             ((JavaExtensionLibrary)javaFunctions).declareJavaClass(uri, theClass);
941         } else {
942             throw new IllegalStateException JavaDoc("saxon:script cannot be used with a custom extension library factory");
943         }
944     }
945
946     /**
947      * Get an imported schema with a given namespace
948      * @param targetNamespace The target namespace of the required schema.
949      * Supply an empty string for the default namespace
950      * @return the required Schema, or null if no such schema has been imported
951      */

952
953     protected boolean isImportedSchema(String JavaDoc targetNamespace) {
954         return schemaIndex.contains(targetNamespace);
955     }
956
957     protected void addImportedSchema(String JavaDoc targetNamespace) {
958         schemaIndex.add(targetNamespace);
959     }
960
961     /**
962      * Compile the stylesheet to create an executable.
963      */

964
965     public Executable compileStylesheet() throws XPathException {
966
967         try {
968
969             // If any XQuery functions were imported, fix up all function calls
970
// registered against these functions.
971
try {
972                 queryFunctions.bindUnboundFunctionCalls();
973                 Iterator qf = queryFunctions.getFunctionDefinitions();
974                 while (qf.hasNext()) {
975                     XQueryFunction f = (XQueryFunction) qf.next();
976                     f.fixupReferences(getStaticContext());
977                 }
978             } catch (XPathException e) {
979                 compileError(e);
980             }
981
982             // Call compile method for each top-level object in the stylesheet
983

984             for (int i = 0; i < topLevel.size(); i++) {
985                 NodeInfo node = (NodeInfo) topLevel.get(i);
986                 if (node instanceof StyleElement) {
987                     StyleElement snode = (StyleElement) node;
988                     //int module = putModuleNumber(snode.getSystemId());
989
Expression inst = snode.compile(exec);
990
991                     if (inst instanceof ComputedExpression) {
992                         ((ComputedExpression) inst).setLocationId(allocateLocationId(getSystemId(), snode.getLineNumber()));
993                     }
994                 }
995             }
996
997             // Fix up references to the default default decimal format
998

999             if (exec.getDecimalFormatManager() != null) {
1000                try {
1001                    exec.getDecimalFormatManager().fixupDefaultDefault();
1002                } catch (StaticError err) {
1003                    compileError(err.getMessage(), err.getErrorCodeLocalPart());
1004                }
1005            }
1006
1007            exec.setStripsWhitespace(stripsWhitespace());
1008            Properties props = gatherOutputProperties(-1);
1009            props.setProperty(SaxonOutputKeys.STYLESHEET_VERSION, getVersion().toString());
1010            exec.setDefaultOutputProperties(props);
1011            exec.setPatternSlotSpace(largestPatternStackFrame);
1012            exec.setStripsInputTypeAnnotations(inputAnnotations == ANNOTATION_STRIP);
1013
1014            // Build the index of named character maps
1015

1016            for (int i = 0; i < topLevel.size(); i++) {
1017                if (topLevel.get(i) instanceof XSLCharacterMap) {
1018                    XSLCharacterMap t = (XSLCharacterMap) topLevel.get(i);
1019                    if (!t.isRedundant()) {
1020                        int fp = t.getCharacterMapFingerprint();
1021                        HashMap map = new HashMap(20);
1022                        t.assemble(map);
1023                        if (exec.getCharacterMapIndex() == null) {
1024                            exec.setCharacterMapIndex(new HashMap(20));
1025                        }
1026                        exec.getCharacterMapIndex().put(new Integer JavaDoc(fp), map);
1027                    }
1028                }
1029            }
1030
1031            // Build a run-time function library. This supports the use of function-available()
1032
// with a dynamic argument, and extensions such as saxon:evaluate().
1033

1034            ExecutableFunctionLibrary overriding = new ExecutableFunctionLibrary(getConfiguration());
1035            ExecutableFunctionLibrary underriding = new ExecutableFunctionLibrary(getConfiguration());
1036
1037            List toplevel = getTopLevel();
1038            for (int i=0; i<toplevel.size(); i++) {
1039                Object JavaDoc child = toplevel.get(i);
1040                if (child instanceof XSLFunction) {
1041                    XSLFunction func = (XSLFunction)child;
1042                    if (func.isOverriding()) {
1043                        overriding.addFunction(func.getCompiledFunction());
1044                    } else {
1045                        underriding.addFunction(func.getCompiledFunction());
1046                    }
1047                }
1048            }
1049
1050            FunctionLibraryList libraryList = new FunctionLibraryList();
1051            libraryList.addFunctionLibrary(new SystemFunctionLibrary(SystemFunctionLibrary.FULL_XSLT));
1052            libraryList.addFunctionLibrary(overriding);
1053            libraryList.addFunctionLibrary(getConfiguration().getVendorFunctionLibrary());
1054            libraryList.addFunctionLibrary(new ConstructorFunctionLibrary(getConfiguration()));
1055            libraryList.addFunctionLibrary(queryFunctions);
1056            if (getConfiguration().isAllowExternalFunctions()) {
1057                libraryList.addFunctionLibrary(javaFunctions);
1058            }
1059            libraryList.addFunctionLibrary(underriding);
1060            exec.setFunctionLibrary(libraryList);
1061
1062
1063            return exec;
1064
1065        } catch (RuntimeException JavaDoc err) {
1066        // if syntax errors were reported earlier, then exceptions may occur during this phase
1067
// due to inconsistency of data structures. We can ignore these exceptions as they
1068
// will go away when the user corrects the stylesheet
1069
if (getPreparedStylesheet().getErrorCount() > 0) {
1070                // do nothing
1071
return exec;
1072            } else {
1073                // rethrow the exception
1074
throw err;
1075            }
1076        }
1077
1078    }
1079
1080    /**
1081     * Dummy compile() method to satisfy the interface
1082     */

1083
1084    public Expression compile(Executable exec) {
1085        return null;
1086    }
1087
1088}
1089
1090//
1091
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
1092
// you may not use this file except in compliance with the License. You may obtain a copy of the
1093
// License at http://www.mozilla.org/MPL/
1094
//
1095
// Software distributed under the License is distributed on an "AS IS" basis,
1096
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
1097
// See the License for the specific language governing rights and limitations under the License.
1098
//
1099
// The Original Code is: all this file.
1100
//
1101
// The Initial Developer of the Original Code is Michael H. Kay.
1102
//
1103
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
1104
//
1105
// Contributor(s):
1106
// Portions marked "e.g." are from Edwin Glaser (edwin@pannenleiter.de)
1107
//
1108
Popular Tags