|
//------------------------------------------------------------------------------
// <copyright file="XslAst.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
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<XslNode> 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<XslNode> EmptyList = new List<XslNode>().AsReadOnly();
public IList<XslNode> Content {
get { return content ?? EmptyList; }
}
public void SetContent(List<XslNode> content) {
this.content = content;
}
public void AddContent(XslNode node) {
Debug.Assert(node != null);
if (content == null) {
content = new List<XslNode>();
}
content.Add(node);
}
public void InsertContent(IEnumerable<XslNode> collection) {
if (content == null) {
content = new List<XslNode>(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("<xsl:attribute-set name=\"");
dbgName.Append(Name.QualifiedName);
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("<xsl:template");
if (Match != null) {
dbgName.Append(" match=\"");
dbgName.Append(Match);
dbgName.Append('"');
}
if (Name != null) {
dbgName.Append(" name=\"");
dbgName.Append(Name.QualifiedName);
dbgName.Append('"');
}
if (!double.IsNaN(Priority)) {
dbgName.Append(" priority=\"");
dbgName.Append(Priority.ToString(CultureInfo.InvariantCulture));
dbgName.Append('"');
}
if (Mode.LocalName.Length != 0) {
dbgName.Append(" mode=\"");
dbgName.Append(Mode.QualifiedName);
dbgName.Append('"');
}
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<QilName, List<Key>> {
protected override QilName GetKeyForItem(List<Key> 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("<xsl:key name=\"");
dbgName.Append(Name.QualifiedName);
dbgName.Append('"');
if (Match != null) {
dbgName.Append(" match=\"");
dbgName.Append(Match);
dbgName.Append('"');
}
if (Use != null) {
dbgName.Append(" use=\"");
dbgName.Append(Use);
dbgName.Append('"');
}
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);
}
}
}
|