0001: package net.sf.saxon.style;
0002:
0003: import net.sf.saxon.Configuration;
0004: import net.sf.saxon.PreparedStylesheet;
0005: import net.sf.saxon.event.SaxonOutputKeys;
0006: import net.sf.saxon.value.Whitespace;
0007: import net.sf.saxon.expr.ComputedExpression;
0008: import net.sf.saxon.expr.Expression;
0009: import net.sf.saxon.functions.*;
0010: import net.sf.saxon.instruct.Executable;
0011: import net.sf.saxon.instruct.LocationMap;
0012: import net.sf.saxon.om.*;
0013: import net.sf.saxon.query.XQueryFunction;
0014: import net.sf.saxon.query.XQueryFunctionLibrary;
0015: import net.sf.saxon.sort.CodepointCollator;
0016: import net.sf.saxon.sort.IntHashMap;
0017: import net.sf.saxon.sort.IntHashSet;
0018: import net.sf.saxon.sort.IntIterator;
0019: import net.sf.saxon.trans.*;
0020: import net.sf.saxon.type.SchemaException;
0021: import net.sf.saxon.type.Type;
0022:
0023: import java.util.*;
0024:
0025: /**
0026: * An xsl:stylesheet or xsl:transform element in the stylesheet. <br>
0027: * Note this element represents a stylesheet module, not necessarily
0028: * the whole stylesheet.
0029: */
0030:
0031: public class XSLStylesheet extends StyleElement {
0032:
0033: Executable exec = new Executable();
0034:
0035: // the Location Map keeps track of modules and line numbers of expressions and instructions
0036: private LocationMap locationMap = new LocationMap();
0037:
0038: // index of global variables and parameters, by fingerprint
0039: // (overridden variables are excluded).
0040: // Used at compile-time only, except for debugging
0041: private HashMap globalVariableIndex = new HashMap(20);
0042:
0043: // the name pool used for names that will be needed at run-time, notably
0044: // the names used in XPath expressions and patterns, but also key names, parameter names, etc
0045: private NamePool targetNamePool;
0046:
0047: // true if this stylesheet was included by xsl:include, false if it is the
0048: // principal stylesheet or if it was imported
0049: private boolean wasIncluded = false;
0050:
0051: // the import precedence for top-level elements in this stylesheet
0052: private int precedence = 0;
0053:
0054: // the lowest precedence of any stylesheet imported by this one
0055: private int minImportPrecedence = 0;
0056:
0057: // the StyleSheet that included or imported this one; null for the principal stylesheet
0058: private XSLStylesheet importer = null;
0059:
0060: // the PreparedStylesheet object used to load this stylesheet
0061: private PreparedStylesheet stylesheet;
0062:
0063: // the top-level elements in this logical stylesheet (after include/import)
0064: private List topLevel;
0065:
0066: // table of named templates. Key is the integer fingerprint of the template name;
0067: // value is the XSLTemplate object in the source stylesheet.
0068: private HashMap templateIndex = new HashMap(20);
0069:
0070: // the value of the inputTypeAnnotations attribute on this module, combined with the values
0071: // on all imported/included modules. This is a combination of the bit-significant values
0072: // ANNOTATION_STRIP and ANNOTATION_PRESERVE.
0073: private int inputAnnotations = 0;
0074: public static final int ANNOTATION_STRIP = 1;
0075: public static final int ANNOTATION_PRESERVE = 2;
0076:
0077: // table of imported schemas. The members of this set are strings holding the target namespace.
0078: private HashSet schemaIndex = new HashSet(10);
0079:
0080: // table of functions imported from XQuery library modules
0081: private XQueryFunctionLibrary queryFunctions;
0082:
0083: // function library for external Java functions
0084: private FunctionLibrary javaFunctions;
0085:
0086: // media type (MIME type) of principal output
0087: //private String mediaType;
0088:
0089: // namespace aliases. This information is needed at compile-time only
0090: private int numberOfAliases = 0;
0091: private ArrayList namespaceAliasList = new ArrayList(5);
0092: private short[] aliasSCodes;
0093: private int[] aliasNCodes;
0094:
0095: // count of the maximum number of local variables in xsl:template match patterns
0096: private int largestPatternStackFrame = 0;
0097:
0098: // default validation
0099: private int defaultValidation = Validation.STRIP;
0100:
0101: // library of functions that are in-scope for XPath expressions in this stylesheet
0102: private FunctionLibraryList functionLibrary;
0103:
0104: // flag: true if there's an xsl:result-document that uses a dynamic format
0105: private boolean needsDynamicOutputProperties = false;
0106:
0107: /**
0108: * Create link to the owning PreparedStylesheet object
0109: */
0110:
0111: public void setPreparedStylesheet(PreparedStylesheet sheet) {
0112: Configuration config = sheet.getConfiguration();
0113: stylesheet = sheet;
0114: targetNamePool = sheet.getTargetNamePool();
0115: exec.setConfiguration(config);
0116: exec.setRuleManager(new RuleManager());
0117: exec.setLocationMap(locationMap);
0118: exec.setHostLanguage(Configuration.XSLT);
0119:
0120: functionLibrary = new FunctionLibraryList();
0121: functionLibrary.addFunctionLibrary(new SystemFunctionLibrary(
0122: SystemFunctionLibrary.FULL_XSLT));
0123: functionLibrary
0124: .addFunctionLibrary(new StylesheetFunctionLibrary(this ,
0125: true));
0126: functionLibrary.addFunctionLibrary(config
0127: .getVendorFunctionLibrary());
0128: functionLibrary
0129: .addFunctionLibrary(new ConstructorFunctionLibrary(
0130: config));
0131: queryFunctions = new XQueryFunctionLibrary(config);
0132: functionLibrary.addFunctionLibrary(queryFunctions);
0133: if (config.isAllowExternalFunctions()) {
0134: javaFunctions = config.getExtensionBinder();
0135: functionLibrary.addFunctionLibrary(javaFunctions);
0136: }
0137: functionLibrary
0138: .addFunctionLibrary(new StylesheetFunctionLibrary(this ,
0139: false));
0140:
0141: }
0142:
0143: /**
0144: * Get the owning PreparedStylesheet object
0145: */
0146:
0147: public PreparedStylesheet getPreparedStylesheet() {
0148: if (importer != null) {
0149: return importer.getPreparedStylesheet();
0150: }
0151: return stylesheet;
0152: }
0153:
0154: /**
0155: * Get the run-time Executable object
0156: */
0157:
0158: public Executable getExecutable() {
0159: return exec;
0160: }
0161:
0162: /**
0163: * Get the function library. Available only on the principal stylesheet module
0164: */
0165:
0166: public FunctionLibrary getFunctionLibrary() {
0167: return functionLibrary;
0168: }
0169:
0170: /**
0171: * Get the locationMap object
0172: */
0173:
0174: public LocationMap getLocationMap() {
0175: return locationMap;
0176: }
0177:
0178: /**
0179: * Get the namepool to be used at run-time, this namepool holds the names used in
0180: * all XPath expressions and patterns
0181: */
0182:
0183: public NamePool getTargetNamePool() {
0184: return targetNamePool;
0185: }
0186:
0187: /**
0188: * Get the RuleManager which handles template rules
0189: */
0190:
0191: public RuleManager getRuleManager() {
0192: return exec.getRuleManager();
0193: }
0194:
0195: /**
0196: * Get the rules determining which nodes are to be stripped from the tree
0197: */
0198:
0199: protected Mode getStripperRules() {
0200: if (exec.getStripperRules() == null) {
0201: exec.setStripperRules(new Mode(Mode.STRIPPER_MODE));
0202: }
0203: return exec.getStripperRules();
0204: }
0205:
0206: /**
0207: * Determine whether this stylesheet does any whitespace stripping
0208: */
0209:
0210: public boolean stripsWhitespace() {
0211: for (int i = 0; i < topLevel.size(); i++) {
0212: NodeInfo s = (NodeInfo) topLevel.get(i);
0213: if (s.getFingerprint() == StandardNames.XSL_STRIP_SPACE) {
0214: return true;
0215: }
0216: }
0217: return false;
0218: }
0219:
0220: /**
0221: * Get the KeyManager which handles key definitions
0222: */
0223:
0224: public KeyManager getKeyManager() {
0225: if (exec.getKeyManager() == null) {
0226: exec.setKeyManager(new KeyManager(getConfiguration()));
0227: }
0228: return exec.getKeyManager();
0229: }
0230:
0231: /**
0232: * Get the DecimalFormatManager which handles decimal-format definitions
0233: */
0234:
0235: public DecimalFormatManager getDecimalFormatManager() {
0236: if (exec.getDecimalFormatManager() == null) {
0237: exec.setDecimalFormatManager(new DecimalFormatManager());
0238: }
0239: return exec.getDecimalFormatManager();
0240: }
0241:
0242: /**
0243: * Register a named collation (actually a Comparator)
0244: */
0245:
0246: public void setCollation(String name, Comparator collation,
0247: boolean isDefault) {
0248: if (exec.getCollationTable() == null) {
0249: exec.setCollationTable(new HashMap(20));
0250: }
0251: exec.getCollationTable().put(name, collation);
0252: if (isDefault) {
0253: exec.setDefaultCollationName(name);
0254: }
0255: }
0256:
0257: /**
0258: * Find a named collation. Note this method should only be used at compile-time, before declarations
0259: * have been pre-processed. After that time, use getCollation().
0260: * @param name identifies the name of the collation required; null indicates that the default
0261: * collation is required
0262: * @return null if the collation is not found
0263: */
0264:
0265: protected Comparator findCollation(String name) {
0266:
0267: if (name.equals(NamespaceConstant.CODEPOINT_COLLATION_URI)) {
0268: return CodepointCollator.getInstance();
0269: }
0270:
0271: // First try to find it in the table
0272:
0273: Comparator c = null;
0274: if (name == null) {
0275: name = exec.getDefaultCollationName();
0276: }
0277: if (exec.getCollationTable() != null) {
0278: c = (Comparator) exec.getCollationTable().get(name);
0279: }
0280: if (c != null)
0281: return c;
0282:
0283: // At compile-time, the collation might not yet be in the table. So search for it
0284:
0285: XSLStylesheet stylesheet = getPrincipalStylesheet();
0286: List toplevel = stylesheet.getTopLevel();
0287:
0288: // search for a matching collation name, starting at the end in case of duplicates.
0289: // this also ensures we get the one with highest import precedence.
0290: for (int i = toplevel.size() - 1; i >= 0; i--) {
0291: if (toplevel.get(i) instanceof SaxonCollation) {
0292: SaxonCollation t = (SaxonCollation) toplevel.get(i);
0293: if (name == null && t.isDefaultCollation()) {
0294: exec.setDefaultCollationName(t.getCollationName());
0295: return t.getCollator();
0296: } else if (t.getCollationName().equals(name)) {
0297: return t.getCollator();
0298: }
0299: }
0300: }
0301:
0302: // if it's not defined in the stylesheet, it might be a standard URI
0303:
0304: if (name == null) {
0305: return null;
0306: }
0307:
0308: Configuration config = getConfiguration();
0309: return config.getCollationURIResolver().resolve(name,
0310: getBaseURI(), config);
0311: }
0312:
0313: /**
0314: * Get the name of the default collation
0315: */
0316:
0317: public String getDefaultCollationName() {
0318: return exec.getDefaultCollationName();
0319: }
0320:
0321: /**
0322: * Get a character map, identified by the fingerprint of its name.
0323: * Search backwards through the stylesheet.
0324: * @param fingerprint The fingerprint of the character map name,
0325: * in the target namepool.
0326: * @return the identified character map, or null if not found
0327: */
0328:
0329: public XSLCharacterMap getCharacterMap(int fingerprint) {
0330: for (int i = topLevel.size() - 1; i >= 0; i--) {
0331: if (topLevel.get(i) instanceof XSLCharacterMap) {
0332: XSLCharacterMap t = (XSLCharacterMap) topLevel.get(i);
0333: if (t.getCharacterMapFingerprint() == fingerprint) {
0334: return t;
0335: }
0336: }
0337: }
0338: return null;
0339: }
0340:
0341: /**
0342: * Set the import precedence of this stylesheet
0343: */
0344:
0345: public void setPrecedence(int prec) {
0346: precedence = prec;
0347: }
0348:
0349: /**
0350: * Get the import precedence of this stylesheet
0351: */
0352:
0353: public int getPrecedence() {
0354: if (wasIncluded)
0355: return importer.getPrecedence();
0356: return precedence;
0357: }
0358:
0359: /**
0360: * Get the minimum import precedence of this stylesheet, that is, the lowest precedence
0361: * of any stylesheet imported by this one
0362: */
0363:
0364: public int getMinImportPrecedence() {
0365: return minImportPrecedence;
0366: }
0367:
0368: /**
0369: * Set the minimum import precedence of this stylesheet, that is, the lowest precedence
0370: * of any stylesheet imported by this one
0371: */
0372:
0373: public void setMinImportPrecedence(int precedence) {
0374: minImportPrecedence = precedence;
0375: }
0376:
0377: /**
0378: * Set the StyleSheet that included or imported this one.
0379: */
0380:
0381: public void setImporter(XSLStylesheet importer) {
0382: this .importer = importer;
0383: }
0384:
0385: /**
0386: * Get the StyleSheet that included or imported this one.
0387: * @return null if this is the principal stylesheet
0388: */
0389:
0390: public XSLStylesheet getImporter() {
0391: return importer;
0392: }
0393:
0394: /**
0395: * Indicate that this stylesheet was included (by its "importer") using an xsl:include
0396: * statement as distinct from xsl:import
0397: */
0398:
0399: public void setWasIncluded() {
0400: wasIncluded = true;
0401: }
0402:
0403: /**
0404: * Get the top level elements in this stylesheet, after applying include/import
0405: */
0406:
0407: public List getTopLevel() {
0408: return topLevel;
0409: }
0410:
0411: /**
0412: * Allocate a slot number for a global variable or parameter
0413: */
0414:
0415: public int allocateGlobalSlot(int fingerprint) {
0416: return exec.getGlobalVariableMap().allocateSlotNumber(
0417: fingerprint);
0418: }
0419:
0420: /**
0421: * Ensure there is enough space for local variables or parameters when evaluating the match pattern of
0422: * template rules
0423: */
0424:
0425: public void allocatePatternSlots(int n) {
0426: if (n > largestPatternStackFrame) {
0427: largestPatternStackFrame = n;
0428: }
0429: }
0430:
0431: /**
0432: * Prepare the attributes on the stylesheet element
0433: */
0434:
0435: public void prepareAttributes() throws XPathException {
0436:
0437: String inputTypeAnnotationsAtt = null;
0438: AttributeCollection atts = getAttributeList();
0439: for (int a = 0; a < atts.getLength(); a++) {
0440:
0441: int nc = atts.getNameCode(a);
0442: String f = getNamePool().getClarkName(nc);
0443: if (f == StandardNames.VERSION) {
0444: processVersionAttribute(f);
0445: } else if (f == StandardNames.ID) {
0446: //
0447: } else if (f == StandardNames.EXTENSION_ELEMENT_PREFIXES) {
0448: //
0449: } else if (f == StandardNames.EXCLUDE_RESULT_PREFIXES) {
0450: //
0451: } else if (f == StandardNames.DEFAULT_VALIDATION) {
0452: defaultValidation = Validation
0453: .getCode(atts.getValue(a));
0454: if (defaultValidation == Validation.INVALID) {
0455: compileError(
0456: "Invalid value for default-validation attribute. "
0457: + "Permitted values are (strict, lax, preserve, strip)",
0458: "XTSE0020");
0459: }
0460: } else if (f == StandardNames.INPUT_TYPE_ANNOTATIONS) {
0461: inputTypeAnnotationsAtt = atts.getValue("", f);
0462: } else {
0463: checkUnknownAttribute(nc);
0464: }
0465: }
0466: if (version == null) {
0467: reportAbsence("version");
0468: }
0469:
0470: if (inputTypeAnnotationsAtt != null) {
0471: if (inputTypeAnnotationsAtt.equals("strip")) {
0472: setInputTypeAnnotations(ANNOTATION_STRIP);
0473: } else if (inputTypeAnnotationsAtt.equals("preserve")) {
0474: setInputTypeAnnotations(ANNOTATION_PRESERVE);
0475: } else if (inputTypeAnnotationsAtt.equals("unspecified")) {
0476: //
0477: } else {
0478: compileError(
0479: "Invalid value for input-type-annotations attribute. "
0480: + "Permitted values are (strip, preserve, unspecified)",
0481: "XTSE0020");
0482: }
0483: }
0484: }
0485:
0486: /**
0487: * Get the value of the default validation attribute
0488: */
0489:
0490: public int getDefaultValidation() {
0491: return defaultValidation;
0492: }
0493:
0494: /**
0495: * Get the value of the input-type-annotations attribute, for this module alone.
0496: * The value is an or-ed combination of the two bits
0497: * {@link #ANNOTATION_STRIP} and {@link #ANNOTATION_PRESERVE}
0498: */
0499:
0500: public int getInputTypeAnnotationsAttribute() throws XPathException {
0501: String inputTypeAnnotationsAtt = getAttributeValue(StandardNames.INPUT_TYPE_ANNOTATIONS);
0502: if (inputTypeAnnotationsAtt != null) {
0503: if (inputTypeAnnotationsAtt.equals("strip")) {
0504: setInputTypeAnnotations(ANNOTATION_STRIP);
0505: } else if (inputTypeAnnotationsAtt.equals("preserve")) {
0506: setInputTypeAnnotations(ANNOTATION_PRESERVE);
0507: } else if (inputTypeAnnotationsAtt.equals("unspecified")) {
0508: //
0509: } else {
0510: compileError(
0511: "Invalid value for input-type-annotations attribute. "
0512: + "Permitted values are (strip, preserve, unspecified)",
0513: "XTSE0020");
0514: }
0515: }
0516: return inputAnnotations;
0517: }
0518:
0519: /**
0520: * Get the value of the input-type-annotations attribute, for this module combined with that
0521: * of all included/imported modules. The value is an or-ed combination of the two bits
0522: * {@link #ANNOTATION_STRIP} and {@link #ANNOTATION_PRESERVE}
0523: */
0524:
0525: public int getInputTypeAnnotations() {
0526: return inputAnnotations;
0527: }
0528:
0529: /**
0530: * Set the value of the input-type-annotations attribute, for this module combined with that
0531: * of all included/imported modules. The value is an or-ed combination of the two bits
0532: * {@link #ANNOTATION_STRIP} and {@link #ANNOTATION_PRESERVE}
0533: */
0534:
0535: public void setInputTypeAnnotations(int annotations)
0536: throws XPathException {
0537: inputAnnotations |= annotations;
0538: if (inputAnnotations == (ANNOTATION_STRIP | ANNOTATION_PRESERVE)) {
0539: compileError(
0540: "One stylesheet module specifies input-type-annotations='strip', "
0541: + "another specifies input-type-annotations='preserve'",
0542: "XTSE0265");
0543: }
0544: }
0545:
0546: /**
0547: * Get the declared namespace alias for a given namespace URI code if there is one.
0548: * If there is more than one, we get the last.
0549: * @param uriCode The code of the uri used in the stylesheet.
0550: * @return The namespace code to be used (prefix in top half, uri in bottom half): return -1
0551: * if no alias is defined
0552: */
0553:
0554: protected int getNamespaceAlias(short uriCode) {
0555:
0556: // if there are several matches, the last in stylesheet takes priority;
0557: // but the list is in reverse stylesheet order
0558: for (int i = 0; i < numberOfAliases; i++) {
0559: if (uriCode == aliasSCodes[i]) {
0560: return aliasNCodes[i];
0561: }
0562: }
0563: return uriCode;
0564: }
0565:
0566: /**
0567: * Determine if a namespace is included in the result-prefix of a namespace-alias
0568: */
0569:
0570: protected boolean isAliasResultNamespace(short uriCode) {
0571: for (int i = 0; i < numberOfAliases; i++) {
0572: if (uriCode == (aliasNCodes[i] & 0xffff)) {
0573: return true;
0574: }
0575: }
0576: return false;
0577: }
0578:
0579: /**
0580: * Validate this element
0581: */
0582:
0583: public void validate() throws XPathException {
0584: if (validationError != null) {
0585: compileError(validationError);
0586: }
0587: if (getParent().getNodeKind() != Type.DOCUMENT) {
0588: compileError(getDisplayName()
0589: + " must be the outermost element", "XTSE0010");
0590: }
0591:
0592: AxisIterator kids = iterateAxis(Axis.CHILD);
0593: while (true) {
0594: NodeInfo curr = (NodeInfo) kids.next();
0595: if (curr == null)
0596: break;
0597: if (curr.getNodeKind() == Type.TEXT
0598: || curr instanceof XSLTemplate
0599: || curr instanceof XSLImport
0600: || curr instanceof XSLInclude
0601: || curr instanceof XSLAttributeSet
0602: || curr instanceof XSLCharacterMap
0603: || curr instanceof XSLDecimalFormat
0604: || curr instanceof XSLFunction
0605: || curr instanceof XSLImportSchema
0606: || curr instanceof XSLKey
0607: || curr instanceof XSLNamespaceAlias
0608: || curr instanceof XSLOutput
0609: || curr instanceof XSLParam
0610: || curr instanceof XSLPreserveSpace
0611: || curr instanceof XSLVariable
0612: || curr instanceof XSLParam
0613: || curr instanceof DataElement) {
0614: // all is well
0615: } else if (!NamespaceConstant.XSLT.equals(curr.getURI())
0616: && !"".equals(curr.getURI())) {
0617: // elements in other namespaces are allowed and ignored
0618: } else if (curr instanceof AbsentExtensionElement
0619: && ((StyleElement) curr)
0620: .forwardsCompatibleModeIsEnabled()) {
0621: // this is OK: an unknown XSLT element is allowed in forwards compatibility mode
0622: } else {
0623: ((StyleElement) curr).compileError("Element "
0624: + curr.getDisplayName()
0625: + " must not appear directly within "
0626: + getDisplayName(), "XTSE0010");
0627: }
0628: }
0629: }
0630:
0631: /**
0632: * Preprocess does all the processing possible before the source document is available.
0633: * It is done once per stylesheet, so the stylesheet can be reused for multiple source
0634: * documents. The method is called only on the XSLStylesheet element representing the
0635: * principal stylesheet module
0636: */
0637:
0638: public void preprocess() throws XPathException {
0639:
0640: // process any xsl:include and xsl:import elements
0641:
0642: spliceIncludes();
0643:
0644: // build indexes for selected top-level elements
0645:
0646: buildIndexes();
0647:
0648: // process the attributes of every node in the tree
0649:
0650: processAllAttributes();
0651:
0652: // collect any namespace aliases
0653:
0654: collectNamespaceAliases();
0655:
0656: // fix up references from XPath expressions to variables and functions, for static typing
0657:
0658: for (int i = 0; i < topLevel.size(); i++) {
0659: Object node = topLevel.get(i);
0660: if (node instanceof StyleElement) {
0661: ((StyleElement) node).fixupReferences();
0662: }
0663: }
0664:
0665: // Validate the whole logical style sheet (i.e. with included and imported sheets)
0666:
0667: validate();
0668: for (int i = 0; i < topLevel.size(); i++) {
0669: Object node = topLevel.get(i);
0670: if (node instanceof StyleElement) {
0671: ((StyleElement) node).validateSubtree();
0672: }
0673: }
0674: }
0675:
0676: /**
0677: * Process xsl:include and xsl:import elements.
0678: */
0679:
0680: public void spliceIncludes() throws XPathException {
0681:
0682: boolean foundNonImport = false;
0683: topLevel = new ArrayList(50);
0684: minImportPrecedence = precedence;
0685: StyleElement previousElement = this ;
0686:
0687: AxisIterator kids = iterateAxis(Axis.CHILD);
0688:
0689: while (true) {
0690: NodeInfo child = (NodeInfo) kids.next();
0691: if (child == null) {
0692: break;
0693: }
0694: if (child.getNodeKind() == Type.TEXT) {
0695: // in an embedded stylesheet, white space nodes may still be there
0696: if (!Whitespace.isWhite(child.getStringValueCS())) {
0697: previousElement
0698: .compileError(
0699: "No character data is allowed between top-level elements",
0700: "XTSE0010");
0701: }
0702:
0703: } else if (child instanceof DataElement) {
0704: foundNonImport = true;
0705: } else {
0706: previousElement = (StyleElement) child;
0707: if (child instanceof XSLGeneralIncorporate) {
0708: XSLGeneralIncorporate xslinc = (XSLGeneralIncorporate) child;
0709: xslinc.processAttributes();
0710:
0711: if (xslinc.isImport()) {
0712: if (foundNonImport) {
0713: xslinc
0714: .compileError(
0715: "xsl:import elements must come first",
0716: "XTSE0010");
0717: }
0718: } else {
0719: foundNonImport = true;
0720: }
0721:
0722: // get the included stylesheet. This follows the URL, builds a tree, and splices
0723: // in any indirectly-included stylesheets.
0724:
0725: XSLStylesheet inc = xslinc.getIncludedStylesheet(
0726: this , precedence);
0727: if (inc == null)
0728: return; // error has been reported
0729:
0730: // after processing the imported stylesheet and any others it brought in,
0731: // adjust the import precedence of this stylesheet if necessary
0732:
0733: if (xslinc.isImport()) {
0734: precedence = inc.getPrecedence() + 1;
0735: } else {
0736: precedence = inc.getPrecedence();
0737: inc.setMinImportPrecedence(minImportPrecedence);
0738: inc.setWasIncluded();
0739: }
0740:
0741: // copy the top-level elements of the included stylesheet into the top level of this
0742: // stylesheet. Normally we add these elements at the end, in order, but if the precedence
0743: // of an element is less than the precedence of the previous element, we promote it.
0744: // This implements the requirement in the spec that when xsl:include is used to
0745: // include a stylesheet, any xsl:import elements in the included document are moved
0746: // up in the including document to after any xsl:import elements in the including
0747: // document.
0748:
0749: List incchildren = inc.topLevel;
0750: for (int j = 0; j < incchildren.size(); j++) {
0751: StyleElement elem = (StyleElement) incchildren
0752: .get(j);
0753: int last = topLevel.size() - 1;
0754: if (last < 0
0755: || elem.getPrecedence() >= ((StyleElement) topLevel
0756: .get(last)).getPrecedence()) {
0757: topLevel.add(elem);
0758: } else {
0759: while (last >= 0
0760: && elem.getPrecedence() < ((StyleElement) topLevel
0761: .get(last)).getPrecedence()) {
0762: last--;
0763: }
0764: topLevel.add(last + 1, elem);
0765: }
0766: }
0767: } else {
0768: foundNonImport = true;
0769: topLevel.add(child);
0770: }
0771: }
0772: }
0773: }
0774:
0775: /**
0776: * Build indexes for selected top-level declarations
0777: */
0778:
0779: private void buildIndexes() throws XPathException {
0780: // Scan the declarations in reverse order
0781: for (int i = topLevel.size() - 1; i >= 0; i--) {
0782: Object node = topLevel.get(i);
0783: if (node instanceof XSLTemplate) {
0784: indexNamedTemplate((XSLTemplate) node);
0785: } else if (node instanceof XSLVariableDeclaration) {
0786: indexVariableDeclaration((XSLVariableDeclaration) node);
0787: } else if (node instanceof XSLNamespaceAlias) {
0788: namespaceAliasList.add(node);
0789: numberOfAliases++;
0790: } else if (node instanceof XSLImportSchema) {
0791: try {
0792: ((XSLImportSchema) node).readSchema();
0793: } catch (SchemaException e) {
0794: throw StaticError.makeStaticError(e);
0795: }
0796: } else if (node instanceof XSLDecimalFormat) {
0797: ((XSLDecimalFormat) node).register();
0798: } else if (node instanceof SaxonImportQuery) {
0799: ((SaxonImportQuery) node).importModule();
0800: }
0801: }
0802: // Now seal all the schemas that have been imported to guarantee consistency with instance documents
0803: Configuration config = getConfiguration();
0804: Iterator iter = schemaIndex.iterator();
0805: while (iter.hasNext()) {
0806: String ns = (String) iter.next();
0807: config.sealNamespace(ns);
0808: }
0809:
0810: }
0811:
0812: /**
0813: * Index a global xsl:variable or xsl:param element
0814: * @param var The XSLVariable or XSLParam element
0815: * @throws XPathException
0816: */
0817:
0818: private void indexVariableDeclaration(XSLVariableDeclaration var)
0819: throws XPathException {
0820: int fingerprint = var.getVariableFingerprint();
0821: //System.err.println("fingerprint = " + fingerprint);
0822: if (fingerprint != -1) {
0823: Integer key = new Integer(fingerprint);
0824: // see if there is already a global variable with this precedence
0825: XSLVariableDeclaration other = (XSLVariableDeclaration) globalVariableIndex
0826: .get(key);
0827: if (other == null) {
0828: // this is the first
0829: globalVariableIndex.put(key, var);
0830: } else {
0831: // check the precedences
0832: int this Precedence = var.getPrecedence();
0833: int otherPrecedence = other.getPrecedence();
0834: if (this Precedence == otherPrecedence) {
0835: var.compileError(
0836: "Duplicate global variable declaration (see line "
0837: + other.getLineNumber() + " of "
0838: + other.getSystemId() + ')',
0839: "XTSE0630");
0840: } else if (this Precedence < otherPrecedence) {
0841: var.setRedundant();
0842: } else {
0843: // can't happen, but we'll play safe
0844: other.setRedundant();
0845: globalVariableIndex.put(key, var);
0846: }
0847: }
0848: }
0849: }
0850:
0851: /**
0852: * Add a named template to the index
0853: * @param template The Template object
0854: * @throws XPathException
0855: */
0856: private void indexNamedTemplate(XSLTemplate template)
0857: throws XPathException {
0858: int fingerprint = template.getTemplateFingerprint();
0859: if (fingerprint != -1) {
0860: Integer key = new Integer(fingerprint);
0861: // see if there is already a named template with this precedence
0862: XSLTemplate other = (XSLTemplate) templateIndex.get(key);
0863: if (other == null) {
0864: // this is the first
0865: templateIndex.put(key, template);
0866: } else {
0867: // check the precedences
0868: int this Precedence = template.getPrecedence();
0869: int otherPrecedence = other.getPrecedence();
0870: if (this Precedence == otherPrecedence) {
0871: template.compileError(
0872: "Duplicate named template (see line "
0873: + other.getLineNumber() + " of "
0874: + other.getSystemId() + ')',
0875: "XTSE0660");
0876: } else if (this Precedence < otherPrecedence) {
0877: //template.setRedundantNamedTemplate();
0878: } else {
0879: // can't happen, but we'll play safe
0880: //other.setRedundantNamedTemplate();
0881: templateIndex.put(key, template);
0882: }
0883: }
0884: exec.putNamedTemplate(fingerprint, template
0885: .getCompiledTemplate());
0886: }
0887: }
0888:
0889: /**
0890: * Collect any namespace aliases
0891: */
0892:
0893: private void collectNamespaceAliases() throws XPathException {
0894: aliasSCodes = new short[numberOfAliases];
0895: aliasNCodes = new int[numberOfAliases];
0896: int precedenceBoundary = 0;
0897: int currentPrecedence = -1;
0898: // Note that we are processing the list in reverse stylesheet order,
0899: // that is, highest precedence first.
0900: for (int i = 0; i < numberOfAliases; i++) {
0901: XSLNamespaceAlias xna = (XSLNamespaceAlias) namespaceAliasList
0902: .get(i);
0903: short scode = xna.getStylesheetURICode();
0904: int ncode = xna.getResultNamespaceCode();
0905: int prec = xna.getPrecedence();
0906:
0907: // check that there isn't a conflict with another xsl:namespace-alias
0908: // at the same precedence
0909:
0910: if (currentPrecedence != prec) {
0911: currentPrecedence = prec;
0912: precedenceBoundary = i;
0913: }
0914:
0915: for (int j = precedenceBoundary; j < i; j++) {
0916: if (scode == aliasSCodes[j]) {
0917: if ((ncode & 0xffff) != (aliasNCodes[j] & 0xffff)) {
0918: xna
0919: .compileError(
0920: "More than one alias is defined for the same namespace prefix",
0921: "XTSE0810");
0922: }
0923: }
0924: }
0925:
0926: aliasSCodes[i] = scode;
0927: aliasNCodes[i] = ncode;
0928: }
0929: namespaceAliasList = null; // throw it in the garbage
0930: }
0931:
0932: protected boolean hasNamespaceAliases() {
0933: return numberOfAliases > 0;
0934: }
0935:
0936: /**
0937: * Process the attributes of every node in the stylesheet
0938: */
0939:
0940: public void processAllAttributes() throws XPathException {
0941: prepareAttributes();
0942: if (topLevel == null)
0943: return; // can happen if xsl:stylesheet appears in the wrong place
0944: for (int i = 0; i < topLevel.size(); i++) {
0945: Object s = topLevel.get(i);
0946: if (s instanceof StyleElement) {
0947: try {
0948: ((StyleElement) s).processAllAttributes();
0949: } catch (XPathException err) {
0950: ((StyleElement) s).compileError(err);
0951: }
0952: }
0953: }
0954: }
0955:
0956: /**
0957: * Get the global variable or parameter with a given fingerprint (taking
0958: * precedence rules into account)
0959: */
0960:
0961: public XSLVariableDeclaration getGlobalVariable(int fingerprint) {
0962: return (XSLVariableDeclaration) globalVariableIndex
0963: .get(new Integer(fingerprint));
0964: }
0965:
0966: /**
0967: * Set that this stylesheet needs dynamic output properties
0968: */
0969:
0970: public void setNeedsDynamicOutputProperties(boolean b) {
0971: needsDynamicOutputProperties = b;
0972: }
0973:
0974: /**
0975: * Create an output properties object representing the xsl:output elements in the stylesheet.
0976: * @param fingerprint The name of the output format required. If set to -1, gathers
0977: * information for the unnamed output format
0978: * @return the Properties object containing the details of the specified output format
0979: * @throws XPathException if a named output format does not exist in
0980: * the stylesheet
0981: */
0982:
0983: public Properties gatherOutputProperties(int fingerprint)
0984: throws XPathException {
0985: boolean found = (fingerprint == -1);
0986: Properties details = new Properties();
0987: HashMap precedences = new HashMap(10);
0988: for (int i = topLevel.size() - 1; i >= 0; i--) {
0989: Object s = topLevel.get(i);
0990: if (s instanceof XSLOutput) {
0991: XSLOutput xo = (XSLOutput) s;
0992: if (xo.getOutputFingerprint() == fingerprint) {
0993: found = true;
0994: xo.gatherOutputProperties(details, precedences);
0995: }
0996: }
0997: }
0998: if (!found) {
0999: compileError("Requested output format "
1000: + (fingerprint == -1 ? "(unnamed)" : getNamePool()
1001: .getClarkName(fingerprint))
1002: + " has not been defined");
1003: }
1004: return details;
1005: }
1006:
1007: /**
1008: * Declare an imported XQuery function
1009: */
1010:
1011: protected void declareXQueryFunction(XQueryFunction function)
1012: throws XPathException {
1013: queryFunctions.declareFunction(function);
1014: }
1015:
1016: /**
1017: * Declare a URI that maps to a Java class containing extension functions
1018: */
1019:
1020: protected void declareJavaClass(String uri, Class theClass) {
1021: if (javaFunctions instanceof JavaExtensionLibrary) {
1022: ((JavaExtensionLibrary) javaFunctions).declareJavaClass(
1023: uri, theClass);
1024: } else {
1025: throw new IllegalStateException(
1026: "saxon:script cannot be used with a custom extension library factory");
1027: }
1028: }
1029:
1030: /**
1031: * Get an imported schema with a given namespace
1032: * @param targetNamespace The target namespace of the required schema.
1033: * Supply an empty string for the default namespace
1034: * @return the required Schema, or null if no such schema has been imported
1035: */
1036:
1037: protected boolean isImportedSchema(String targetNamespace) {
1038: return schemaIndex.contains(targetNamespace);
1039: }
1040:
1041: protected void addImportedSchema(String targetNamespace) {
1042: schemaIndex.add(targetNamespace);
1043: }
1044:
1045: protected HashSet getImportedSchemaTable() {
1046: return schemaIndex;
1047: }
1048:
1049: /**
1050: * Compile the stylesheet to create an executable.
1051: */
1052:
1053: public Executable compileStylesheet() throws XPathException {
1054:
1055: try {
1056:
1057: // If any XQuery functions were imported, fix up all function calls
1058: // registered against these functions.
1059: try {
1060: //queryFunctions.bindUnboundFunctionCalls();
1061: Iterator qf = queryFunctions.getFunctionDefinitions();
1062: while (qf.hasNext()) {
1063: XQueryFunction f = (XQueryFunction) qf.next();
1064: f.fixupReferences(getStaticContext());
1065: }
1066: } catch (XPathException e) {
1067: compileError(e);
1068: }
1069:
1070: // Call compile method for each top-level object in the stylesheet
1071:
1072: for (int i = 0; i < topLevel.size(); i++) {
1073: NodeInfo node = (NodeInfo) topLevel.get(i);
1074: if (node instanceof StyleElement) {
1075: StyleElement snode = (StyleElement) node;
1076: //int module = putModuleNumber(snode.getSystemId());
1077: Expression inst = snode.compile(exec);
1078:
1079: if (inst instanceof ComputedExpression) {
1080: ((ComputedExpression) inst)
1081: .setLocationId(allocateLocationId(
1082: getSystemId(), snode
1083: .getLineNumber()));
1084: }
1085: }
1086: }
1087:
1088: // Fix up references to the default default decimal format
1089:
1090: if (exec.getDecimalFormatManager() != null) {
1091: try {
1092: exec.getDecimalFormatManager()
1093: .fixupDefaultDefault();
1094: } catch (StaticError err) {
1095: compileError(err.getMessage(), err
1096: .getErrorCodeLocalPart());
1097: }
1098: }
1099:
1100: exec.setStripsWhitespace(stripsWhitespace());
1101: Properties props = gatherOutputProperties(-1);
1102: props.setProperty(SaxonOutputKeys.STYLESHEET_VERSION,
1103: getVersion().toString());
1104: exec.setDefaultOutputProperties(props);
1105: if (needsDynamicOutputProperties) {
1106: // need to save all the named output formats for use at run-time
1107: IntHashSet outputNames = new IntHashSet(5);
1108: for (int i = 0; i < topLevel.size(); i++) {
1109: Object child = topLevel.get(i);
1110: if (child instanceof XSLOutput) {
1111: XSLOutput out = (XSLOutput) child;
1112: int fp = out.getOutputFingerprint();
1113: if (fp != -1) {
1114: outputNames.add(fp);
1115: }
1116: }
1117: }
1118: if (outputNames.isEmpty()) {
1119: compileError("The stylesheet contains xsl:result-document instructions that calculate the output "
1120: + "format name at run-time, but there are no named xsl:output declarations");
1121: } else {
1122: for (IntIterator iter = outputNames.iterator(); iter
1123: .hasNext();) {
1124: int fp = iter.next();
1125: Properties oprops = gatherOutputProperties(fp);
1126: exec.setOutputProperties(fp, oprops);
1127: }
1128: }
1129: }
1130:
1131: exec.setPatternSlotSpace(largestPatternStackFrame);
1132: exec
1133: .setStripsInputTypeAnnotations(inputAnnotations == ANNOTATION_STRIP);
1134:
1135: // Build the index of named character maps
1136:
1137: for (int i = 0; i < topLevel.size(); i++) {
1138: if (topLevel.get(i) instanceof XSLCharacterMap) {
1139: XSLCharacterMap t = (XSLCharacterMap) topLevel
1140: .get(i);
1141: if (!t.isRedundant()) {
1142: int fp = t.getCharacterMapFingerprint();
1143: IntHashMap map = new IntHashMap(20);
1144: t.assemble(map);
1145: if (exec.getCharacterMapIndex() == null) {
1146: exec
1147: .setCharacterMapIndex(new IntHashMap(
1148: 20));
1149: }
1150: exec.getCharacterMapIndex().put(fp, map);
1151: }
1152: }
1153: }
1154:
1155: // Build a run-time function library. This supports the use of function-available()
1156: // with a dynamic argument, and extensions such as saxon:evaluate().
1157:
1158: ExecutableFunctionLibrary overriding = new ExecutableFunctionLibrary(
1159: getConfiguration());
1160: ExecutableFunctionLibrary underriding = new ExecutableFunctionLibrary(
1161: getConfiguration());
1162:
1163: for (int i = 0; i < topLevel.size(); i++) {
1164: Object child = topLevel.get(i);
1165: if (child instanceof XSLFunction) {
1166: XSLFunction func = (XSLFunction) child;
1167: if (func.isOverriding()) {
1168: overriding.addFunction(func
1169: .getCompiledFunction());
1170: } else {
1171: underriding.addFunction(func
1172: .getCompiledFunction());
1173: }
1174: }
1175: }
1176:
1177: FunctionLibraryList libraryList = new FunctionLibraryList();
1178: libraryList.addFunctionLibrary(new SystemFunctionLibrary(
1179: SystemFunctionLibrary.FULL_XSLT));
1180: libraryList.addFunctionLibrary(overriding);
1181: libraryList.addFunctionLibrary(getConfiguration()
1182: .getVendorFunctionLibrary());
1183: libraryList
1184: .addFunctionLibrary(new ConstructorFunctionLibrary(
1185: getConfiguration()));
1186: libraryList.addFunctionLibrary(queryFunctions);
1187: if (getConfiguration().isAllowExternalFunctions()) {
1188: libraryList.addFunctionLibrary(javaFunctions);
1189: }
1190: libraryList.addFunctionLibrary(underriding);
1191: exec.setFunctionLibrary(libraryList);
1192:
1193: return exec;
1194:
1195: } catch (RuntimeException err) {
1196: // if syntax errors were reported earlier, then exceptions may occur during this phase
1197: // due to inconsistency of data structures. We can ignore these exceptions as they
1198: // will go away when the user corrects the stylesheet
1199: if (getPreparedStylesheet().getErrorCount() > 0) {
1200: // do nothing
1201: return exec;
1202: } else {
1203: // rethrow the exception
1204: throw err;
1205: }
1206: }
1207:
1208: }
1209:
1210: /**
1211: * Dummy compile() method to satisfy the interface
1212: */
1213:
1214: public Expression compile(Executable exec) {
1215: return null;
1216: }
1217:
1218: }
1219:
1220: //
1221: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
1222: // you may not use this file except in compliance with the License. You may obtain a copy of the
1223: // License at http://www.mozilla.org/MPL/
1224: //
1225: // Software distributed under the License is distributed on an "AS IS" basis,
1226: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
1227: // See the License for the specific language governing rights and limitations under the License.
1228: //
1229: // The Original Code is: all this file.
1230: //
1231: // The Initial Developer of the Original Code is Michael H. Kay.
1232: //
1233: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
1234: //
1235: // Contributor(s):
1236: // Portions marked "e.g." are from Edwin Glaser (edwin@pannenleiter.de)
1237: //
|