//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // Microsoft //------------------------------------------------------------------------------ using System.Text; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.Xml.Xsl.Qil; namespace System.Xml.Xsl.Xslt { using ContextInfo = XsltInput.ContextInfo; using XPathQilFactory = System.Xml.Xsl.XPath.XPathQilFactory; // Set of classes that represent XSLT AST // XSLT AST is a tree of nodes that represent content of xsl template. // All nodes are subclasses of QilNode. This was done to keep NodeCtor and Text ctors // the sames nodes thay will be in resulting QilExpression tree. // So we have: ElementCtor, AttributeCtor, QilTextCtor, CommentCtor, PICtor, NamespaceDecl, List. // Plus couple subclasses of XslNode that represent different xslt instructions // including artifitial: Sort, ExNamespaceDecl, UseAttributeSets internal enum XslNodeType { Unknown = 0, ApplyImports, ApplyTemplates, Attribute, AttributeSet, CallTemplate, Choose, Comment, Copy, CopyOf, Element, Error, ForEach, If, Key, List, LiteralAttribute, LiteralElement, Message, Nop, Number, Otherwise, Param, PI, Sort, Template, Text, UseAttributeSet, ValueOf, ValueOfDoe, Variable, WithParam, } internal class NsDecl { public readonly NsDecl Prev; public readonly string Prefix; // Empty string denotes the default namespace, null - extension or excluded namespace public readonly string NsUri; // null means "#all" -- all namespace defined above this one are excluded. public NsDecl(NsDecl prev, string prefix, string nsUri) { Debug.Assert(nsUri != null || Prefix == null); this.Prev = prev; this.Prefix = prefix; this.NsUri = nsUri; } } internal class XslNode { public readonly XslNodeType NodeType; public ISourceLineInfo SourceLine; public NsDecl Namespaces; public readonly QilName Name; // name or mode public readonly object Arg; // select or test or terminate or stylesheet;-) public readonly XslVersion XslVersion; public XslFlags Flags; private List content; public XslNode(XslNodeType nodeType, QilName name, object arg, XslVersion xslVer) { this.NodeType = nodeType; this.Name = name; this.Arg = arg; this.XslVersion = xslVer; } public XslNode(XslNodeType nodeType) { this.NodeType = nodeType; this.XslVersion = XslVersion.Current; } public string Select { get { return (string)Arg; } } public bool ForwardsCompatible { get { return XslVersion == XslVersion.ForwardsCompatible; } } // -------------------------------- Content Management -------------------------------- private static readonly IList EmptyList = new List().AsReadOnly(); public IList Content { get { return content ?? EmptyList; } } public void SetContent(List content) { this.content = content; } public void AddContent(XslNode node) { Debug.Assert(node != null); if (content == null) { content = new List(); } content.Add(node); } public void InsertContent(IEnumerable collection) { if (content == null) { content = new List(collection); } else { content.InsertRange(0, collection); } } internal string TraceName { get { #if DEBUG System.Text.StringBuilder sb = new System.Text.StringBuilder(); string nodeTypeName; switch (NodeType) { case XslNodeType.AttributeSet : nodeTypeName = "attribute-set"; break; case XslNodeType.Template : nodeTypeName = "template"; break; case XslNodeType.Param : nodeTypeName = "param"; break; case XslNodeType.Variable : nodeTypeName = "variable"; break; case XslNodeType.WithParam : nodeTypeName = "with-param"; break; default : nodeTypeName = NodeType.ToString(); break; } sb.Append(nodeTypeName); if (Name != null) { sb.Append(' '); sb.Append(Name.QualifiedName); } ISourceLineInfo lineInfo = SourceLine; if (lineInfo == null && NodeType == XslNodeType.AttributeSet) { lineInfo = Content[0].SourceLine; Debug.Assert(lineInfo != null); } if (lineInfo != null) { string fileName = SourceLineInfo.GetFileName(lineInfo.Uri); int idx = fileName.LastIndexOf(System.IO.Path.DirectorySeparatorChar) + 1; sb.Append(" ("); sb.Append(fileName, idx, fileName.Length - idx); sb.Append(':'); sb.Append(lineInfo.Start.Line); sb.Append(')'); } return sb.ToString(); #else return null; #endif } } } internal abstract class ProtoTemplate : XslNode { public QilFunction Function; // Compiled body public ProtoTemplate(XslNodeType nt, QilName name, XslVersion xslVer) : base(nt, name, null, xslVer) {} public abstract string GetDebugName(); } internal enum CycleCheck { NotStarted = 0, Processing = 1, Completed = 2, } internal class AttributeSet : ProtoTemplate { public CycleCheck CycleCheck; // Used to detect circular references public AttributeSet(QilName name, XslVersion xslVer) : base(XslNodeType.AttributeSet, name, xslVer) {} public override string GetDebugName() { StringBuilder dbgName = new StringBuilder(); dbgName.Append(""); return dbgName.ToString(); } public new void AddContent(XslNode node) { Debug.Assert(node != null && node.NodeType == XslNodeType.List); base.AddContent(node); } public void MergeContent(AttributeSet other) { InsertContent(other.Content); } } internal class Template : ProtoTemplate { public readonly string Match; public readonly QilName Mode; public readonly double Priority; public int ImportPrecedence; public int OrderNumber; public Template(QilName name, string match, QilName mode, double priority, XslVersion xslVer) : base(XslNodeType.Template, name, xslVer) { this.Match = match; this.Mode = mode; this.Priority = priority; } public override string GetDebugName() { StringBuilder dbgName = new StringBuilder(); dbgName.Append("'); return dbgName.ToString(); } } internal class VarPar : XslNode { public XslFlags DefValueFlags; public QilNode Value; // Contains value for WithParams and global VarPars public VarPar(XslNodeType nt, QilName name, string select, XslVersion xslVer) : base(nt, name, select, xslVer) {} } internal class Sort : XslNode { public readonly string Lang; public readonly string DataType; public readonly string Order; public readonly string CaseOrder; public Sort(string select, string lang, string dataType, string order, string caseOrder, XslVersion xslVer) : base(XslNodeType.Sort, null, select, xslVer) { this.Lang = lang; this.DataType = dataType; this.Order = order; this.CaseOrder = caseOrder; } } internal class Keys : KeyedCollection> { protected override QilName GetKeyForItem(List list) { Debug.Assert(list != null && list.Count > 0); return list[0].Name; } } internal class Key : XslNode { public readonly string Match; public readonly string Use; public QilFunction Function; public Key(QilName name, string match, string use, XslVersion xslVer) : base(XslNodeType.Key, name, null, xslVer) { // match and use can be null in case of incorrect stylesheet Debug.Assert(name != null); this.Match = match; this.Use = use; } public string GetDebugName() { StringBuilder dbgName = new StringBuilder(); dbgName.Append("'); return dbgName.ToString(); } } internal enum NumberLevel { Single, Multiple, Any, } internal class Number : XslNode { public readonly NumberLevel Level; public readonly string Count; public readonly string From; public readonly string Value; public readonly string Format; public readonly string Lang; public readonly string LetterValue; public readonly string GroupingSeparator; public readonly string GroupingSize; public Number(NumberLevel level, string count, string from, string value, string format, string lang, string letterValue, string groupingSeparator, string groupingSize, XslVersion xslVer) : base(XslNodeType.Number, null, null, xslVer) { this.Level = level; this.Count = count; this.From = from; this.Value = value; this.Format = format; this.Lang = lang; this.LetterValue = letterValue; this.GroupingSeparator = groupingSeparator; this.GroupingSize = groupingSize; } } internal class NodeCtor : XslNode { public readonly string NameAvt; public readonly string NsAvt; public NodeCtor(XslNodeType nt, string nameAvt, string nsAvt, XslVersion xslVer) : base(nt, null, null, xslVer) { this.NameAvt = nameAvt; this.NsAvt = nsAvt; } } internal class Text : XslNode { public readonly SerializationHints Hints; public Text(string data, SerializationHints hints, XslVersion xslVer) : base(XslNodeType.Text, null, data, xslVer) { this.Hints = hints; } } internal class XslNodeEx : XslNode { public readonly ISourceLineInfo ElemNameLi; public readonly ISourceLineInfo EndTagLi; public XslNodeEx(XslNodeType t, QilName name, object arg, ContextInfo ctxInfo, XslVersion xslVer) : base(t, name, arg, xslVer) { ElemNameLi = ctxInfo.elemNameLi; EndTagLi = ctxInfo.endTagLi; } public XslNodeEx(XslNodeType t, QilName name, object arg, XslVersion xslVer) : base(t, name, arg, xslVer) { } } internal static class AstFactory { public static XslNode XslNode(XslNodeType nodeType, QilName name, string arg, XslVersion xslVer) { return new XslNode(nodeType, name, arg, xslVer); } public static XslNode ApplyImports(QilName mode, Stylesheet sheet, XslVersion xslVer) { return new XslNode(XslNodeType.ApplyImports, mode, sheet, xslVer); } public static XslNodeEx ApplyTemplates(QilName mode, string select, ContextInfo ctxInfo, XslVersion xslVer) { return new XslNodeEx(XslNodeType.ApplyTemplates, mode, select, ctxInfo, xslVer); } // Special node for start apply-templates public static XslNodeEx ApplyTemplates(QilName mode) { return new XslNodeEx(XslNodeType.ApplyTemplates, mode, /*select:*/null, XslVersion.Current); } public static NodeCtor Attribute(string nameAvt, string nsAvt, XslVersion xslVer) { return new NodeCtor(XslNodeType.Attribute, nameAvt, nsAvt, xslVer); } public static AttributeSet AttributeSet(QilName name) { return new AttributeSet(name, XslVersion.Current); } public static XslNodeEx CallTemplate(QilName name, ContextInfo ctxInfo) { return new XslNodeEx(XslNodeType.CallTemplate, name, null, ctxInfo, XslVersion.Current); } public static XslNode Choose() { return new XslNode(XslNodeType.Choose); } public static XslNode Comment() { return new XslNode(XslNodeType.Comment); } public static XslNode Copy() { return new XslNode(XslNodeType.Copy); } public static XslNode CopyOf(string select, XslVersion xslVer) { return new XslNode(XslNodeType.CopyOf, null, select, xslVer); } public static NodeCtor Element(string nameAvt, string nsAvt, XslVersion xslVer) { return new NodeCtor(XslNodeType.Element, nameAvt, nsAvt, xslVer); } public static XslNode Error(string message) { return new XslNode(XslNodeType.Error, null, message, XslVersion.Current); } public static XslNodeEx ForEach(string select, ContextInfo ctxInfo, XslVersion xslVer) { return new XslNodeEx(XslNodeType.ForEach, null, select, ctxInfo, xslVer); } public static XslNode If(string test, XslVersion xslVer) { return new XslNode(XslNodeType.If, null, test, xslVer); } public static Key Key(QilName name, string match, string use, XslVersion xslVer) { return new Key(name, match, use, xslVer); } public static XslNode List() { return new XslNode(XslNodeType.List); } public static XslNode LiteralAttribute(QilName name, string value, XslVersion xslVer) { return new XslNode(XslNodeType.LiteralAttribute, name, value, xslVer); } public static XslNode LiteralElement(QilName name) { return new XslNode(XslNodeType.LiteralElement, name, null, XslVersion.Current); } public static XslNode Message(bool term) { return new XslNode(XslNodeType.Message, null, term, XslVersion.Current); } public static XslNode Nop() { return new XslNode(XslNodeType.Nop); } public static Number Number(NumberLevel level, string count, string from, string value, string format, string lang, string letterValue, string groupingSeparator, string groupingSize, XslVersion xslVer) { return new Number(level, count, from, value, format, lang, letterValue, groupingSeparator, groupingSize, xslVer); } public static XslNode Otherwise() { return new XslNode(XslNodeType.Otherwise); } public static XslNode PI(string name, XslVersion xslVer) { return new XslNode(XslNodeType.PI, null, name, xslVer); } public static Sort Sort(string select, string lang, string dataType, string order, string caseOrder, XslVersion xslVer) { return new Sort(select, lang, dataType, order, caseOrder, xslVer); } public static Template Template(QilName name, string match, QilName mode, double priority, XslVersion xslVer) { return new Template(name, match, mode, priority, xslVer); } public static XslNode Text(string data) { return new Text(data, SerializationHints.None, XslVersion.Current); } public static XslNode Text(string data, SerializationHints hints) { return new Text(data, hints, XslVersion.Current); } public static XslNode UseAttributeSet(QilName name) { return new XslNode(XslNodeType.UseAttributeSet, name, null, XslVersion.Current); } public static VarPar VarPar(XslNodeType nt, QilName name, string select, XslVersion xslVer) { return new VarPar(nt, name, select, xslVer); } public static VarPar WithParam(QilName name) { return VarPar(XslNodeType.WithParam, name, /*select*/null, XslVersion.Current); } private static QilFactory f = new QilFactory(); public static QilName QName(string local, string uri, string prefix) { return f.LiteralQName(local, uri, prefix); } public static QilName QName(string local) { return f.LiteralQName(local); } } }