You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -0,0 +1,424 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="Compiler.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Xml.XPath;
|
||||
using System.Xml.Xsl.Qil;
|
||||
using System.Xml.Xsl.XPath;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace System.Xml.Xsl.Xslt {
|
||||
using Res = System.Xml.Utils.Res;
|
||||
using TypeFactory = XmlQueryTypeFactory;
|
||||
#if DEBUG
|
||||
using XmlILTrace = System.Xml.Xsl.IlGen.XmlILTrace;
|
||||
#endif
|
||||
|
||||
internal enum XslVersion {
|
||||
Version10 = 0,
|
||||
ForwardsCompatible = 1,
|
||||
Current = Version10,
|
||||
}
|
||||
|
||||
// RootLevel is underdeveloped consept currently. I plane to move here more collections from Compiler.
|
||||
// Compiler is like a stylesheet in some sense. it has a lot of properties of stylesheet. Instead of
|
||||
// inhereting from Styleseet (or StylesheetLevel) I desided to agregate special subclass of StylesheetLevel.
|
||||
// One more reason to for this design is to normolize apply-templates and apply-imports to one concept:
|
||||
// apply-templates is apply-imports(compiler.Root).
|
||||
// For now I don't create new files for these new classes to simplify integrations WebData <-> WebData_xsl
|
||||
internal class RootLevel : StylesheetLevel {
|
||||
public RootLevel(Stylesheet principal) {
|
||||
base.Imports = new Stylesheet[] { principal };
|
||||
}
|
||||
}
|
||||
|
||||
internal class Compiler {
|
||||
public XsltSettings Settings;
|
||||
public bool IsDebug;
|
||||
public string ScriptAssemblyPath;
|
||||
public int Version; // 0 - Auto; 1 - XSLT 1.0; 2 - XSLT 2.0
|
||||
public string inputTypeAnnotations; // null - "unspecified"; "preserve"; "strip"
|
||||
|
||||
public CompilerResults CompilerResults; // Results of the compilation
|
||||
public int CurrentPrecedence = 0; // Decreases by 1 with each import
|
||||
public XslNode StartApplyTemplates;
|
||||
public RootLevel Root;
|
||||
public Scripts Scripts;
|
||||
public Output Output = new Output();
|
||||
public List<VarPar> ExternalPars = new List<VarPar>();
|
||||
public List<VarPar> GlobalVars = new List<VarPar>();
|
||||
public List<WhitespaceRule> WhitespaceRules = new List<WhitespaceRule>();
|
||||
public DecimalFormats DecimalFormats = new DecimalFormats();
|
||||
public Keys Keys = new Keys();
|
||||
public List<ProtoTemplate> AllTemplates = new List<ProtoTemplate>();
|
||||
|
||||
public Dictionary<QilName, VarPar> AllGlobalVarPars = new Dictionary<QilName, VarPar>();
|
||||
public Dictionary<QilName, Template> NamedTemplates = new Dictionary<QilName, Template>();
|
||||
public Dictionary<QilName, AttributeSet> AttributeSets = new Dictionary<QilName, AttributeSet>();
|
||||
public Dictionary<string, NsAlias> NsAliases = new Dictionary<string, NsAlias>();
|
||||
|
||||
private Dictionary<string, int> moduleOrder = new Dictionary<string,int>();
|
||||
|
||||
public Compiler(XsltSettings settings, bool debug, string scriptAssemblyPath) {
|
||||
Debug.Assert(CompilerResults == null, "Compiler cannot be reused");
|
||||
|
||||
// Keep all intermediate files if tracing is enabled
|
||||
TempFileCollection tempFiles = settings.TempFiles ?? new TempFileCollection();
|
||||
|
||||
#if DEBUG
|
||||
if (XmlILTrace.IsEnabled) {
|
||||
tempFiles.KeepFiles = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
Settings = settings;
|
||||
IsDebug = settings.IncludeDebugInformation | debug;
|
||||
ScriptAssemblyPath = scriptAssemblyPath;
|
||||
|
||||
CompilerResults = new CompilerResults(tempFiles);
|
||||
Scripts = new Scripts(this);
|
||||
}
|
||||
|
||||
[ResourceConsumption(ResourceScope.Machine)]
|
||||
[ResourceExposure(ResourceScope.Machine)]
|
||||
public CompilerResults Compile(object stylesheet, XmlResolver xmlResolver, out QilExpression qil) {
|
||||
Debug.Assert(stylesheet != null);
|
||||
Debug.Assert(Root == null, "Compiler cannot be reused");
|
||||
|
||||
new XsltLoader().Load(this, stylesheet, xmlResolver);
|
||||
qil = QilGenerator.CompileStylesheet(this);
|
||||
SortErrors();
|
||||
return CompilerResults;
|
||||
}
|
||||
|
||||
public Stylesheet CreateStylesheet() {
|
||||
Stylesheet sheet = new Stylesheet(this, CurrentPrecedence);
|
||||
if (CurrentPrecedence-- == 0) {
|
||||
Root = new RootLevel(sheet);
|
||||
}
|
||||
return sheet;
|
||||
}
|
||||
|
||||
public void AddModule(string baseUri) {
|
||||
if (!moduleOrder.ContainsKey(baseUri)) {
|
||||
moduleOrder[baseUri] = moduleOrder.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyNsAliases(ref string prefix, ref string nsUri) {
|
||||
NsAlias alias;
|
||||
if (NsAliases.TryGetValue(nsUri, out alias)) {
|
||||
nsUri = alias.ResultNsUri;
|
||||
prefix = alias.ResultPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true in case of redefinition
|
||||
public bool SetNsAlias(string ssheetNsUri, string resultNsUri, string resultPrefix, int importPrecedence) {
|
||||
NsAlias oldNsAlias;
|
||||
if (NsAliases.TryGetValue(ssheetNsUri, out oldNsAlias)) {
|
||||
// Namespace alias for this stylesheet namespace URI has already been defined
|
||||
Debug.Assert(importPrecedence <= oldNsAlias.ImportPrecedence, "Stylesheets must be processed in the order of decreasing import precedence");
|
||||
if (importPrecedence < oldNsAlias.ImportPrecedence || resultNsUri == oldNsAlias.ResultNsUri) {
|
||||
// Either the identical definition or lower precedence - ignore it
|
||||
return false;
|
||||
}
|
||||
// Recover by choosing the declaration that occurs later in the stylesheet
|
||||
}
|
||||
NsAliases[ssheetNsUri] = new NsAlias(resultNsUri, resultPrefix, importPrecedence);
|
||||
return oldNsAlias != null;
|
||||
}
|
||||
|
||||
private void MergeWhitespaceRules(Stylesheet sheet) {
|
||||
for (int idx = 0; idx <= 2; idx++) {
|
||||
sheet.WhitespaceRules[idx].Reverse();
|
||||
this.WhitespaceRules.AddRange(sheet.WhitespaceRules[idx]);
|
||||
}
|
||||
sheet.WhitespaceRules = null;
|
||||
}
|
||||
|
||||
private void MergeAttributeSets(Stylesheet sheet) {
|
||||
foreach (QilName attSetName in sheet.AttributeSets.Keys) {
|
||||
AttributeSet attSet;
|
||||
if (!this.AttributeSets.TryGetValue(attSetName, out attSet)) {
|
||||
this.AttributeSets[attSetName] = sheet.AttributeSets[attSetName];
|
||||
} else {
|
||||
// Lower import precedence - insert before all previous definitions
|
||||
attSet.MergeContent(sheet.AttributeSets[attSetName]);
|
||||
}
|
||||
}
|
||||
sheet.AttributeSets = null;
|
||||
}
|
||||
|
||||
private void MergeGlobalVarPars(Stylesheet sheet) {
|
||||
foreach (VarPar var in sheet.GlobalVarPars) {
|
||||
Debug.Assert(var.NodeType == XslNodeType.Variable || var.NodeType == XslNodeType.Param);
|
||||
if (!AllGlobalVarPars.ContainsKey(var.Name)) {
|
||||
if (var.NodeType == XslNodeType.Variable) {
|
||||
GlobalVars.Add(var);
|
||||
} else {
|
||||
ExternalPars.Add(var);
|
||||
}
|
||||
AllGlobalVarPars[var.Name] = var;
|
||||
}
|
||||
}
|
||||
sheet.GlobalVarPars = null;
|
||||
}
|
||||
|
||||
public void MergeWithStylesheet(Stylesheet sheet) {
|
||||
MergeWhitespaceRules(sheet);
|
||||
MergeAttributeSets(sheet);
|
||||
MergeGlobalVarPars(sheet);
|
||||
}
|
||||
|
||||
public static string ConstructQName(string prefix, string localName) {
|
||||
if (prefix.Length == 0) {
|
||||
return localName;
|
||||
} else {
|
||||
return prefix + ':' + localName;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ParseQName(string qname, out string prefix, out string localName, IErrorHelper errorHelper) {
|
||||
Debug.Assert(qname != null);
|
||||
try {
|
||||
ValidateNames.ParseQNameThrow(qname, out prefix, out localName);
|
||||
return true;
|
||||
}
|
||||
catch (XmlException e) {
|
||||
errorHelper.ReportError(/*[XT_042]*/e.Message, null);
|
||||
prefix = PhantomNCName;
|
||||
localName = PhantomNCName;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ParseNameTest(string nameTest, out string prefix, out string localName, IErrorHelper errorHelper) {
|
||||
Debug.Assert(nameTest != null);
|
||||
try {
|
||||
ValidateNames.ParseNameTestThrow(nameTest, out prefix, out localName);
|
||||
return true;
|
||||
}
|
||||
catch (XmlException e) {
|
||||
errorHelper.ReportError(/*[XT_043]*/e.Message, null);
|
||||
prefix = PhantomNCName;
|
||||
localName = PhantomNCName;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void ValidatePiName(string name, IErrorHelper errorHelper) {
|
||||
Debug.Assert(name != null);
|
||||
try {
|
||||
ValidateNames.ValidateNameThrow(
|
||||
/*prefix:*/string.Empty, /*localName:*/name, /*ns:*/string.Empty,
|
||||
XPathNodeType.ProcessingInstruction, ValidateNames.Flags.AllExceptPrefixMapping
|
||||
);
|
||||
}
|
||||
catch (XmlException e) {
|
||||
errorHelper.ReportError(/*[XT_044]*/e.Message, null);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly string PhantomNCName = "error";
|
||||
private int phantomNsCounter = 0;
|
||||
|
||||
public string CreatePhantomNamespace() {
|
||||
// Prepend invalid XmlChar to ensure this name would not clash with any namespace name in the stylesheet
|
||||
return "\0namespace" + phantomNsCounter++;
|
||||
}
|
||||
|
||||
public bool IsPhantomNamespace(string namespaceName) {
|
||||
return namespaceName.Length > 0 && namespaceName[0] == '\0';
|
||||
}
|
||||
|
||||
public bool IsPhantomName(QilName qname) {
|
||||
string nsUri = qname.NamespaceUri;
|
||||
return nsUri.Length > 0 && nsUri[0] == '\0';
|
||||
}
|
||||
|
||||
// -------------------------------- Error Handling --------------------------------
|
||||
|
||||
private int ErrorCount {
|
||||
get {
|
||||
return CompilerResults.Errors.Count;
|
||||
}
|
||||
set {
|
||||
Debug.Assert(value <= ErrorCount);
|
||||
for (int idx = ErrorCount - 1; idx >= value; idx--) {
|
||||
CompilerResults.Errors.RemoveAt(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int savedErrorCount = -1;
|
||||
|
||||
public void EnterForwardsCompatible() {
|
||||
Debug.Assert(savedErrorCount == -1, "Nested EnterForwardsCompatible calls");
|
||||
savedErrorCount = ErrorCount;
|
||||
}
|
||||
|
||||
// Returns true if no errors were suppressed
|
||||
public bool ExitForwardsCompatible(bool fwdCompat) {
|
||||
Debug.Assert(savedErrorCount != -1, "ExitForwardsCompatible without EnterForwardsCompatible");
|
||||
if (fwdCompat && ErrorCount > savedErrorCount) {
|
||||
ErrorCount = savedErrorCount;
|
||||
Debug.Assert((savedErrorCount = -1) < 0);
|
||||
return false;
|
||||
}
|
||||
Debug.Assert((savedErrorCount = -1) < 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
public CompilerError CreateError(ISourceLineInfo lineInfo, string res, params string[] args) {
|
||||
AddModule(lineInfo.Uri);
|
||||
return new CompilerError(
|
||||
lineInfo.Uri, lineInfo.Start.Line, lineInfo.Start.Pos, /*errorNumber:*/string.Empty,
|
||||
/*errorText:*/XslTransformException.CreateMessage(res, args)
|
||||
);
|
||||
}
|
||||
|
||||
public void ReportError(ISourceLineInfo lineInfo, string res, params string[] args) {
|
||||
CompilerError error = CreateError(lineInfo, res, args);
|
||||
CompilerResults.Errors.Add(error);
|
||||
}
|
||||
|
||||
public void ReportWarning(ISourceLineInfo lineInfo, string res, params string[] args) {
|
||||
int warningLevel = 1;
|
||||
if (0 <= Settings.WarningLevel && Settings.WarningLevel < warningLevel) {
|
||||
// Ignore warning
|
||||
return;
|
||||
}
|
||||
CompilerError error = CreateError(lineInfo, res, args);
|
||||
if (Settings.TreatWarningsAsErrors) {
|
||||
error.ErrorText = XslTransformException.CreateMessage(Res.Xslt_WarningAsError, error.ErrorText);
|
||||
CompilerResults.Errors.Add(error);
|
||||
} else {
|
||||
error.IsWarning = true;
|
||||
CompilerResults.Errors.Add(error);
|
||||
}
|
||||
}
|
||||
|
||||
private void SortErrors() {
|
||||
CompilerErrorCollection errorColl = this.CompilerResults.Errors;
|
||||
if (errorColl.Count > 1) {
|
||||
CompilerError[] errors = new CompilerError[errorColl.Count];
|
||||
errorColl.CopyTo(errors, 0);
|
||||
Array.Sort<CompilerError>(errors, new CompilerErrorComparer(this.moduleOrder));
|
||||
errorColl.Clear();
|
||||
errorColl.AddRange(errors);
|
||||
}
|
||||
}
|
||||
|
||||
private class CompilerErrorComparer : IComparer<CompilerError> {
|
||||
Dictionary<string, int> moduleOrder;
|
||||
|
||||
public CompilerErrorComparer(Dictionary<string, int> moduleOrder) {
|
||||
this.moduleOrder = moduleOrder;
|
||||
}
|
||||
|
||||
public int Compare(CompilerError x, CompilerError y) {
|
||||
if ((object)x == (object)y)
|
||||
return 0;
|
||||
|
||||
if (x == null)
|
||||
return -1;
|
||||
|
||||
if (y == null)
|
||||
return 1;
|
||||
|
||||
int result = moduleOrder[x.FileName].CompareTo(moduleOrder[y.FileName]);
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
result = x.Line.CompareTo(y.Line);
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
result = x.Column.CompareTo(y.Column);
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
result = x.IsWarning.CompareTo(y.IsWarning);
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
result = string.CompareOrdinal(x.ErrorNumber, y.ErrorNumber);
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
return string.CompareOrdinal(x.ErrorText, y.ErrorText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class Output {
|
||||
public XmlWriterSettings Settings;
|
||||
public string Version;
|
||||
public string Encoding;
|
||||
public XmlQualifiedName Method;
|
||||
|
||||
// All the xsl:output elements occurring in a stylesheet are merged into a single effective xsl:output element.
|
||||
// We store the import precedence of each attribute value to catch redefinitions with the same import precedence.
|
||||
public const int NeverDeclaredPrec = int.MinValue;
|
||||
public int MethodPrec = NeverDeclaredPrec;
|
||||
public int VersionPrec = NeverDeclaredPrec;
|
||||
public int EncodingPrec = NeverDeclaredPrec;
|
||||
public int OmitXmlDeclarationPrec = NeverDeclaredPrec;
|
||||
public int StandalonePrec = NeverDeclaredPrec;
|
||||
public int DocTypePublicPrec = NeverDeclaredPrec;
|
||||
public int DocTypeSystemPrec = NeverDeclaredPrec;
|
||||
public int IndentPrec = NeverDeclaredPrec;
|
||||
public int MediaTypePrec = NeverDeclaredPrec;
|
||||
|
||||
public Output() {
|
||||
Settings = new XmlWriterSettings();
|
||||
Settings.OutputMethod = XmlOutputMethod.AutoDetect;
|
||||
Settings.AutoXmlDeclaration = true;
|
||||
Settings.ConformanceLevel = ConformanceLevel.Auto;
|
||||
Settings.MergeCDataSections = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DecimalFormats : KeyedCollection<XmlQualifiedName, DecimalFormatDecl> {
|
||||
protected override XmlQualifiedName GetKeyForItem(DecimalFormatDecl format) {
|
||||
return format.Name;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DecimalFormatDecl {
|
||||
public readonly XmlQualifiedName Name;
|
||||
public readonly string InfinitySymbol;
|
||||
public readonly string NanSymbol;
|
||||
public readonly char[] Characters;
|
||||
|
||||
public static DecimalFormatDecl Default = new DecimalFormatDecl(new XmlQualifiedName(), "Infinity", "NaN", ".,%\u20300#;-");
|
||||
|
||||
public DecimalFormatDecl(XmlQualifiedName name, string infinitySymbol, string nanSymbol, string characters) {
|
||||
Debug.Assert(characters.Length == 8);
|
||||
this.Name = name;
|
||||
this.InfinitySymbol = infinitySymbol;
|
||||
this.NanSymbol = nanSymbol;
|
||||
this.Characters = characters.ToCharArray();
|
||||
}
|
||||
}
|
||||
|
||||
internal class NsAlias {
|
||||
public readonly string ResultNsUri;
|
||||
public readonly string ResultPrefix;
|
||||
public readonly int ImportPrecedence;
|
||||
|
||||
public NsAlias(string resultNsUri, string resultPrefix, int importPrecedence) {
|
||||
this.ResultNsUri = resultNsUri;
|
||||
this.ResultPrefix = resultPrefix;
|
||||
this.ImportPrecedence = importPrecedence;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="CompilerScopeManager.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Xml.Xsl.Xslt {
|
||||
using QilName = System.Xml.Xsl.Qil.QilName;
|
||||
|
||||
// Compiler scope manager keeps track of
|
||||
// Variable declarations
|
||||
// Namespace declarations
|
||||
// Extension and excluded namespaces
|
||||
internal sealed class CompilerScopeManager<V> {
|
||||
public enum ScopeFlags {
|
||||
BackwardCompatibility = 0x1,
|
||||
ForwardCompatibility = 0x2,
|
||||
CanHaveApplyImports = 0x4,
|
||||
NsDecl = 0x10, // NS declaration
|
||||
NsExcl = 0x20, // NS Extencion (null for ExcludeAll)
|
||||
Variable = 0x40,
|
||||
|
||||
CompatibilityFlags = BackwardCompatibility | ForwardCompatibility,
|
||||
InheritedFlags = CompatibilityFlags | CanHaveApplyImports,
|
||||
ExclusiveFlags = NsDecl | NsExcl | Variable
|
||||
}
|
||||
|
||||
public struct ScopeRecord {
|
||||
public int scopeCount;
|
||||
public ScopeFlags flags;
|
||||
public string ncName; // local-name for variable, prefix for namespace, null for extension or excluded namespace
|
||||
public string nsUri; // namespace uri
|
||||
public V value; // value for variable, null for namespace
|
||||
|
||||
// Exactly one of these three properties is true for every given record
|
||||
public bool IsVariable { get { return (flags & ScopeFlags.Variable) != 0; } }
|
||||
public bool IsNamespace { get { return (flags & ScopeFlags.NsDecl ) != 0; } }
|
||||
// public bool IsExNamespace { get { return (flags & ScopeFlags.NsExcl ) != 0; } }
|
||||
}
|
||||
|
||||
// Number of predefined records minus one
|
||||
private const int LastPredefRecord = 0;
|
||||
|
||||
private ScopeRecord[] records = new ScopeRecord[32];
|
||||
private int lastRecord = LastPredefRecord;
|
||||
|
||||
// This is cache of records[lastRecord].scopeCount field;
|
||||
// most often we will have PushScope()/PopScope pare over the same record.
|
||||
// It has sence to avoid adresing this field through array access.
|
||||
private int lastScopes = 0;
|
||||
|
||||
public CompilerScopeManager() {
|
||||
// The prefix 'xml' is by definition bound to the namespace name http://www.w3.org/XML/1998/namespace
|
||||
records[0].flags = ScopeFlags.NsDecl;
|
||||
records[0].ncName = "xml";
|
||||
records[0].nsUri = XmlReservedNs.NsXml;
|
||||
}
|
||||
|
||||
public CompilerScopeManager(KeywordsTable atoms) {
|
||||
records[0].flags = ScopeFlags.NsDecl;
|
||||
records[0].ncName = atoms.Xml;
|
||||
records[0].nsUri = atoms.UriXml;
|
||||
}
|
||||
|
||||
public void EnterScope() {
|
||||
lastScopes++;
|
||||
}
|
||||
|
||||
public void ExitScope() {
|
||||
if (0 < lastScopes) {
|
||||
lastScopes--;
|
||||
} else {
|
||||
while (records[--lastRecord].scopeCount == 0) {
|
||||
}
|
||||
lastScopes = records[lastRecord].scopeCount;
|
||||
lastScopes--;
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public void CheckEmpty() {
|
||||
ExitScope();
|
||||
Debug.Assert(lastRecord == 0 && lastScopes == 0, "PushScope() and PopScope() calls are unbalanced");
|
||||
}
|
||||
|
||||
// returns true if ns decls was added to scope
|
||||
public bool EnterScope(NsDecl nsDecl) {
|
||||
lastScopes++;
|
||||
|
||||
bool hasNamespaces = false;
|
||||
bool excludeAll = false;
|
||||
for (; nsDecl != null; nsDecl = nsDecl.Prev) {
|
||||
if (nsDecl.NsUri == null) {
|
||||
Debug.Assert(nsDecl.Prefix == null, "NS may be null only when prefix is null where it is used for extension-element-prefixes='#all'");
|
||||
excludeAll = true;
|
||||
} else if (nsDecl.Prefix == null) {
|
||||
AddExNamespace(nsDecl.NsUri);
|
||||
} else {
|
||||
hasNamespaces = true;
|
||||
AddNsDeclaration(nsDecl.Prefix, nsDecl.NsUri);
|
||||
}
|
||||
}
|
||||
if (excludeAll) {
|
||||
// #all should be on the top of the stack, becase all NSs on this element should be excluded as well
|
||||
AddExNamespace(null);
|
||||
}
|
||||
return hasNamespaces;
|
||||
}
|
||||
|
||||
private void AddRecord() {
|
||||
// Store cached fields:
|
||||
records[lastRecord].scopeCount = lastScopes;
|
||||
// Extend record buffer:
|
||||
if (++lastRecord == records.Length) {
|
||||
ScopeRecord[] newRecords = new ScopeRecord[lastRecord * 2];
|
||||
Array.Copy(records, 0, newRecords, 0, lastRecord);
|
||||
records = newRecords;
|
||||
}
|
||||
// reset scope count:
|
||||
lastScopes = 0;
|
||||
}
|
||||
|
||||
private void AddRecord(ScopeFlags flag, string ncName, string uri, V value) {
|
||||
Debug.Assert(flag == (flag & ScopeFlags.ExclusiveFlags) && (flag & (flag - 1)) == 0 && flag != 0, "One exclusive flag");
|
||||
Debug.Assert(uri != null || ncName == null, "null, null means exclude '#all'");
|
||||
|
||||
ScopeFlags flags = records[lastRecord].flags;
|
||||
bool canReuseLastRecord = (lastScopes == 0) && (flags & ScopeFlags.ExclusiveFlags) == 0;
|
||||
if (!canReuseLastRecord) {
|
||||
AddRecord();
|
||||
flags &= ScopeFlags.InheritedFlags;
|
||||
}
|
||||
|
||||
records[lastRecord].flags = flags | flag;
|
||||
records[lastRecord].ncName = ncName;
|
||||
records[lastRecord].nsUri = uri;
|
||||
records[lastRecord].value = value;
|
||||
}
|
||||
|
||||
private void SetFlag(ScopeFlags flag, bool value) {
|
||||
Debug.Assert(flag == (flag & ScopeFlags.InheritedFlags) && (flag & (flag - 1)) == 0 && flag != 0, "one inherited flag");
|
||||
ScopeFlags flags = records[lastRecord].flags;
|
||||
if (((flags & flag) != 0) != value) {
|
||||
// lastScopes == records[lastRecord].scopeCount; // we know this because we are cashing it.
|
||||
bool canReuseLastRecord = lastScopes == 0; // last record is from last scope
|
||||
if (!canReuseLastRecord) {
|
||||
AddRecord();
|
||||
flags &= ScopeFlags.InheritedFlags;
|
||||
}
|
||||
if (flag == ScopeFlags.CanHaveApplyImports) {
|
||||
flags ^= flag;
|
||||
} else {
|
||||
flags &= ~ScopeFlags.CompatibilityFlags;
|
||||
if (value) {
|
||||
flags |= flag;
|
||||
}
|
||||
}
|
||||
records[lastRecord].flags = flags;
|
||||
}
|
||||
Debug.Assert((records[lastRecord].flags & ScopeFlags.CompatibilityFlags) != ScopeFlags.CompatibilityFlags,
|
||||
"BackwardCompatibility and ForwardCompatibility flags are mutually exclusive"
|
||||
);
|
||||
}
|
||||
|
||||
// Add variable to the current scope. Returns false in case of duplicates.
|
||||
public void AddVariable(QilName varName, V value) {
|
||||
Debug.Assert(varName.LocalName != null && varName.NamespaceUri != null);
|
||||
AddRecord(ScopeFlags.Variable, varName.LocalName, varName.NamespaceUri, value);
|
||||
}
|
||||
|
||||
// Since the prefix might be redefined in an inner scope, we search in descending order in [to, from]
|
||||
// If interval is empty (from < to), the function returns null.
|
||||
private string LookupNamespace(string prefix, int from, int to) {
|
||||
Debug.Assert(prefix != null);
|
||||
for (int record = from; to <= record; --record) {
|
||||
string recPrefix, recNsUri;
|
||||
ScopeFlags flags = GetName(ref records[record], out recPrefix, out recNsUri);
|
||||
if (
|
||||
(flags & ScopeFlags.NsDecl) != 0 &&
|
||||
recPrefix == prefix
|
||||
) {
|
||||
return recNsUri;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public string LookupNamespace(string prefix) {
|
||||
return LookupNamespace(prefix, lastRecord, 0);
|
||||
}
|
||||
|
||||
private static ScopeFlags GetName(ref ScopeRecord re, out string prefix, out string nsUri) {
|
||||
prefix = re.ncName;
|
||||
nsUri = re.nsUri;
|
||||
return re.flags;
|
||||
}
|
||||
|
||||
public void AddNsDeclaration(string prefix, string nsUri) {
|
||||
AddRecord(ScopeFlags.NsDecl, prefix, nsUri, default(V));
|
||||
}
|
||||
|
||||
public void AddExNamespace(string nsUri) {
|
||||
AddRecord(ScopeFlags.NsExcl, null, nsUri, default(V));
|
||||
}
|
||||
|
||||
public bool IsExNamespace(string nsUri) {
|
||||
Debug.Assert(nsUri != null);
|
||||
int exAll = 0;
|
||||
for (int record = lastRecord; 0 <= record; record--) {
|
||||
string recPrefix, recNsUri;
|
||||
ScopeFlags flags = GetName(ref records[record], out recPrefix, out recNsUri);
|
||||
if ((flags & ScopeFlags.NsExcl) != 0) {
|
||||
Debug.Assert(recPrefix == null);
|
||||
if (recNsUri == nsUri) {
|
||||
return true; // This namespace is excluded
|
||||
}
|
||||
if (recNsUri == null) {
|
||||
exAll = record; // #all namespaces below are excluded
|
||||
}
|
||||
} else if (
|
||||
exAll != 0 &&
|
||||
(flags & ScopeFlags.NsDecl) != 0 &&
|
||||
recNsUri == nsUri
|
||||
) {
|
||||
// We need to check that this namespace wasn't undefined before last "#all"
|
||||
bool undefined = false;
|
||||
for (int prev = record + 1; prev < exAll; prev++) {
|
||||
string prevPrefix, prevNsUri;
|
||||
ScopeFlags prevFlags = GetName(ref records[prev], out prevPrefix, out prevNsUri);
|
||||
if (
|
||||
(flags & ScopeFlags.NsDecl) != 0 &&
|
||||
prevPrefix == recPrefix
|
||||
) {
|
||||
// We don't care if records[prev].nsUri == records[record].nsUri.
|
||||
// In this case the namespace was already undefined above.
|
||||
undefined = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!undefined) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int SearchVariable(string localName, string uri) {
|
||||
Debug.Assert(localName != null);
|
||||
for (int record = lastRecord; 0 <= record; --record) {
|
||||
string recLocal, recNsUri;
|
||||
ScopeFlags flags = GetName(ref records[record], out recLocal, out recNsUri);
|
||||
if (
|
||||
(flags & ScopeFlags.Variable) != 0 &&
|
||||
recLocal == localName &&
|
||||
recNsUri == uri
|
||||
) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public V LookupVariable(string localName, string uri) {
|
||||
int record = SearchVariable(localName, uri);
|
||||
return (record < 0) ? default(V) : records[record].value;
|
||||
}
|
||||
|
||||
public bool IsLocalVariable(string localName, string uri) {
|
||||
int record = SearchVariable(localName, uri);
|
||||
while (0 <= --record) {
|
||||
if (records[record].scopeCount != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool ForwardCompatibility {
|
||||
get { return (records[lastRecord].flags & ScopeFlags.ForwardCompatibility) != 0; }
|
||||
set { SetFlag(ScopeFlags.ForwardCompatibility, value); }
|
||||
}
|
||||
|
||||
public bool BackwardCompatibility {
|
||||
get { return (records[lastRecord].flags & ScopeFlags.BackwardCompatibility) != 0; }
|
||||
set { SetFlag(ScopeFlags.BackwardCompatibility, value); }
|
||||
}
|
||||
|
||||
public bool CanHaveApplyImports {
|
||||
get { return (records[lastRecord].flags & ScopeFlags.CanHaveApplyImports) != 0; }
|
||||
set { SetFlag(ScopeFlags.CanHaveApplyImports, value); }
|
||||
}
|
||||
|
||||
internal System.Collections.Generic.IEnumerable<ScopeRecord> GetActiveRecords() {
|
||||
int currentRecord = this.lastRecord + 1;
|
||||
// This logic comes from NamespaceEnumerator.MoveNext but also returns variables
|
||||
while (LastPredefRecord < --currentRecord) {
|
||||
if (records[currentRecord].IsNamespace) {
|
||||
// This is a namespace declaration
|
||||
if (LookupNamespace(records[currentRecord].ncName, lastRecord, currentRecord + 1) != null) {
|
||||
continue;
|
||||
}
|
||||
// Its prefix has not been redefined later in [currentRecord + 1, lastRecord]
|
||||
}
|
||||
yield return records[currentRecord];
|
||||
}
|
||||
}
|
||||
|
||||
public NamespaceEnumerator GetEnumerator() {
|
||||
return new NamespaceEnumerator(this);
|
||||
}
|
||||
|
||||
internal struct NamespaceEnumerator {
|
||||
CompilerScopeManager<V> scope;
|
||||
int lastRecord;
|
||||
int currentRecord;
|
||||
|
||||
public NamespaceEnumerator(CompilerScopeManager<V> scope) {
|
||||
this.scope = scope;
|
||||
this.lastRecord = scope.lastRecord;
|
||||
this.currentRecord = lastRecord + 1;
|
||||
}
|
||||
|
||||
public void Reset() {
|
||||
currentRecord = lastRecord + 1;
|
||||
}
|
||||
|
||||
public bool MoveNext() {
|
||||
while (LastPredefRecord < --currentRecord) {
|
||||
if (scope.records[currentRecord].IsNamespace) {
|
||||
// This is a namespace declaration
|
||||
if (scope.LookupNamespace(scope.records[currentRecord].ncName, lastRecord, currentRecord + 1) == null) {
|
||||
// Its prefix has not been redefined later in [currentRecord + 1, lastRecord]
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public ScopeRecord Current {
|
||||
get {
|
||||
Debug.Assert(LastPredefRecord <= currentRecord && currentRecord <= scope.lastRecord, "MoveNext() either was not called or returned false");
|
||||
Debug.Assert(scope.records[currentRecord].IsNamespace);
|
||||
return scope.records[currentRecord];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="Focus.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Xml.Xsl.XPath;
|
||||
using System.Xml.Xsl.Qil;
|
||||
|
||||
namespace System.Xml.Xsl.Xslt {
|
||||
using T = XmlQueryTypeFactory;
|
||||
|
||||
// <spec>http://www.w3.org/TR/xslt20/#dt-singleton-focus</spec>
|
||||
internal enum SingletonFocusType {
|
||||
// No context set
|
||||
// Used to prevent bugs
|
||||
None,
|
||||
|
||||
// Document node of the document containing the initial context node
|
||||
// Used while compiling global variables and params
|
||||
InitialDocumentNode,
|
||||
|
||||
// Initial context node for the transformation
|
||||
// Used while compiling initial apply-templates
|
||||
InitialContextNode,
|
||||
|
||||
// Context node is specified by iterator
|
||||
// Used while compiling keys
|
||||
Iterator,
|
||||
}
|
||||
|
||||
internal struct SingletonFocus : IFocus {
|
||||
private XPathQilFactory f;
|
||||
private SingletonFocusType focusType;
|
||||
private QilIterator current;
|
||||
|
||||
public SingletonFocus(XPathQilFactory f) {
|
||||
this.f = f;
|
||||
focusType = SingletonFocusType.None;
|
||||
current = null;
|
||||
}
|
||||
|
||||
public void SetFocus(SingletonFocusType focusType) {
|
||||
Debug.Assert(focusType != SingletonFocusType.Iterator);
|
||||
this.focusType = focusType;
|
||||
}
|
||||
|
||||
public void SetFocus(QilIterator current) {
|
||||
if (current != null) {
|
||||
this.focusType = SingletonFocusType.Iterator;
|
||||
this.current = current;
|
||||
} else {
|
||||
this.focusType = SingletonFocusType.None;
|
||||
this.current = null;
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private void CheckFocus() {
|
||||
Debug.Assert(focusType != SingletonFocusType.None, "Focus is not set, call SetFocus first");
|
||||
}
|
||||
|
||||
public QilNode GetCurrent() {
|
||||
CheckFocus();
|
||||
switch (focusType) {
|
||||
case SingletonFocusType.InitialDocumentNode: return f.Root(f.XmlContext());
|
||||
case SingletonFocusType.InitialContextNode : return f.XmlContext();
|
||||
default:
|
||||
Debug.Assert(focusType == SingletonFocusType.Iterator && current != null, "Unexpected singleton focus type");
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
public QilNode GetPosition() {
|
||||
CheckFocus();
|
||||
return f.Double(1);
|
||||
}
|
||||
|
||||
public QilNode GetLast() {
|
||||
CheckFocus();
|
||||
return f.Double(1);
|
||||
}
|
||||
}
|
||||
|
||||
internal struct FunctionFocus : IFocus {
|
||||
private bool isSet;
|
||||
private QilParameter current, position, last;
|
||||
|
||||
public void StartFocus(IList<QilNode> args, XslFlags flags) {
|
||||
Debug.Assert(! IsFocusSet, "Focus was already set");
|
||||
int argNum = 0;
|
||||
if ((flags & XslFlags.Current) != 0) {
|
||||
this.current = (QilParameter)args[argNum ++];
|
||||
Debug.Assert(this.current.Name.NamespaceUri == XmlReservedNs.NsXslDebug && this.current.Name.LocalName == "current");
|
||||
}
|
||||
if ((flags & XslFlags.Position) != 0) {
|
||||
this.position = (QilParameter)args[argNum ++];
|
||||
Debug.Assert(this.position.Name.NamespaceUri == XmlReservedNs.NsXslDebug && this.position.Name.LocalName == "position");
|
||||
}
|
||||
if ((flags & XslFlags.Last) != 0) {
|
||||
this.last = (QilParameter)args[argNum ++];
|
||||
Debug.Assert(this.last.Name.NamespaceUri == XmlReservedNs.NsXslDebug && this.last.Name.LocalName == "last");
|
||||
}
|
||||
this.isSet = true;
|
||||
}
|
||||
public void StopFocus() {
|
||||
Debug.Assert(IsFocusSet, "Focus was not set");
|
||||
isSet = false;
|
||||
this.current = this.position = this.last = null;
|
||||
}
|
||||
public bool IsFocusSet {
|
||||
get { return this.isSet; }
|
||||
}
|
||||
|
||||
public QilNode GetCurrent() {
|
||||
Debug.Assert(this.current != null, "---- current() is not expected in this function");
|
||||
return this.current;
|
||||
}
|
||||
|
||||
public QilNode GetPosition() {
|
||||
Debug.Assert(this.position != null, "---- position() is not expected in this function");
|
||||
return this.position;
|
||||
}
|
||||
|
||||
public QilNode GetLast() {
|
||||
Debug.Assert(this.last != null, "---- last() is not expected in this function");
|
||||
return this.last;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct LoopFocus : IFocus {
|
||||
private XPathQilFactory f;
|
||||
private QilIterator current, cached, last;
|
||||
|
||||
public LoopFocus(XPathQilFactory f) {
|
||||
this.f = f;
|
||||
current = cached = last = null;
|
||||
}
|
||||
|
||||
public void SetFocus(QilIterator current) {
|
||||
this.current = current;
|
||||
cached = last = null;
|
||||
}
|
||||
|
||||
public bool IsFocusSet {
|
||||
get { return current != null; }
|
||||
}
|
||||
|
||||
public QilNode GetCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public QilNode GetPosition() {
|
||||
return f.XsltConvert(f.PositionOf(current), T.DoubleX);
|
||||
}
|
||||
|
||||
public QilNode GetLast() {
|
||||
if (last == null) {
|
||||
// Create a let that will be fixed up later in ConstructLoop or by LastFixupVisitor
|
||||
last = f.Let(f.Double(0));
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
public void EnsureCache() {
|
||||
if (cached == null) {
|
||||
cached = f.Let(current.Binding);
|
||||
current.Binding = cached;
|
||||
}
|
||||
}
|
||||
|
||||
public void Sort(QilNode sortKeys) {
|
||||
if (sortKeys != null) {
|
||||
// If sorting is required, cache the input node-set to support last() within sort key expressions
|
||||
EnsureCache();
|
||||
// The rest of the loop content must be compiled in the context of already sorted node-set
|
||||
current = f.For(f.Sort(current, sortKeys));
|
||||
}
|
||||
}
|
||||
|
||||
public QilLoop ConstructLoop(QilNode body) {
|
||||
QilLoop result;
|
||||
if (last != null) {
|
||||
// last() encountered either in the sort keys or in the body of the current loop
|
||||
EnsureCache();
|
||||
last.Binding = f.XsltConvert(f.Length(cached), T.DoubleX);
|
||||
}
|
||||
result = f.BaseFactory.Loop(current, body);
|
||||
if (last != null) {
|
||||
result = f.BaseFactory.Loop(last, result);
|
||||
}
|
||||
if (cached != null) {
|
||||
result = f.BaseFactory.Loop(cached, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="IErrorHelper.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">antonl</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Xml.Xsl {
|
||||
|
||||
internal interface IErrorHelper {
|
||||
|
||||
void ReportError(string res, params string[] args);
|
||||
|
||||
void ReportWarning(string res, params string[] args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="InvokeGenerator.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Xml.Xsl.Qil;
|
||||
|
||||
namespace System.Xml.Xsl.Xslt {
|
||||
using T = XmlQueryTypeFactory;
|
||||
|
||||
/**
|
||||
InvokeGenerator is one of the trikest peaces here.
|
||||
ARGS:
|
||||
QilFunction func -- Functions which should be invoked. Arguments of this function (formalArgs) are Let nodes
|
||||
anotated with names and default valies.
|
||||
Problem 1 is that default values can contain references to previouse args of this function.
|
||||
Problem 2 is that default values shouldn't contain fixup nodes.
|
||||
ArrayList actualArgs -- Array of QilNodes anotated with names. When name of formalArg match name actualArg last one
|
||||
is used as invokeArg, otherwise formalArg's default value is cloned and used.
|
||||
**/
|
||||
|
||||
internal class InvokeGenerator : QilCloneVisitor {
|
||||
private bool debug;
|
||||
private Stack<QilIterator> iterStack;
|
||||
|
||||
private QilList formalArgs;
|
||||
private QilList invokeArgs;
|
||||
private int curArg; // this.Clone() depends on this value
|
||||
|
||||
private XsltQilFactory fac;
|
||||
|
||||
public InvokeGenerator(XsltQilFactory f, bool debug) : base(f.BaseFactory) {
|
||||
this.debug = debug;
|
||||
this.fac = f;
|
||||
this.iterStack = new Stack<QilIterator>();
|
||||
}
|
||||
|
||||
public QilNode GenerateInvoke(QilFunction func, IList<XslNode> actualArgs) {
|
||||
iterStack.Clear();
|
||||
formalArgs = func.Arguments;
|
||||
invokeArgs = fac.ActualParameterList();
|
||||
|
||||
// curArg is an instance variable used in Clone() method
|
||||
for (curArg = 0; curArg < formalArgs.Count; curArg ++) {
|
||||
// Find actual value for a given formal arg
|
||||
QilParameter formalArg = (QilParameter)formalArgs[curArg];
|
||||
QilNode invokeArg = FindActualArg(formalArg, actualArgs);
|
||||
|
||||
// If actual value was not specified, use the default value and copy its debug comment
|
||||
if (invokeArg == null) {
|
||||
if (debug) {
|
||||
if (formalArg.Name.NamespaceUri == XmlReservedNs.NsXslDebug) {
|
||||
Debug.Assert(formalArg.Name.LocalName == "namespaces", "Cur,Pos,Last don't have default values and should be always added to by caller in AddImplicitArgs()");
|
||||
Debug.Assert(formalArg.DefaultValue != null, "PrecompileProtoTemplatesHeaders() set it");
|
||||
invokeArg = Clone(formalArg.DefaultValue);
|
||||
} else {
|
||||
invokeArg = fac.DefaultValueMarker();
|
||||
}
|
||||
} else {
|
||||
Debug.Assert(formalArg.Name.NamespaceUri != XmlReservedNs.NsXslDebug, "Cur,Pos,Last don't have default values and should be always added to by caller in AddImplicitArgs(). We don't have $namespaces in !debug.");
|
||||
invokeArg = Clone(formalArg.DefaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
XmlQueryType formalType = formalArg.XmlType;
|
||||
XmlQueryType invokeType = invokeArg.XmlType;
|
||||
|
||||
// Possible arg types: anyType, node-set, string, boolean, and number
|
||||
fac.CheckXsltType(formalArg);
|
||||
fac.CheckXsltType(invokeArg);
|
||||
|
||||
if (!invokeType.IsSubtypeOf(formalType)) {
|
||||
// This may occur only if inferred type of invokeArg is XslFlags.None
|
||||
Debug.Assert(invokeType == T.ItemS, "Actual argument type is not a subtype of formal argument type");
|
||||
invokeArg = fac.TypeAssert(invokeArg, formalType);
|
||||
}
|
||||
|
||||
invokeArgs.Add(invokeArg);
|
||||
}
|
||||
|
||||
// Create Invoke node and wrap it with previous parameter declarations
|
||||
QilNode invoke = fac.Invoke(func, invokeArgs);
|
||||
while (iterStack.Count != 0)
|
||||
invoke = fac.Loop(iterStack.Pop(), invoke);
|
||||
|
||||
return invoke;
|
||||
}
|
||||
|
||||
private QilNode FindActualArg(QilParameter formalArg, IList<XslNode> actualArgs) {
|
||||
QilName argName = formalArg.Name;
|
||||
Debug.Assert(argName != null);
|
||||
foreach (XslNode actualArg in actualArgs) {
|
||||
if (actualArg.Name.Equals(argName)) {
|
||||
return ((VarPar)actualArg).Value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ------------------------------------ QilCloneVisitor -------------------------------------
|
||||
|
||||
protected override QilNode VisitReference(QilNode n) {
|
||||
QilNode replacement = FindClonedReference(n);
|
||||
|
||||
// If the reference is internal for the subtree being cloned, return it as is
|
||||
if (replacement != null) {
|
||||
return replacement;
|
||||
}
|
||||
|
||||
// Replacement was not found, thus the reference is external for the subtree being cloned.
|
||||
// The case when it refers to one of previous arguments (xsl:param can refer to previous
|
||||
// xsl:param's) must be taken care of.
|
||||
for (int prevArg = 0; prevArg < curArg; prevArg++) {
|
||||
Debug.Assert(formalArgs[prevArg] != null, "formalArg must be in the list");
|
||||
Debug.Assert(invokeArgs[prevArg] != null, "This arg should be compiled already");
|
||||
|
||||
// Is this a reference to prevArg?
|
||||
if (n == formalArgs[prevArg]) {
|
||||
// If prevArg is a literal, just clone it
|
||||
if (invokeArgs[prevArg] is QilLiteral) {
|
||||
return invokeArgs[prevArg].ShallowClone(fac.BaseFactory);
|
||||
}
|
||||
|
||||
// If prevArg is not an iterator, cache it in an iterator, and return it
|
||||
if (!(invokeArgs[prevArg] is QilIterator)) {
|
||||
QilIterator var = fac.BaseFactory.Let(invokeArgs[prevArg]);
|
||||
iterStack.Push(var);
|
||||
invokeArgs[prevArg] = var;
|
||||
}
|
||||
Debug.Assert(invokeArgs[prevArg] is QilIterator);
|
||||
return invokeArgs[prevArg];
|
||||
}
|
||||
}
|
||||
|
||||
// This is a truly external reference, return it as is
|
||||
return n;
|
||||
}
|
||||
|
||||
protected override QilNode VisitFunction(QilFunction n) {
|
||||
// No need to change function references
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="KeyMatchBuilder.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using System.Xml.XPath;
|
||||
using MS.Internal.Xml;
|
||||
using System.Xml.Xsl.XPath;
|
||||
using System.Xml.Xsl.Qil;
|
||||
|
||||
namespace System.Xml.Xsl.Xslt {
|
||||
|
||||
internal class KeyMatchBuilder : XPathBuilder, XPathPatternParser.IPatternBuilder {
|
||||
private int depth = 0;
|
||||
PathConvertor convertor;
|
||||
|
||||
public KeyMatchBuilder(IXPathEnvironment env) : base(env) {
|
||||
convertor = new PathConvertor(env.Factory);
|
||||
}
|
||||
|
||||
public override void StartBuild() {
|
||||
Debug.Assert(0 <= depth && depth <= 1, "this shouldn't happen");
|
||||
if (depth == 0) {
|
||||
base.StartBuild();
|
||||
}
|
||||
depth ++;
|
||||
}
|
||||
|
||||
public override QilNode EndBuild(QilNode result) {
|
||||
depth --;
|
||||
Debug.Assert(0 <= depth && depth <= 1, "this shouldn't happen");
|
||||
if (result == null) { // special door to clean builder state in exception handlers
|
||||
return base.EndBuild(result);
|
||||
}
|
||||
if (depth == 0) {
|
||||
Debug.Assert(base.numFixupLast == 0);
|
||||
Debug.Assert(base.numFixupPosition == 0);
|
||||
result = convertor.ConvertReletive2Absolute(result, base.fixupCurrent);
|
||||
result = base.EndBuild(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// -------------------------------------- GetPredicateBuilder() ---------------------------------------
|
||||
|
||||
public virtual IXPathBuilder<QilNode> GetPredicateBuilder(QilNode ctx) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// This code depends on particula shapes that XPathBuilder generates.
|
||||
// It works only on pathes.
|
||||
// ToDo: We can do better here.
|
||||
internal class PathConvertor : QilReplaceVisitor {
|
||||
new XPathQilFactory f;
|
||||
QilNode fixup;
|
||||
public PathConvertor(XPathQilFactory f) : base (f.BaseFactory) {
|
||||
this.f = f;
|
||||
}
|
||||
|
||||
public QilNode ConvertReletive2Absolute(QilNode node, QilNode fixup) {
|
||||
QilDepthChecker.Check(node);
|
||||
Debug.Assert(node != null);
|
||||
Debug.Assert(fixup != null);
|
||||
this.fixup = fixup;
|
||||
return this.Visit(node);
|
||||
}
|
||||
|
||||
// transparantly passing through Union and DocOrder
|
||||
protected override QilNode Visit(QilNode n) {
|
||||
if (
|
||||
n.NodeType == QilNodeType.Union ||
|
||||
n.NodeType == QilNodeType.DocOrderDistinct ||
|
||||
n.NodeType == QilNodeType.Filter ||
|
||||
n.NodeType == QilNodeType.Loop
|
||||
) {
|
||||
return base.Visit(n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
// Filers that travers Content being converted to global travers:
|
||||
// Filter($j= ... Filter($i = Content(fixup), ...)) -> Filter($j= ... Filter($i = Loop($j = DesendentOrSelf(Root(fixup)), Content($j), ...)))
|
||||
protected override QilNode VisitLoop(QilLoop n) {
|
||||
if (n.Variable.Binding.NodeType == QilNodeType.Root || n.Variable.Binding.NodeType == QilNodeType.Deref) {
|
||||
// This is absolute path already. We shouldn't touch it
|
||||
return n;
|
||||
}
|
||||
if (n.Variable.Binding.NodeType == QilNodeType.Content) {
|
||||
// This is "begin" of reletive path. Let's rewrite it as absolute:
|
||||
QilUnary content = (QilUnary)n.Variable.Binding;
|
||||
Debug.Assert(content.Child == this.fixup, "Unexpected content node");
|
||||
QilIterator it = f.For(f.DescendantOrSelf(f.Root(this.fixup)));
|
||||
content.Child = it;
|
||||
n.Variable.Binding = f.Loop(it, content);
|
||||
return n;
|
||||
}
|
||||
n.Variable.Binding = Visit(n.Variable.Binding);
|
||||
return n;
|
||||
}
|
||||
|
||||
protected override QilNode VisitFilter(QilLoop n) {
|
||||
return VisitLoop(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="Keywords.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Xml;
|
||||
|
||||
namespace System.Xml.Xsl.Xslt {
|
||||
|
||||
internal class KeywordsTable {
|
||||
public XmlNameTable NameTable;
|
||||
public string AnalyzeString;
|
||||
public string ApplyImports;
|
||||
public string ApplyTemplates;
|
||||
public string Assembly;
|
||||
public string Attribute;
|
||||
public string AttributeSet;
|
||||
public string CallTemplate;
|
||||
public string CaseOrder;
|
||||
public string CDataSectionElements;
|
||||
public string Character;
|
||||
public string CharacterMap;
|
||||
public string Choose;
|
||||
public string Comment;
|
||||
public string Copy;
|
||||
public string CopyOf;
|
||||
public string Count;
|
||||
public string DataType;
|
||||
public string DecimalFormat;
|
||||
public string DecimalSeparator;
|
||||
public string DefaultCollation;
|
||||
public string DefaultValidation;
|
||||
public string Digit;
|
||||
public string DisableOutputEscaping;
|
||||
public string DocTypePublic;
|
||||
public string DocTypeSystem;
|
||||
public string Document;
|
||||
public string Element;
|
||||
public string Elements;
|
||||
public string Encoding;
|
||||
public string ExcludeResultPrefixes;
|
||||
public string ExtensionElementPrefixes;
|
||||
public string Fallback;
|
||||
public string ForEach;
|
||||
public string ForEachGroup;
|
||||
public string Format;
|
||||
public string From;
|
||||
public string Function;
|
||||
public string GroupingSeparator;
|
||||
public string GroupingSize;
|
||||
public string Href;
|
||||
public string Id;
|
||||
public string If;
|
||||
public string ImplementsPrefix;
|
||||
public string Import;
|
||||
public string ImportSchema;
|
||||
public string Include;
|
||||
public string Indent;
|
||||
public string Infinity;
|
||||
public string Key;
|
||||
public string Lang;
|
||||
public string Language;
|
||||
public string LetterValue;
|
||||
public string Level;
|
||||
public string Match;
|
||||
public string MatchingSubstring;
|
||||
public string MediaType;
|
||||
public string Message;
|
||||
public string Method;
|
||||
public string MinusSign;
|
||||
public string Mode;
|
||||
public string Name;
|
||||
public string Namespace;
|
||||
public string NamespaceAlias;
|
||||
public string NaN;
|
||||
public string NextMatch;
|
||||
public string NonMatchingSubstring;
|
||||
public string Number;
|
||||
public string OmitXmlDeclaration;
|
||||
public string Order;
|
||||
public string Otherwise;
|
||||
public string Output;
|
||||
public string OutputCharacter;
|
||||
public string OutputVersion;
|
||||
public string Param;
|
||||
public string PatternSeparator;
|
||||
public string Percent;
|
||||
public string PerformSort;
|
||||
public string PerMille;
|
||||
public string PreserveSpace;
|
||||
public string Priority;
|
||||
public string ProcessingInstruction;
|
||||
public string Required;
|
||||
public string ResultDocument;
|
||||
public string ResultPrefix;
|
||||
public string Script;
|
||||
public string Select;
|
||||
public string Separator;
|
||||
public string Sequence;
|
||||
public string Sort;
|
||||
public string Space;
|
||||
public string Standalone;
|
||||
public string StripSpace;
|
||||
public string Stylesheet;
|
||||
public string StylesheetPrefix;
|
||||
public string Template;
|
||||
public string Terminate;
|
||||
public string Test;
|
||||
public string Text;
|
||||
public string Transform;
|
||||
public string UrnMsxsl;
|
||||
public string UriXml;
|
||||
public string UriXsl;
|
||||
public string UriWdXsl;
|
||||
public string Use;
|
||||
public string UseAttributeSets;
|
||||
public string UseWhen;
|
||||
public string Using;
|
||||
public string Value;
|
||||
public string ValueOf;
|
||||
public string Variable;
|
||||
public string Version;
|
||||
public string When;
|
||||
public string WithParam;
|
||||
public string Xml;
|
||||
public string Xmlns;
|
||||
public string XPathDefaultNamespace;
|
||||
public string ZeroDigit;
|
||||
|
||||
public KeywordsTable(XmlNameTable nt) {
|
||||
this.NameTable = nt;
|
||||
AnalyzeString = nt.Add("analyze-string");
|
||||
ApplyImports = nt.Add("apply-imports");
|
||||
ApplyTemplates = nt.Add("apply-templates");
|
||||
Assembly = nt.Add("assembly");
|
||||
Attribute = nt.Add("attribute");
|
||||
AttributeSet = nt.Add("attribute-set");
|
||||
CallTemplate = nt.Add("call-template");
|
||||
CaseOrder = nt.Add("case-order");
|
||||
CDataSectionElements = nt.Add("cdata-section-elements");
|
||||
Character = nt.Add("character");
|
||||
CharacterMap = nt.Add("character-map");
|
||||
Choose = nt.Add("choose");
|
||||
Comment = nt.Add("comment");
|
||||
Copy = nt.Add("copy");
|
||||
CopyOf = nt.Add("copy-of");
|
||||
Count = nt.Add("count");
|
||||
DataType = nt.Add("data-type");
|
||||
DecimalFormat = nt.Add("decimal-format");
|
||||
DecimalSeparator = nt.Add("decimal-separator");
|
||||
DefaultCollation = nt.Add("default-collation");
|
||||
DefaultValidation = nt.Add("default-validation");
|
||||
Digit = nt.Add("digit");
|
||||
DisableOutputEscaping = nt.Add("disable-output-escaping");
|
||||
DocTypePublic = nt.Add("doctype-public");
|
||||
DocTypeSystem = nt.Add("doctype-system");
|
||||
Document = nt.Add("document");
|
||||
Element = nt.Add("element");
|
||||
Elements = nt.Add("elements");
|
||||
Encoding = nt.Add("encoding");
|
||||
ExcludeResultPrefixes = nt.Add("exclude-result-prefixes");
|
||||
ExtensionElementPrefixes = nt.Add("extension-element-prefixes");
|
||||
Fallback = nt.Add("fallback");
|
||||
ForEach = nt.Add("for-each");
|
||||
ForEachGroup = nt.Add("for-each-group");
|
||||
Format = nt.Add("format");
|
||||
From = nt.Add("from");
|
||||
Function = nt.Add("function");
|
||||
GroupingSeparator = nt.Add("grouping-separator");
|
||||
GroupingSize = nt.Add("grouping-size");
|
||||
Href = nt.Add("href");
|
||||
Id = nt.Add("id");
|
||||
If = nt.Add("if");
|
||||
ImplementsPrefix = nt.Add("implements-prefix");
|
||||
Import = nt.Add("import");
|
||||
ImportSchema = nt.Add("import-schema");
|
||||
Include = nt.Add("include");
|
||||
Indent = nt.Add("indent");
|
||||
Infinity = nt.Add("infinity");
|
||||
Key = nt.Add("key");
|
||||
Lang = nt.Add("lang");
|
||||
Language = nt.Add("language");
|
||||
LetterValue = nt.Add("letter-value");
|
||||
Level = nt.Add("level");
|
||||
Match = nt.Add("match");
|
||||
MatchingSubstring = nt.Add("matching-substring");
|
||||
MediaType = nt.Add("media-type");
|
||||
Message = nt.Add("message");
|
||||
Method = nt.Add("method");
|
||||
MinusSign = nt.Add("minus-sign");
|
||||
Mode = nt.Add("mode");
|
||||
Name = nt.Add("name");
|
||||
Namespace = nt.Add("namespace");
|
||||
NamespaceAlias = nt.Add("namespace-alias");
|
||||
NaN = nt.Add("NaN");
|
||||
NextMatch = nt.Add("next-match");
|
||||
NonMatchingSubstring = nt.Add("non-matching-substring");
|
||||
Number = nt.Add("number");
|
||||
OmitXmlDeclaration = nt.Add("omit-xml-declaration");
|
||||
Otherwise = nt.Add("otherwise");
|
||||
Order = nt.Add("order");
|
||||
Output = nt.Add("output");
|
||||
OutputCharacter = nt.Add("output-character");
|
||||
OutputVersion = nt.Add("output-version");
|
||||
Param = nt.Add("param");
|
||||
PatternSeparator = nt.Add("pattern-separator");
|
||||
Percent = nt.Add("percent");
|
||||
PerformSort = nt.Add("perform-sort");
|
||||
PerMille = nt.Add("per-mille");
|
||||
PreserveSpace = nt.Add("preserve-space");
|
||||
Priority = nt.Add("priority");
|
||||
ProcessingInstruction = nt.Add("processing-instruction");
|
||||
Required = nt.Add("required");
|
||||
ResultDocument = nt.Add("result-document");
|
||||
ResultPrefix = nt.Add("result-prefix");
|
||||
Script = nt.Add("script");
|
||||
Select = nt.Add("select");
|
||||
Separator = nt.Add("separator");
|
||||
Sequence = nt.Add("sequence");
|
||||
Sort = nt.Add("sort");
|
||||
Space = nt.Add("space");
|
||||
Standalone = nt.Add("standalone");
|
||||
StripSpace = nt.Add("strip-space");
|
||||
Stylesheet = nt.Add("stylesheet");
|
||||
StylesheetPrefix = nt.Add("stylesheet-prefix");
|
||||
Template = nt.Add("template");
|
||||
Terminate = nt.Add("terminate");
|
||||
Test = nt.Add("test");
|
||||
Text = nt.Add("text");
|
||||
Transform = nt.Add("transform");
|
||||
UrnMsxsl = nt.Add(XmlReservedNs.NsMsxsl);
|
||||
UriXml = nt.Add(XmlReservedNs.NsXml);
|
||||
UriXsl = nt.Add(XmlReservedNs.NsXslt);
|
||||
UriWdXsl = nt.Add(XmlReservedNs.NsWdXsl);
|
||||
Use = nt.Add("use");
|
||||
UseAttributeSets = nt.Add("use-attribute-sets");
|
||||
UseWhen = nt.Add("use-when");
|
||||
Using = nt.Add("using");
|
||||
Value = nt.Add("value");
|
||||
ValueOf = nt.Add("value-of");
|
||||
Variable = nt.Add("variable");
|
||||
Version = nt.Add("version");
|
||||
When = nt.Add("when");
|
||||
WithParam = nt.Add("with-param");
|
||||
Xml = nt.Add("xml");
|
||||
Xmlns = nt.Add("xmlns");
|
||||
XPathDefaultNamespace = nt.Add("xpath-default-namespace");
|
||||
ZeroDigit = nt.Add("zero-digit");
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,139 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="OutputScopeManager.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Xml;
|
||||
using System.Collections;
|
||||
|
||||
namespace System.Xml.Xsl.Xslt {
|
||||
internal class OutputScopeManager {
|
||||
public struct ScopeReord {
|
||||
public int scopeCount;
|
||||
public string prefix;
|
||||
public string nsUri;
|
||||
}
|
||||
ScopeReord[] records = new ScopeReord[32];
|
||||
int lastRecord = 0;
|
||||
int lastScopes = 0; // This is cash of records[lastRecord].scopeCount field;
|
||||
// most often we will have PushScope()/PopScope pare over the same record.
|
||||
// It has sence to avoid adresing this field through array access.
|
||||
|
||||
public OutputScopeManager() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset() {
|
||||
// AddNamespace(null, null); -- lookup barier
|
||||
records[0].prefix = null;
|
||||
records[0].nsUri = null;
|
||||
PushScope();
|
||||
}
|
||||
|
||||
public void PushScope() {
|
||||
lastScopes ++;
|
||||
}
|
||||
|
||||
public void PopScope() {
|
||||
if (0 < lastScopes) {
|
||||
lastScopes --;
|
||||
}
|
||||
else {
|
||||
while(records[-- lastRecord].scopeCount == 0) ;
|
||||
lastScopes = records[lastRecord].scopeCount;
|
||||
lastScopes --;
|
||||
}
|
||||
}
|
||||
|
||||
// This can be ns declaration or ns exclussion. Las one when prefix == null;
|
||||
public void AddNamespace(string prefix, string uri) {
|
||||
Debug.Assert(prefix != null);
|
||||
Debug.Assert(uri != null);
|
||||
// uri = nameTable.Add(uri);
|
||||
AddRecord(prefix, uri);
|
||||
}
|
||||
|
||||
private void AddRecord(string prefix, string uri) {
|
||||
records[lastRecord].scopeCount = lastScopes;
|
||||
lastRecord ++;
|
||||
if (lastRecord == records.Length) {
|
||||
ScopeReord[] newRecords = new ScopeReord[lastRecord * 2];
|
||||
Array.Copy(records, 0, newRecords, 0, lastRecord);
|
||||
records = newRecords;
|
||||
}
|
||||
lastScopes = 0;
|
||||
records[lastRecord].prefix = prefix;
|
||||
records[lastRecord].nsUri = uri;
|
||||
}
|
||||
|
||||
// There are some cases where we can't predict namespace content. To garantee correct results we should output all
|
||||
// literal namespaces once again.
|
||||
// <xsl:element name="{}" namespace="{}"> all prefixes should be invalidated
|
||||
// <xsl:element name="{}" namespace="FOO"> all prefixes should be invalidated
|
||||
// <xsl:element name="foo:A" namespace="{}"> prefixe "foo" should be invalidated
|
||||
// <xsl:element name="foo:{}" namespace="{}"> prefixe "foo" should be invalidated
|
||||
// <xsl:element name="foo:A" namespace="FOO"> no invalidations reqired
|
||||
// <xsl:attribute name="{}" namespace="FOO"> all prefixes should be invalidated but not default ""
|
||||
// <xsl:attribute name="foo:A" namespace="{}"> all prefixes should be invalidated but not default ""
|
||||
// <xsl:element name="foo:A" namespace="FOO"> We can try to invalidate only foo prefix, but there to many thinks to consider here.
|
||||
// So for now if attribute has non-null namespace it invalidates all prefixes in the
|
||||
// scope of its element.
|
||||
//
|
||||
// <xsl:copy-of select="@*|namespace::*"> all prefixes are invalidated for the current element scope
|
||||
// <xsl:copy-of select="/|*|text()|etc."> no invalidations needed
|
||||
// <xsl:copy> if the node is either attribute or namespace, all prefixes are invalidated for the current element scope
|
||||
// if the node is element, new scope is created, and all prefixes are invalidated
|
||||
// otherwise, no invalidations needed
|
||||
|
||||
//// We need following methods:
|
||||
//public void InvalidatePrefix(string prefix) {
|
||||
// Debug.Assert(prefix != null);
|
||||
// if (LookupNamespace(prefix) == null) { // This is optimisation. May be better just add this record?
|
||||
// return;
|
||||
// }
|
||||
// AddRecord(prefix, null);
|
||||
//}
|
||||
|
||||
public void InvalidateAllPrefixes() {
|
||||
if (records[lastRecord].prefix == null) {
|
||||
return; // Averything was invalidated already. Nothing to do.
|
||||
}
|
||||
AddRecord(null, null);
|
||||
}
|
||||
|
||||
public void InvalidateNonDefaultPrefixes() {
|
||||
string defaultNs = LookupNamespace(string.Empty);
|
||||
if (defaultNs == null) { // We don't know default NS anyway.
|
||||
InvalidateAllPrefixes();
|
||||
}
|
||||
else {
|
||||
if (
|
||||
records[lastRecord ].prefix.Length == 0 &&
|
||||
records[lastRecord - 1].prefix == null
|
||||
) {
|
||||
return; // Averything was already done
|
||||
}
|
||||
AddRecord(null, null);
|
||||
AddRecord(string.Empty, defaultNs);
|
||||
}
|
||||
}
|
||||
|
||||
public string LookupNamespace(string prefix) {
|
||||
Debug.Assert(prefix != null);
|
||||
for (
|
||||
int record = lastRecord; // from last record
|
||||
records[record].prefix != null; // till lookup barrier
|
||||
-- record // in reverce direction
|
||||
) {
|
||||
Debug.Assert(0 < record, "first record is lookup bariaer, so we don't need to check this condition runtime");
|
||||
if (records[record].prefix == prefix) {
|
||||
return records[record].nsUri;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
d29bc0ca3bfac5ac64dab6111fbe9b18fbf1a03d
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,78 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="QilStrConcatenator.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
using System.Diagnostics;
|
||||
using System.Xml;
|
||||
using System.Text;
|
||||
using System.Xml.Schema;
|
||||
using System.Xml.Xsl.XPath;
|
||||
using System.Xml.Xsl.Qil;
|
||||
|
||||
namespace System.Xml.Xsl.Xslt {
|
||||
|
||||
internal class QilStrConcatenator {
|
||||
private XPathQilFactory f;
|
||||
private StringBuilder builder;
|
||||
private QilList concat;
|
||||
private bool inUse = false;
|
||||
|
||||
public QilStrConcatenator(XPathQilFactory f) {
|
||||
this.f = f;
|
||||
builder = new StringBuilder();
|
||||
}
|
||||
|
||||
public void Reset() {
|
||||
Debug.Assert(! inUse);
|
||||
inUse = true;
|
||||
builder.Length = 0;
|
||||
concat = null;
|
||||
}
|
||||
|
||||
private void FlushBuilder() {
|
||||
if (concat == null) {
|
||||
concat = f.BaseFactory.Sequence();
|
||||
}
|
||||
if (builder.Length != 0) {
|
||||
concat.Add(f.String(builder.ToString()));
|
||||
builder.Length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Append(string value) {
|
||||
Debug.Assert(inUse, "Reset() wasn't called");
|
||||
builder.Append(value);
|
||||
}
|
||||
|
||||
public void Append(char value) {
|
||||
Debug.Assert(inUse, "Reset() wasn't called");
|
||||
builder.Append(value);
|
||||
}
|
||||
|
||||
public void Append(QilNode value) {
|
||||
Debug.Assert(inUse, "Reset() wasn't called");
|
||||
if (value != null) {
|
||||
Debug.Assert(value.XmlType.TypeCode == XmlTypeCode.String);
|
||||
if (value.NodeType == QilNodeType.LiteralString) {
|
||||
builder.Append((string)(QilLiteral)value);
|
||||
} else {
|
||||
FlushBuilder();
|
||||
concat.Add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public QilNode ToQil() {
|
||||
Debug.Assert(inUse); // If we want allow multiple calls to ToQil() this logic should be changed
|
||||
inUse = false;
|
||||
if (concat == null) {
|
||||
return f.String(builder.ToString());
|
||||
} else {
|
||||
FlushBuilder();
|
||||
return f.StrConcat(concat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="Scripts.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
// <spec>http://devdiv/Documents/Whidbey/CLR/CurrentSpecs/BCL/CodeDom%20Activation.doc</spec>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.CodeDom;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Configuration;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Security.Permissions;
|
||||
using System.Threading;
|
||||
using System.Xml.Xsl.IlGen;
|
||||
using System.Xml.Xsl.Runtime;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace System.Xml.Xsl.Xslt {
|
||||
using Res = System.Xml.Utils.Res;
|
||||
|
||||
internal class ScriptClass {
|
||||
public string ns;
|
||||
public CompilerInfo compilerInfo;
|
||||
public StringCollection refAssemblies;
|
||||
public StringCollection nsImports;
|
||||
public CodeTypeDeclaration typeDecl;
|
||||
public bool refAssembliesByHref;
|
||||
|
||||
public Dictionary<string, string> scriptUris;
|
||||
|
||||
// These two fields are used to report a compile error when its position is outside
|
||||
// of all user code snippets in the generated temporary file
|
||||
public string endUri;
|
||||
public Location endLoc;
|
||||
|
||||
public ScriptClass(string ns, CompilerInfo compilerInfo) {
|
||||
this.ns = ns;
|
||||
this.compilerInfo = compilerInfo;
|
||||
this.refAssemblies = new StringCollection();
|
||||
this.nsImports = new StringCollection();
|
||||
this.typeDecl = new CodeTypeDeclaration(GenerateUniqueClassName());
|
||||
this.refAssembliesByHref = false;
|
||||
this.scriptUris = new Dictionary<string, string>(
|
||||
#if !FEATURE_CASE_SENSITIVE_FILESYSTEM
|
||||
StringComparer.OrdinalIgnoreCase
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
private static long scriptClassCounter = 0;
|
||||
|
||||
private static string GenerateUniqueClassName() {
|
||||
return "Script" + Interlocked.Increment(ref scriptClassCounter);
|
||||
}
|
||||
|
||||
public void AddScriptBlock(string source, string uriString, int lineNumber, Location end) {
|
||||
CodeSnippetTypeMember scriptSnippet = new CodeSnippetTypeMember(source);
|
||||
string fileName = SourceLineInfo.GetFileName(uriString);
|
||||
if (lineNumber > 0) {
|
||||
scriptSnippet.LinePragma = new CodeLinePragma(fileName, lineNumber);
|
||||
scriptUris[fileName] = uriString;
|
||||
}
|
||||
typeDecl.Members.Add(scriptSnippet);
|
||||
|
||||
this.endUri = uriString;
|
||||
this.endLoc = end;
|
||||
}
|
||||
|
||||
public ISourceLineInfo EndLineInfo {
|
||||
get {
|
||||
return new SourceLineInfo(this.endUri, this.endLoc, this.endLoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class Scripts {
|
||||
private const string ScriptClassesNamespace = "System.Xml.Xsl.CompiledQuery";
|
||||
|
||||
private Compiler compiler;
|
||||
private List<ScriptClass> scriptClasses = new List<ScriptClass>();
|
||||
private Dictionary<string, Type> nsToType = new Dictionary<string, Type>();
|
||||
private XmlExtensionFunctionTable extFuncs = new XmlExtensionFunctionTable();
|
||||
|
||||
public Scripts(Compiler compiler) {
|
||||
this.compiler = compiler;
|
||||
}
|
||||
|
||||
public Dictionary<string, Type> ScriptClasses {
|
||||
get { return nsToType; }
|
||||
}
|
||||
|
||||
public XmlExtensionFunction ResolveFunction(string name, string ns, int numArgs, IErrorHelper errorHelper) {
|
||||
Type type;
|
||||
if (nsToType.TryGetValue(ns, out type)) {
|
||||
try {
|
||||
return extFuncs.Bind(name, ns, numArgs, type, XmlQueryRuntime.EarlyBoundFlags);
|
||||
}
|
||||
catch (XslTransformException e) {
|
||||
errorHelper.ReportError(e.Message);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ScriptClass GetScriptClass(string ns, string language, IErrorHelper errorHelper) {
|
||||
#if CONFIGURATION_DEP
|
||||
CompilerInfo compilerInfo;
|
||||
try {
|
||||
compilerInfo = CodeDomProvider.GetCompilerInfo(language);
|
||||
Debug.Assert(compilerInfo != null);
|
||||
}
|
||||
catch (ConfigurationException) {
|
||||
// There is no CodeDom provider defined for this language
|
||||
errorHelper.ReportError(/*[XT_010]*/Res.Xslt_ScriptInvalidLanguage, language);
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (ScriptClass scriptClass in scriptClasses) {
|
||||
if (ns == scriptClass.ns) {
|
||||
// Use object comparison because CompilerInfo.Equals may throw
|
||||
if (compilerInfo != scriptClass.compilerInfo) {
|
||||
errorHelper.ReportError(/*[XT_011]*/Res.Xslt_ScriptMixedLanguages, ns);
|
||||
return null;
|
||||
}
|
||||
return scriptClass;
|
||||
}
|
||||
}
|
||||
|
||||
ScriptClass newScriptClass = new ScriptClass(ns, compilerInfo);
|
||||
newScriptClass.typeDecl.TypeAttributes = TypeAttributes.Public;
|
||||
scriptClasses.Add(newScriptClass);
|
||||
return newScriptClass;
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------
|
||||
// Compilation
|
||||
//------------------------------------------------
|
||||
|
||||
public void CompileScripts() {
|
||||
List<ScriptClass> scriptsForLang = new List<ScriptClass>();
|
||||
|
||||
for (int i = 0; i < scriptClasses.Count; i++) {
|
||||
// If the script is already compiled, skip it
|
||||
if (scriptClasses[i] == null)
|
||||
continue;
|
||||
|
||||
// Group together scripts with the same CompilerInfo
|
||||
CompilerInfo compilerInfo = scriptClasses[i].compilerInfo;
|
||||
scriptsForLang.Clear();
|
||||
|
||||
for (int j = i; j < scriptClasses.Count; j++) {
|
||||
// Use object comparison because CompilerInfo.Equals may throw
|
||||
if (scriptClasses[j] != null && scriptClasses[j].compilerInfo == compilerInfo) {
|
||||
scriptsForLang.Add(scriptClasses[j]);
|
||||
scriptClasses[j] = null;
|
||||
}
|
||||
}
|
||||
|
||||
Assembly assembly = CompileAssembly(scriptsForLang);
|
||||
|
||||
if (assembly != null) {
|
||||
foreach (ScriptClass script in scriptsForLang) {
|
||||
Type clrType = assembly.GetType(ScriptClassesNamespace + Type.Delimiter + script.typeDecl.Name);
|
||||
if (clrType != null) {
|
||||
nsToType.Add(script.ns, clrType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Namespaces we always import when compiling
|
||||
private static readonly string[] defaultNamespaces = new string[] {
|
||||
"System",
|
||||
"System.Collections",
|
||||
"System.Text",
|
||||
"System.Text.RegularExpressions",
|
||||
"System.Xml",
|
||||
"System.Xml.Xsl",
|
||||
"System.Xml.XPath",
|
||||
};
|
||||
|
||||
// SxS: This method does not take any resource name and does not expose any resources to the caller.
|
||||
// It's OK to suppress the SxS warning.
|
||||
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
|
||||
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
private Assembly CompileAssembly(List<ScriptClass> scriptsForLang) {
|
||||
TempFileCollection allTempFiles = compiler.CompilerResults.TempFiles;
|
||||
CompilerErrorCollection allErrors = compiler.CompilerResults.Errors;
|
||||
ScriptClass lastScript = scriptsForLang[scriptsForLang.Count - 1];
|
||||
CodeDomProvider provider;
|
||||
bool isVB = false;
|
||||
|
||||
try {
|
||||
provider = lastScript.compilerInfo.CreateProvider();
|
||||
}
|
||||
catch (ConfigurationException e) {
|
||||
// The CodeDom provider type could not be located, or some error in machine.config
|
||||
allErrors.Add(compiler.CreateError(lastScript.EndLineInfo, /*[XT_041]*/Res.Xslt_ScriptCompileException, e.Message));
|
||||
return null;
|
||||
}
|
||||
|
||||
#if !FEATURE_PAL // visualbasic
|
||||
isVB = provider is Microsoft.VisualBasic.VBCodeProvider;
|
||||
#endif // !FEATURE_PAL
|
||||
|
||||
CodeCompileUnit[] codeUnits = new CodeCompileUnit[scriptsForLang.Count];
|
||||
CompilerParameters compilParams = lastScript.compilerInfo.CreateDefaultCompilerParameters();
|
||||
|
||||
//
|
||||
|
||||
|
||||
compilParams.ReferencedAssemblies.Add(typeof(System.Xml.Res).Assembly.Location);
|
||||
compilParams.ReferencedAssemblies.Add("System.dll");
|
||||
if (isVB) {
|
||||
compilParams.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll");
|
||||
}
|
||||
|
||||
bool refAssembliesByHref = false;
|
||||
|
||||
for (int idx = 0; idx < scriptsForLang.Count; idx++) {
|
||||
ScriptClass script = scriptsForLang[idx];
|
||||
CodeNamespace scriptNs = new CodeNamespace(ScriptClassesNamespace);
|
||||
|
||||
// Add imported namespaces
|
||||
foreach (string ns in defaultNamespaces) {
|
||||
scriptNs.Imports.Add(new CodeNamespaceImport(ns));
|
||||
}
|
||||
if (isVB) {
|
||||
scriptNs.Imports.Add(new CodeNamespaceImport("Microsoft.VisualBasic"));
|
||||
}
|
||||
foreach (string ns in script.nsImports) {
|
||||
scriptNs.Imports.Add(new CodeNamespaceImport(ns));
|
||||
}
|
||||
|
||||
scriptNs.Types.Add(script.typeDecl);
|
||||
|
||||
CodeCompileUnit unit = new CodeCompileUnit(); {
|
||||
unit.Namespaces.Add(scriptNs);
|
||||
|
||||
if (isVB) {
|
||||
// This settings have sense for Visual Basic only. In future releases we may allow to specify
|
||||
// them explicitly in the msxsl:script element.
|
||||
unit.UserData["AllowLateBound"] = true; // Allow variables to be declared untyped
|
||||
unit.UserData["RequireVariableDeclaration"] = false; // Allow variables to be undeclared
|
||||
}
|
||||
|
||||
// Put SecurityTransparentAttribute and SecurityRulesAttribute on the first CodeCompileUnit only
|
||||
if (idx == 0) {
|
||||
unit.AssemblyCustomAttributes.Add(new CodeAttributeDeclaration("System.Security.SecurityTransparentAttribute"));
|
||||
|
||||
// We want the assemblies generated for scripts to stick to the old security model
|
||||
unit.AssemblyCustomAttributes.Add(
|
||||
new CodeAttributeDeclaration(
|
||||
new CodeTypeReference(typeof(System.Security.SecurityRulesAttribute)),
|
||||
new CodeAttributeArgument(
|
||||
new CodeFieldReferenceExpression(
|
||||
new CodeTypeReferenceExpression(typeof(System.Security.SecurityRuleSet)), "Level1"))));
|
||||
}
|
||||
}
|
||||
|
||||
codeUnits[idx] = unit;
|
||||
foreach (string name in script.refAssemblies) {
|
||||
compilParams.ReferencedAssemblies.Add(name);
|
||||
}
|
||||
|
||||
refAssembliesByHref |= script.refAssembliesByHref;
|
||||
}
|
||||
|
||||
XsltSettings settings = compiler.Settings;
|
||||
compilParams.WarningLevel = settings.WarningLevel >= 0 ? settings.WarningLevel : compilParams.WarningLevel;
|
||||
compilParams.TreatWarningsAsErrors = settings.TreatWarningsAsErrors;
|
||||
compilParams.IncludeDebugInformation = compiler.IsDebug;
|
||||
|
||||
string asmPath = compiler.ScriptAssemblyPath;
|
||||
if (asmPath != null && scriptsForLang.Count < scriptClasses.Count) {
|
||||
asmPath = Path.ChangeExtension(asmPath, "." + GetLanguageName(lastScript.compilerInfo) + Path.GetExtension(asmPath));
|
||||
}
|
||||
compilParams.OutputAssembly = asmPath;
|
||||
|
||||
string tempDir = (settings.TempFiles != null) ? settings.TempFiles.TempDir : null;
|
||||
compilParams.TempFiles = new TempFileCollection(tempDir);
|
||||
|
||||
// We need only .dll and .pdb, but there is no way to specify that
|
||||
bool keepFiles = (compiler.IsDebug && asmPath == null);
|
||||
#if DEBUG
|
||||
keepFiles = keepFiles || XmlILTrace.IsEnabled;
|
||||
#endif
|
||||
keepFiles = keepFiles && !settings.CheckOnly;
|
||||
|
||||
|
||||
compilParams.TempFiles.KeepFiles = keepFiles;
|
||||
|
||||
// If GenerateInMemory == true, then CodeDom loads the compiled assembly using Assembly.Load(byte[])
|
||||
// instead of Assembly.Load(AssemblyName). That means the assembly will be loaded in the anonymous
|
||||
// context (http://blogs.msdn.com/[....]/archive/2003/05/29/57143.aspx), and its dependencies can only
|
||||
// be loaded from the Load context or using AssemblyResolve event. However we want to use the LoadFrom
|
||||
// context to preload all dependencies specified by <ms:assembly href="uri-reference"/>, so we turn off
|
||||
// GenerateInMemory here.
|
||||
compilParams.GenerateInMemory = (asmPath == null && !compiler.IsDebug && !refAssembliesByHref) || settings.CheckOnly;
|
||||
|
||||
CompilerResults results;
|
||||
|
||||
try {
|
||||
results = provider.CompileAssemblyFromDom(compilParams, codeUnits);
|
||||
}
|
||||
catch (ExternalException e) {
|
||||
// Compiler might have created temporary files
|
||||
results = new CompilerResults(compilParams.TempFiles);
|
||||
results.Errors.Add(compiler.CreateError(lastScript.EndLineInfo, /*[XT_041]*/Res.Xslt_ScriptCompileException, e.Message));
|
||||
}
|
||||
|
||||
if (!settings.CheckOnly) {
|
||||
foreach (string fileName in results.TempFiles) {
|
||||
allTempFiles.AddFile(fileName, allTempFiles.KeepFiles);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (CompilerError error in results.Errors) {
|
||||
FixErrorPosition(error, scriptsForLang);
|
||||
compiler.AddModule(error.FileName);
|
||||
}
|
||||
|
||||
allErrors.AddRange(results.Errors);
|
||||
return results.Errors.HasErrors ? null : results.CompiledAssembly;
|
||||
}
|
||||
|
||||
private int assemblyCounter = 0;
|
||||
|
||||
private string GetLanguageName(CompilerInfo compilerInfo) {
|
||||
Regex alphaNumeric = new Regex("^[0-9a-zA-Z]+$");
|
||||
foreach (string name in compilerInfo.GetLanguages()) {
|
||||
if (alphaNumeric.IsMatch(name))
|
||||
return name;
|
||||
}
|
||||
return "script" + (++assemblyCounter).ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
// The position of a compile error may be outside of all user code snippets (for example, in case of
|
||||
// unclosed '{'). In that case filename would be the name of the temporary file, and not the name
|
||||
// of the stylesheet file. Exposing the path of the temporary file is considered to be a security issue,
|
||||
// so here we check that filename is amongst user files.
|
||||
private static void FixErrorPosition(CompilerError error, List<ScriptClass> scriptsForLang) {
|
||||
string fileName = error.FileName;
|
||||
string uri;
|
||||
|
||||
foreach (ScriptClass script in scriptsForLang) {
|
||||
// We assume that CodeDom provider returns absolute paths (VSWhidbey 289665).
|
||||
// Note that casing may be different.
|
||||
if (script.scriptUris.TryGetValue(fileName, out uri)) {
|
||||
// The error position is within one of user stylesheets, its URI may be reported
|
||||
error.FileName = uri;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Error is outside user code snippeets, we should hide filename for security reasons.
|
||||
// Return filename and position of the end of the last script block for the given class.
|
||||
int idx, scriptNumber;
|
||||
ScriptClass errScript = scriptsForLang[scriptsForLang.Count - 1];
|
||||
|
||||
// Normally temporary source files are named according to the scheme "<random name>.<script number>.
|
||||
// <language extension>". Try to extract the middle part to find the relevant script class. In case
|
||||
// of a non-standard CodeDomProvider, use the last script class.
|
||||
fileName = Path.GetFileNameWithoutExtension(fileName);
|
||||
if ((idx = fileName.LastIndexOf('.')) >= 0)
|
||||
if (int.TryParse(fileName.Substring(idx + 1), NumberStyles.None, NumberFormatInfo.InvariantInfo, out scriptNumber))
|
||||
if ((uint)scriptNumber < scriptsForLang.Count) {
|
||||
errScript = scriptsForLang[scriptNumber];
|
||||
}
|
||||
|
||||
error.FileName = errScript.endUri;
|
||||
error.Line = errScript.endLoc.Line;
|
||||
error.Column = errScript.endLoc.Pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="Stylesheet.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Xml.Xsl.Qil;
|
||||
|
||||
namespace System.Xml.Xsl.Xslt {
|
||||
|
||||
internal class StylesheetLevel {
|
||||
public Stylesheet[] Imports = null;
|
||||
|
||||
// If (this is Stylesheet) {
|
||||
// ModeFlags and ApplyFunctions are abblout apply-imports
|
||||
// } else {
|
||||
// ModeFlags and ApplyFunctions are abblout apply-templates
|
||||
// }
|
||||
// mode -> FocusFlags; Used to generate and call apply-imports/apply-template functions
|
||||
public Dictionary<QilName, XslFlags> ModeFlags = new Dictionary<QilName, XslFlags>();
|
||||
// mode -> xsl:apply-import functions for that mode
|
||||
public Dictionary<QilName, List<QilFunction>> ApplyFunctions = new Dictionary<QilName, List<QilFunction>>();
|
||||
}
|
||||
|
||||
internal class Stylesheet : StylesheetLevel {
|
||||
private Compiler compiler;
|
||||
public List<Uri> ImportHrefs = new List<Uri>();
|
||||
public List<XslNode> GlobalVarPars = new List<XslNode>();
|
||||
|
||||
// xsl:attribute-set/@name -> AttributeSet
|
||||
public Dictionary<QilName, AttributeSet> AttributeSets = new Dictionary<QilName, AttributeSet>();
|
||||
|
||||
private int importPrecedence;
|
||||
private int orderNumber = 0;
|
||||
|
||||
/*
|
||||
WhitespaceRules[0] - rules with default priority 0
|
||||
WhitespaceRules[1] - rules with default priority -0.25
|
||||
WhitespaceRules[2] - rules with default priority -0.5
|
||||
*/
|
||||
public List<WhitespaceRule>[] WhitespaceRules = new List<WhitespaceRule>[3];
|
||||
|
||||
public List<Template> Templates = new List<Template>(); // Templates defined on this level. Empty for RootLevel.
|
||||
// xsl:template/@mode -> list of @match'es
|
||||
public Dictionary<QilName, List<TemplateMatch>> TemplateMatches = new Dictionary<QilName, List<TemplateMatch>>();
|
||||
|
||||
public void AddTemplateMatch(Template template, QilLoop filter) {
|
||||
List<TemplateMatch> matchesForMode;
|
||||
if (!TemplateMatches.TryGetValue(template.Mode, out matchesForMode)) {
|
||||
matchesForMode = TemplateMatches[template.Mode] = new List<TemplateMatch>();
|
||||
}
|
||||
matchesForMode.Add(new TemplateMatch(template, filter));
|
||||
}
|
||||
|
||||
public void SortTemplateMatches() {
|
||||
foreach (QilName mode in TemplateMatches.Keys) {
|
||||
TemplateMatches[mode].Sort(TemplateMatch.Comparer);
|
||||
}
|
||||
}
|
||||
|
||||
public Stylesheet(Compiler compiler, int importPrecedence) {
|
||||
this.compiler = compiler;
|
||||
this.importPrecedence = importPrecedence;
|
||||
|
||||
WhitespaceRules[0] = new List<WhitespaceRule>();
|
||||
WhitespaceRules[1] = new List<WhitespaceRule>();
|
||||
WhitespaceRules[2] = new List<WhitespaceRule>();
|
||||
}
|
||||
|
||||
public int ImportPrecedence { get { return importPrecedence; } }
|
||||
|
||||
public void AddWhitespaceRule(int index, WhitespaceRule rule) {
|
||||
WhitespaceRules[index].Add(rule);
|
||||
}
|
||||
|
||||
public bool AddVarPar(VarPar var) {
|
||||
Debug.Assert(var.NodeType == XslNodeType.Variable || var.NodeType == XslNodeType.Param);
|
||||
Debug.Assert(var.Name.NamespaceUri != null, "Name must be resolved in XsltLoader");
|
||||
foreach (XslNode prevVar in GlobalVarPars) {
|
||||
if (prevVar.Name.Equals(var.Name)) {
|
||||
// [ERR XT0630] It is a static error if a stylesheet contains more than one binding
|
||||
// of a global variable with the same name and same import precedence, unless it also
|
||||
// contains another binding with the same name and higher import precedence.
|
||||
return compiler.AllGlobalVarPars.ContainsKey(var.Name);
|
||||
}
|
||||
}
|
||||
GlobalVarPars.Add(var);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool AddTemplate(Template template) {
|
||||
Debug.Assert(template.ImportPrecedence == 0);
|
||||
|
||||
template.ImportPrecedence = this.importPrecedence;
|
||||
template.OrderNumber = this.orderNumber++;
|
||||
|
||||
compiler.AllTemplates.Add(template);
|
||||
|
||||
if (template.Name != null) {
|
||||
Template old;
|
||||
if (!compiler.NamedTemplates.TryGetValue(template.Name, out old)) {
|
||||
compiler.NamedTemplates[template.Name] = template;
|
||||
} else {
|
||||
Debug.Assert(template.ImportPrecedence <= old.ImportPrecedence, "Global objects are processed in order of decreasing import precedence");
|
||||
if (old.ImportPrecedence == template.ImportPrecedence) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (template.Match != null) {
|
||||
Templates.Add(template);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="XPathPatternBuilder.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Xml.XPath;
|
||||
using System.Xml.Schema;
|
||||
using System.Xml.Xsl.Qil;
|
||||
using System.Xml.Xsl.XPath;
|
||||
|
||||
namespace System.Xml.Xsl.Xslt {
|
||||
using T = XmlQueryTypeFactory;
|
||||
|
||||
internal class XPathPatternBuilder : XPathPatternParser.IPatternBuilder {
|
||||
private XPathPredicateEnvironment predicateEnvironment;
|
||||
private XPathBuilder predicateBuilder;
|
||||
private bool inTheBuild;
|
||||
private XPathQilFactory f;
|
||||
private QilNode fixupNode;
|
||||
private IXPathEnvironment environment;
|
||||
|
||||
public XPathPatternBuilder(IXPathEnvironment environment) {
|
||||
Debug.Assert(environment != null);
|
||||
this.environment = environment;
|
||||
this.f = environment.Factory;
|
||||
this.predicateEnvironment = new XPathPredicateEnvironment(environment);
|
||||
this.predicateBuilder = new XPathBuilder(predicateEnvironment);
|
||||
|
||||
this.fixupNode = f.Unknown(T.NodeNotRtfS);
|
||||
}
|
||||
|
||||
public QilNode FixupNode {
|
||||
get { return fixupNode; }
|
||||
}
|
||||
|
||||
public virtual void StartBuild() {
|
||||
Debug.Assert(! inTheBuild, "XPathBuilder is buisy!");
|
||||
inTheBuild = true;
|
||||
return;
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
public void AssertFilter(QilLoop filter) {
|
||||
Debug.Assert(filter.NodeType == QilNodeType.Filter, "XPathPatternBuilder expected to generate list of Filters on top level");
|
||||
Debug.Assert(filter.Variable.XmlType.IsSubtypeOf(T.NodeNotRtf));
|
||||
Debug.Assert(filter.Variable.Binding.NodeType == QilNodeType.Unknown); // fixupNode
|
||||
Debug.Assert(filter.Body.XmlType.IsSubtypeOf(T.Boolean));
|
||||
}
|
||||
|
||||
private void FixupFilterBinding(QilLoop filter, QilNode newBinding) {
|
||||
AssertFilter(filter);
|
||||
filter.Variable.Binding = newBinding;
|
||||
}
|
||||
|
||||
public virtual QilNode EndBuild(QilNode result) {
|
||||
Debug.Assert(inTheBuild, "StartBuild() wasn't called");
|
||||
if (result == null) {
|
||||
// Special door to clean builder state in exception handlers
|
||||
}
|
||||
|
||||
// All these variables will be positive for "false() and (. = position() + last())"
|
||||
// since QilPatternFactory eliminates the right operand of 'and'
|
||||
Debug.Assert(predicateEnvironment.numFixupCurrent >= 0, "Context fixup error");
|
||||
Debug.Assert(predicateEnvironment.numFixupPosition >= 0, "Context fixup error");
|
||||
Debug.Assert(predicateEnvironment.numFixupLast >= 0, "Context fixup error");
|
||||
inTheBuild = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
public QilNode Operator(XPathOperator op, QilNode left, QilNode right) {
|
||||
Debug.Assert(op == XPathOperator.Union);
|
||||
Debug.Assert(left != null);
|
||||
Debug.Assert(right != null);
|
||||
// It is important to not create nested lists here
|
||||
Debug.Assert(right.NodeType == QilNodeType.Filter, "LocationPathPattern must be compiled into a filter");
|
||||
if (left.NodeType == QilNodeType.Sequence) {
|
||||
((QilList)left).Add(right);
|
||||
return left;
|
||||
} else {
|
||||
Debug.Assert(left.NodeType == QilNodeType.Filter, "LocationPathPattern must be compiled into a filter");
|
||||
return f.Sequence(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
private static QilLoop BuildAxisFilter(QilPatternFactory f, QilIterator itr, XPathAxis xpathAxis, XPathNodeType nodeType, string name, string nsUri) {
|
||||
QilNode nameTest = (
|
||||
name != null && nsUri != null ? f.Eq(f.NameOf(itr), f.QName(name, nsUri)) : // ns:bar || bar
|
||||
nsUri != null ? f.Eq(f.NamespaceUriOf(itr), f.String(nsUri)) : // ns:*
|
||||
name != null ? f.Eq(f.LocalNameOf(itr), f.String(name)) : // *:foo
|
||||
/*name == nsUri == null*/ f.True() // *
|
||||
);
|
||||
|
||||
XmlNodeKindFlags intersection = XPathBuilder.AxisTypeMask(itr.XmlType.NodeKinds, nodeType, xpathAxis);
|
||||
|
||||
QilNode typeTest = (
|
||||
intersection == 0 ? f.False() : // input & required doesn't intersect
|
||||
intersection == itr.XmlType.NodeKinds ? f.True() : // input is subset of required
|
||||
/*else*/ f.IsType(itr, T.NodeChoice(intersection))
|
||||
);
|
||||
|
||||
QilLoop filter = f.BaseFactory.Filter(itr, f.And(typeTest, nameTest));
|
||||
filter.XmlType = T.PrimeProduct(T.NodeChoice(intersection), filter.XmlType.Cardinality);
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
public QilNode Axis(XPathAxis xpathAxis, XPathNodeType nodeType, string prefix, string name) {
|
||||
Debug.Assert(
|
||||
xpathAxis == XPathAxis.Child ||
|
||||
xpathAxis == XPathAxis.Attribute ||
|
||||
xpathAxis == XPathAxis.DescendantOrSelf ||
|
||||
xpathAxis == XPathAxis.Root
|
||||
);
|
||||
QilLoop result;
|
||||
double priority;
|
||||
switch (xpathAxis) {
|
||||
case XPathAxis.DescendantOrSelf :
|
||||
Debug.Assert(nodeType == XPathNodeType.All && prefix == null && name == null, " // is the only d-o-s axes that we can have in pattern");
|
||||
return f.Nop(this.fixupNode); // We using Nop as a flag that DescendantOrSelf exis was used between steps.
|
||||
case XPathAxis.Root :
|
||||
QilIterator i;
|
||||
result = f.BaseFactory.Filter(i = f.For(fixupNode), f.IsType(i, T.Document));
|
||||
priority = 0.5;
|
||||
break;
|
||||
default :
|
||||
string nsUri = prefix == null ? null : this.environment.ResolvePrefix(prefix);
|
||||
result = BuildAxisFilter(f, f.For(fixupNode), xpathAxis, nodeType, name, nsUri);
|
||||
switch (nodeType) {
|
||||
case XPathNodeType.Element :
|
||||
case XPathNodeType.Attribute :
|
||||
if (name != null) {
|
||||
priority = 0;
|
||||
} else {
|
||||
if (prefix != null) {
|
||||
priority = -0.25;
|
||||
} else {
|
||||
priority = -0.5;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case XPathNodeType.ProcessingInstruction :
|
||||
priority = name != null ? 0 : -0.5;
|
||||
break;
|
||||
default:
|
||||
priority = -0.5;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
SetPriority(result, priority);
|
||||
SetLastParent(result, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// a/b/c -> self::c[parent::b[parent::a]]
|
||||
// a/b//c -> self::c[ancestor::b[parent::a]]
|
||||
// a/b -> self::b[parent::a]
|
||||
// -> JoinStep(Axis('a'), Axis('b'))
|
||||
// -> Filter('b' & Parent(Filter('a')))
|
||||
// a//b
|
||||
// -> JoinStep(Axis('a'), JoingStep(Axis(DescendantOrSelf), Axis('b')))
|
||||
// -> JoinStep(Filter('a'), JoingStep(Nop(null), Filter('b')))
|
||||
// -> JoinStep(Filter('a'), Nop(Filter('b')))
|
||||
// -> Filter('b' & Ancestor(Filter('a')))
|
||||
public QilNode JoinStep(QilNode left, QilNode right) {
|
||||
Debug.Assert(left != null);
|
||||
Debug.Assert(right != null);
|
||||
if (left.NodeType == QilNodeType.Nop) {
|
||||
QilUnary nop = (QilUnary)left;
|
||||
Debug.Assert(nop.Child == this.fixupNode);
|
||||
nop.Child = right; // We use Nop as a flag that DescendantOrSelf axis was used between steps.
|
||||
return nop;
|
||||
}
|
||||
Debug.Assert(GetLastParent(left) == left, "Left is always single axis and never the step");
|
||||
Debug.Assert(left.NodeType == QilNodeType.Filter);
|
||||
CleanAnnotation(left);
|
||||
QilLoop parentFilter = (QilLoop) left;
|
||||
bool ancestor = false; {
|
||||
if (right.NodeType == QilNodeType.Nop) {
|
||||
ancestor = true;
|
||||
QilUnary nop = (QilUnary)right;
|
||||
Debug.Assert(nop.Child != null);
|
||||
right = nop.Child;
|
||||
}
|
||||
}
|
||||
Debug.Assert(right.NodeType == QilNodeType.Filter);
|
||||
QilLoop lastParent = GetLastParent(right);
|
||||
FixupFilterBinding(parentFilter, ancestor ? f.Ancestor(lastParent.Variable) : f.Parent(lastParent.Variable));
|
||||
lastParent.Body = f.And(lastParent.Body, f.Not(f.IsEmpty(parentFilter)));
|
||||
SetPriority(right, 0.5);
|
||||
SetLastParent(right, parentFilter);
|
||||
return right;
|
||||
}
|
||||
|
||||
QilNode IXPathBuilder<QilNode>.Predicate(QilNode node, QilNode condition, bool isReverseStep) {
|
||||
Debug.Assert(false, "Should not call to this function.");
|
||||
return null;
|
||||
}
|
||||
|
||||
//The structure of result is a Filter, variable is current node, body is the match condition.
|
||||
//Previous predicate build logic in XPathPatternBuilder is match from right to left, which have 2^n complexiy when have lots of position predicates. TFS #368771
|
||||
//Now change the logic to: If predicates contains position/last predicates, given the current node, filter out all the nodes that match the predicates,
|
||||
//and then check if current node is in the result set.
|
||||
public QilNode BuildPredicates(QilNode nodeset, List<QilNode> predicates) {
|
||||
//convert predicates to boolean type
|
||||
List<QilNode> convertedPredicates = new List<QilNode>(predicates.Count);
|
||||
foreach (var predicate in predicates) {
|
||||
convertedPredicates.Add(XPathBuilder.PredicateToBoolean(predicate, f, predicateEnvironment));
|
||||
}
|
||||
|
||||
QilLoop nodeFilter = (QilLoop)nodeset;
|
||||
QilIterator current = nodeFilter.Variable;
|
||||
|
||||
//If no last() and position() in predicates, use nodeFilter.Variable to fixup current
|
||||
//because all the predicates only based on the input variable, no matter what other predicates are.
|
||||
if (predicateEnvironment.numFixupLast == 0 && predicateEnvironment.numFixupPosition == 0) {
|
||||
foreach (var predicate in convertedPredicates) {
|
||||
nodeFilter.Body = f.And(nodeFilter.Body, predicate);
|
||||
}
|
||||
nodeFilter.Body = predicateEnvironment.fixupVisitor.Fixup(nodeFilter.Body, current, null);
|
||||
}
|
||||
//If any preidcate contains last() or position() node, then the current node is based on previous predicates,
|
||||
//for instance, a[...][2] is match second node after filter 'a[...]' instead of second 'a'.
|
||||
else {
|
||||
//filter out the siblings
|
||||
QilIterator parentIter = f.For(f.Parent(current));
|
||||
QilNode sibling = f.Content(parentIter);
|
||||
//generate filter based on input filter
|
||||
QilLoop siblingFilter = (QilLoop)nodeset.DeepClone(f.BaseFactory);
|
||||
siblingFilter.Variable.Binding = sibling;
|
||||
siblingFilter = (QilLoop)f.Loop(parentIter, siblingFilter);
|
||||
|
||||
//build predicates from left to right to get all the matching nodes
|
||||
QilNode matchingSet = siblingFilter;
|
||||
foreach (var predicate in convertedPredicates) {
|
||||
matchingSet = XPathBuilder.BuildOnePredicate(matchingSet, predicate, /*isReverseStep*/false,
|
||||
f, predicateEnvironment.fixupVisitor,
|
||||
ref predicateEnvironment.numFixupCurrent, ref predicateEnvironment.numFixupPosition, ref predicateEnvironment.numFixupLast);
|
||||
}
|
||||
|
||||
//check if the matching nodes contains the current node
|
||||
QilIterator matchNodeIter = f.For(matchingSet);
|
||||
QilNode filterCurrent = f.Filter(matchNodeIter, f.Is(matchNodeIter, current));
|
||||
nodeFilter.Body = f.Not(f.IsEmpty(filterCurrent));
|
||||
//for passing type check, explict say the result is target type
|
||||
nodeFilter.Body = f.And(f.IsType(current, nodeFilter.XmlType), nodeFilter.Body);
|
||||
}
|
||||
|
||||
SetPriority(nodeset, 0.5);
|
||||
return nodeset;
|
||||
}
|
||||
|
||||
public QilNode Function(string prefix, string name, IList<QilNode> args) {
|
||||
Debug.Assert(prefix.Length == 0);
|
||||
QilIterator i = f.For(fixupNode);
|
||||
QilNode matches;
|
||||
|
||||
if (name == "id") {
|
||||
Debug.Assert(
|
||||
args.Count == 1 && args[0].NodeType == QilNodeType.LiteralString,
|
||||
"Function id() must have one literal string argument"
|
||||
);
|
||||
matches = f.Id(i, args[0]);
|
||||
} else {
|
||||
Debug.Assert(name == "key", "Unexpected function");
|
||||
Debug.Assert(
|
||||
args.Count == 2 &&
|
||||
args[0].NodeType == QilNodeType.LiteralString && args[1].NodeType == QilNodeType.LiteralString,
|
||||
"Function key() must have two literal string arguments"
|
||||
);
|
||||
matches = environment.ResolveFunction(prefix, name, args, new XsltFunctionFocus(i));
|
||||
}
|
||||
|
||||
QilIterator j;
|
||||
QilLoop result = f.BaseFactory.Filter(i, f.Not(f.IsEmpty(f.Filter(j = f.For(matches), f.Is(j, i)))));
|
||||
SetPriority(result, 0.5);
|
||||
SetLastParent(result, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public QilNode String(string value) { return f.String(value); } // As argument of id() or key() function
|
||||
public QilNode Number(double value) { return UnexpectedToken("Literal number"); }
|
||||
public QilNode Variable(string prefix, string name) { return UnexpectedToken("Variable"); }
|
||||
|
||||
private QilNode UnexpectedToken(string tokenName) {
|
||||
string prompt = string.Format(CultureInfo.InvariantCulture, "Internal Error: {0} is not allowed in XSLT pattern outside of predicate.", tokenName);
|
||||
Debug.Assert(false, prompt);
|
||||
throw new Exception(prompt);
|
||||
}
|
||||
|
||||
// -------------------------------------- Priority / Parent ---------------------------------------
|
||||
|
||||
private class Annotation {
|
||||
public double Priority;
|
||||
public QilLoop Parent;
|
||||
}
|
||||
|
||||
public static void SetPriority(QilNode node, double priority) {
|
||||
Annotation ann = (Annotation)node.Annotation ?? new Annotation();
|
||||
ann.Priority = priority;
|
||||
node.Annotation = ann;
|
||||
}
|
||||
|
||||
public static double GetPriority(QilNode node) {
|
||||
return ((Annotation)node.Annotation).Priority;
|
||||
}
|
||||
|
||||
private static void SetLastParent(QilNode node, QilLoop parent) {
|
||||
Debug.Assert(parent.NodeType == QilNodeType.Filter);
|
||||
Annotation ann = (Annotation)node.Annotation ?? new Annotation();
|
||||
ann.Parent = parent;
|
||||
node.Annotation = ann;
|
||||
}
|
||||
|
||||
private static QilLoop GetLastParent(QilNode node) {
|
||||
return ((Annotation)node.Annotation).Parent;
|
||||
}
|
||||
|
||||
public static void CleanAnnotation(QilNode node) {
|
||||
node.Annotation = null;
|
||||
}
|
||||
|
||||
// -------------------------------------- GetPredicateBuilder() ---------------------------------------
|
||||
|
||||
public IXPathBuilder<QilNode> GetPredicateBuilder(QilNode ctx) {
|
||||
QilLoop context = (QilLoop) ctx;
|
||||
Debug.Assert(context != null, "Predicate always has step so it can't have context == null");
|
||||
Debug.Assert(context.Variable.NodeType == QilNodeType.For, "It shouldn't be Let, becaus predicates in PatternBuilder don't produce cached tuples.");
|
||||
return predicateBuilder;
|
||||
}
|
||||
|
||||
private class XPathPredicateEnvironment : IXPathEnvironment {
|
||||
private readonly IXPathEnvironment baseEnvironment;
|
||||
private readonly XPathQilFactory f;
|
||||
public readonly XPathBuilder.FixupVisitor fixupVisitor;
|
||||
private readonly QilNode fixupCurrent, fixupPosition, fixupLast;
|
||||
|
||||
// Number of unresolved fixup nodes
|
||||
public int numFixupCurrent, numFixupPosition, numFixupLast;
|
||||
|
||||
public XPathPredicateEnvironment(IXPathEnvironment baseEnvironment) {
|
||||
this.baseEnvironment = baseEnvironment;
|
||||
this.f = baseEnvironment.Factory;
|
||||
this.fixupCurrent = f.Unknown(T.NodeNotRtf);
|
||||
this.fixupPosition = f.Unknown(T.DoubleX);
|
||||
this.fixupLast = f.Unknown(T.DoubleX);
|
||||
this.fixupVisitor = new XPathBuilder.FixupVisitor(f, fixupCurrent, fixupPosition, fixupLast);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
IXPathEnvironment interface
|
||||
*/
|
||||
public XPathQilFactory Factory { get { return f; } }
|
||||
|
||||
public QilNode ResolveVariable(string prefix, string name) {
|
||||
return baseEnvironment.ResolveVariable(prefix, name);
|
||||
}
|
||||
public QilNode ResolveFunction(string prefix, string name, IList<QilNode> args, IFocus env) {
|
||||
return baseEnvironment.ResolveFunction(prefix, name, args, env);
|
||||
}
|
||||
public string ResolvePrefix(string prefix) {
|
||||
return baseEnvironment.ResolvePrefix(prefix);
|
||||
}
|
||||
|
||||
public QilNode GetCurrent() { numFixupCurrent++; return fixupCurrent; }
|
||||
public QilNode GetPosition() { numFixupPosition++; return fixupPosition; }
|
||||
public QilNode GetLast() { numFixupLast++; return fixupLast; }
|
||||
}
|
||||
|
||||
private class XsltFunctionFocus : IFocus {
|
||||
private QilIterator current;
|
||||
|
||||
public XsltFunctionFocus(QilIterator current) {
|
||||
Debug.Assert(current != null);
|
||||
this.current = current;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
IFocus interface
|
||||
*/
|
||||
public QilNode GetCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public QilNode GetPosition() {
|
||||
Debug.Fail("GetPosition() must not be called");
|
||||
return null;
|
||||
}
|
||||
|
||||
public QilNode GetLast() {
|
||||
Debug.Fail("GetLast() must not be called");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="XPathPatternParser.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Xml;
|
||||
using System.Xml.Xsl.Qil;
|
||||
using System.Xml.Xsl.XPath;
|
||||
|
||||
namespace System.Xml.Xsl.Xslt {
|
||||
using XPathParser = XPathParser<QilNode>;
|
||||
using XPathNodeType = System.Xml.XPath.XPathNodeType;
|
||||
using Res = System.Xml.Utils.Res;
|
||||
|
||||
internal class XPathPatternParser {
|
||||
public interface IPatternBuilder : IXPathBuilder<QilNode> {
|
||||
IXPathBuilder<QilNode> GetPredicateBuilder(QilNode context);
|
||||
}
|
||||
|
||||
XPathScanner scanner;
|
||||
IPatternBuilder ptrnBuilder;
|
||||
XPathParser predicateParser = new XPathParser();
|
||||
|
||||
public QilNode Parse(XPathScanner scanner, IPatternBuilder ptrnBuilder) {
|
||||
Debug.Assert(this.scanner == null && this.ptrnBuilder == null);
|
||||
Debug.Assert(scanner != null && ptrnBuilder != null);
|
||||
QilNode result = null;
|
||||
ptrnBuilder.StartBuild();
|
||||
try {
|
||||
this.scanner = scanner;
|
||||
this.ptrnBuilder = ptrnBuilder;
|
||||
result = this.ParsePattern();
|
||||
this.scanner.CheckToken(LexKind.Eof);
|
||||
} finally {
|
||||
result = ptrnBuilder.EndBuild(result);
|
||||
#if DEBUG
|
||||
this.ptrnBuilder = null;
|
||||
this.scanner = null;
|
||||
#endif
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pattern ::= LocationPathPattern ('|' LocationPathPattern)*
|
||||
*/
|
||||
private QilNode ParsePattern() {
|
||||
QilNode opnd = ParseLocationPathPattern();
|
||||
|
||||
while (scanner.Kind == LexKind.Union) {
|
||||
scanner.NextLex();
|
||||
opnd = ptrnBuilder.Operator(XPathOperator.Union, opnd, ParseLocationPathPattern());
|
||||
}
|
||||
return opnd;
|
||||
}
|
||||
|
||||
/*
|
||||
* LocationPathPattern ::= '/' RelativePathPattern? | '//'? RelativePathPattern | IdKeyPattern (('/' | '//') RelativePathPattern)?
|
||||
*/
|
||||
private QilNode ParseLocationPathPattern() {
|
||||
QilNode opnd;
|
||||
|
||||
switch (scanner.Kind) {
|
||||
case LexKind.Slash :
|
||||
scanner.NextLex();
|
||||
opnd = ptrnBuilder.Axis(XPathAxis.Root, XPathNodeType.All, null, null);
|
||||
|
||||
if (XPathParser.IsStep(scanner.Kind)) {
|
||||
opnd = ptrnBuilder.JoinStep(opnd, ParseRelativePathPattern());
|
||||
}
|
||||
return opnd;
|
||||
case LexKind.SlashSlash :
|
||||
scanner.NextLex();
|
||||
return ptrnBuilder.JoinStep(
|
||||
ptrnBuilder.Axis(XPathAxis.Root, XPathNodeType.All, null, null),
|
||||
ptrnBuilder.JoinStep(
|
||||
ptrnBuilder.Axis(XPathAxis.DescendantOrSelf, XPathNodeType.All, null, null),
|
||||
ParseRelativePathPattern()
|
||||
)
|
||||
);
|
||||
case LexKind.Name :
|
||||
if (scanner.CanBeFunction && scanner.Prefix.Length == 0 && (scanner.Name == "id" || scanner.Name == "key")) {
|
||||
opnd = ParseIdKeyPattern();
|
||||
switch (scanner.Kind) {
|
||||
case LexKind.Slash :
|
||||
scanner.NextLex();
|
||||
opnd = ptrnBuilder.JoinStep(opnd, ParseRelativePathPattern());
|
||||
break;
|
||||
case LexKind.SlashSlash :
|
||||
scanner.NextLex();
|
||||
opnd = ptrnBuilder.JoinStep(opnd,
|
||||
ptrnBuilder.JoinStep(
|
||||
ptrnBuilder.Axis(XPathAxis.DescendantOrSelf, XPathNodeType.All, null, null),
|
||||
ParseRelativePathPattern()
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
return opnd;
|
||||
}
|
||||
break;
|
||||
}
|
||||
opnd = ParseRelativePathPattern();
|
||||
return opnd;
|
||||
}
|
||||
|
||||
/*
|
||||
* IdKeyPattern ::= 'id' '(' Literal ')' | 'key' '(' Literal ',' Literal ')'
|
||||
*/
|
||||
private QilNode ParseIdKeyPattern() {
|
||||
Debug.Assert(scanner.CanBeFunction);
|
||||
Debug.Assert(scanner.Prefix.Length == 0);
|
||||
Debug.Assert(scanner.Name == "id" || scanner.Name == "key");
|
||||
List<QilNode> args = new List<QilNode>(2);
|
||||
|
||||
if (scanner.Name == "id") {
|
||||
scanner.NextLex();
|
||||
scanner.PassToken(LexKind.LParens);
|
||||
scanner.CheckToken(LexKind.String);
|
||||
args.Add(ptrnBuilder.String(scanner.StringValue));
|
||||
scanner.NextLex();
|
||||
scanner.PassToken(LexKind.RParens);
|
||||
return ptrnBuilder.Function("", "id", args);
|
||||
} else {
|
||||
scanner.NextLex();
|
||||
scanner.PassToken(LexKind.LParens);
|
||||
scanner.CheckToken(LexKind.String);
|
||||
args.Add(ptrnBuilder.String(scanner.StringValue));
|
||||
scanner.NextLex();
|
||||
scanner.PassToken(LexKind.Comma);
|
||||
scanner.CheckToken(LexKind.String);
|
||||
args.Add(ptrnBuilder.String(scanner.StringValue));
|
||||
scanner.NextLex();
|
||||
scanner.PassToken(LexKind.RParens);
|
||||
return ptrnBuilder.Function("", "key", args);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* RelativePathPattern ::= StepPattern (('/' | '//') StepPattern)*
|
||||
*/
|
||||
//Max depth to avoid StackOverflow
|
||||
const int MaxParseRelativePathDepth = 1024;
|
||||
private int parseRelativePath = 0;
|
||||
private QilNode ParseRelativePathPattern() {
|
||||
if (++parseRelativePath > MaxParseRelativePathDepth) {
|
||||
if (System.Xml.XmlConfiguration.XsltConfigSection.LimitXPathComplexity) {
|
||||
throw scanner.CreateException(System.Xml.Utils.Res.Xslt_InputTooComplex);
|
||||
}
|
||||
}
|
||||
QilNode opnd = ParseStepPattern();
|
||||
if (scanner.Kind == LexKind.Slash) {
|
||||
scanner.NextLex();
|
||||
opnd = ptrnBuilder.JoinStep(opnd, ParseRelativePathPattern());
|
||||
} else if (scanner.Kind == LexKind.SlashSlash) {
|
||||
scanner.NextLex();
|
||||
opnd = ptrnBuilder.JoinStep(opnd,
|
||||
ptrnBuilder.JoinStep(
|
||||
ptrnBuilder.Axis(XPathAxis.DescendantOrSelf, XPathNodeType.All, null, null),
|
||||
ParseRelativePathPattern()
|
||||
)
|
||||
);
|
||||
}
|
||||
--parseRelativePath;
|
||||
return opnd;
|
||||
}
|
||||
|
||||
/*
|
||||
* StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
|
||||
* ChildOrAttributeAxisSpecifier ::= @ ? | ('child' | 'attribute') '::'
|
||||
*/
|
||||
private QilNode ParseStepPattern() {
|
||||
QilNode opnd;
|
||||
XPathAxis axis;
|
||||
|
||||
switch (scanner.Kind) {
|
||||
case LexKind.Dot:
|
||||
case LexKind.DotDot:
|
||||
throw scanner.CreateException(Res.XPath_InvalidAxisInPattern);
|
||||
case LexKind.At:
|
||||
axis = XPathAxis.Attribute;
|
||||
scanner.NextLex();
|
||||
break;
|
||||
case LexKind.Axis:
|
||||
axis = scanner.Axis;
|
||||
if (axis != XPathAxis.Child && axis != XPathAxis.Attribute) {
|
||||
throw scanner.CreateException(Res.XPath_InvalidAxisInPattern);
|
||||
}
|
||||
scanner.NextLex(); // Skip '::'
|
||||
scanner.NextLex();
|
||||
break;
|
||||
case LexKind.Name:
|
||||
case LexKind.Star:
|
||||
// NodeTest must start with Name or '*'
|
||||
axis = XPathAxis.Child;
|
||||
break;
|
||||
default:
|
||||
throw scanner.CreateException(Res.XPath_UnexpectedToken, scanner.RawValue);
|
||||
}
|
||||
|
||||
XPathNodeType nodeType;
|
||||
string nodePrefix, nodeName;
|
||||
XPathParser.InternalParseNodeTest(scanner, axis, out nodeType, out nodePrefix, out nodeName);
|
||||
opnd = ptrnBuilder.Axis(axis, nodeType, nodePrefix, nodeName);
|
||||
|
||||
XPathPatternBuilder xpathPatternBuilder = ptrnBuilder as XPathPatternBuilder;
|
||||
if (xpathPatternBuilder != null) {
|
||||
//for XPathPatternBuilder, get all predicates and then build them
|
||||
List<QilNode> predicates = new List<QilNode>();
|
||||
while (scanner.Kind == LexKind.LBracket) {
|
||||
predicates.Add(ParsePredicate(opnd));
|
||||
}
|
||||
if (predicates.Count > 0)
|
||||
opnd = xpathPatternBuilder.BuildPredicates(opnd, predicates);
|
||||
}
|
||||
else {
|
||||
while (scanner.Kind == LexKind.LBracket) {
|
||||
opnd = ptrnBuilder.Predicate(opnd, ParsePredicate(opnd), /*reverseStep:*/false);
|
||||
}
|
||||
}
|
||||
return opnd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Predicate ::= '[' Expr ']'
|
||||
*/
|
||||
private QilNode ParsePredicate(QilNode context) {
|
||||
Debug.Assert(scanner.Kind == LexKind.LBracket);
|
||||
scanner.NextLex();
|
||||
QilNode result = predicateParser.Parse(scanner, ptrnBuilder.GetPredicateBuilder(context), LexKind.RBracket);
|
||||
Debug.Assert(scanner.Kind == LexKind.RBracket);
|
||||
scanner.NextLex();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,46 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="XslFlags.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">sdub</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Xml.Xsl {
|
||||
[Flags]
|
||||
internal enum XslFlags {
|
||||
None = 0x0000,
|
||||
|
||||
// XPath types flags. These flags indicate what type the result of the expression may have.
|
||||
String = 0x0001,
|
||||
Number = 0x0002,
|
||||
Boolean = 0x0004,
|
||||
Node = 0x0008,
|
||||
Nodeset = 0x0010,
|
||||
Rtf = 0x0020,
|
||||
TypeFilter = AnyType,
|
||||
AnyType = XslFlags.String | XslFlags.Number | XslFlags.Boolean | XslFlags.Node | XslFlags.Nodeset | XslFlags.Rtf,
|
||||
|
||||
// Focus flags. These flags indicate which of the three focus values (context item, context position,
|
||||
// context size) are required for calculation of the expression.
|
||||
Current = 0x0100,
|
||||
Position = 0x0200,
|
||||
Last = 0x0400,
|
||||
FocusFilter = FullFocus,
|
||||
FullFocus = XslFlags.Current | XslFlags.Position | XslFlags.Last,
|
||||
|
||||
// Indicates that the expression contains at least one of xsl:call-template, xsl:apply-templates,
|
||||
// xsl:apply-imports, [xsl:]use-attribute-sets. Needed for default values of xsl:param's.
|
||||
HasCalls = 0x1000,
|
||||
|
||||
// Used for xsl:param's only. Indicates that at least one caller does not pass value for this param,
|
||||
// so its default value will be used.
|
||||
MayBeDefault = 0x2000,
|
||||
|
||||
// Indicates that expression may produce side effects
|
||||
// This flag is on for xsl:message and for calls to extension functions.
|
||||
SideEffects = 0x4000,
|
||||
|
||||
// Indicates that the corresponding graph vertex has been already visited in flag propagation process.
|
||||
Stop = 0x8000,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="XslVisitor.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
// <owner current="true" primary="true">[....]</owner>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Xml.Xsl.Xslt {
|
||||
|
||||
internal abstract class XslVisitor<T> {
|
||||
protected virtual T Visit(XslNode node) {
|
||||
switch (node.NodeType) {
|
||||
case XslNodeType.ApplyImports : return VisitApplyImports ((XslNode) node);
|
||||
case XslNodeType.ApplyTemplates : return VisitApplyTemplates ((XslNode) node);
|
||||
case XslNodeType.Attribute : return VisitAttribute ((NodeCtor) node);
|
||||
case XslNodeType.AttributeSet : return VisitAttributeSet ((AttributeSet)node);
|
||||
case XslNodeType.CallTemplate : return VisitCallTemplate ((XslNode) node);
|
||||
case XslNodeType.Choose : return VisitChoose ((XslNode) node);
|
||||
case XslNodeType.Comment : return VisitComment ((XslNode) node);
|
||||
case XslNodeType.Copy : return VisitCopy ((XslNode) node);
|
||||
case XslNodeType.CopyOf : return VisitCopyOf ((XslNode) node);
|
||||
case XslNodeType.Element : return VisitElement ((NodeCtor) node);
|
||||
case XslNodeType.Error : return VisitError ((XslNode) node);
|
||||
case XslNodeType.ForEach : return VisitForEach ((XslNode) node);
|
||||
case XslNodeType.If : return VisitIf ((XslNode) node);
|
||||
case XslNodeType.Key : return VisitKey ((Key) node);
|
||||
case XslNodeType.List : return VisitList ((XslNode) node);
|
||||
case XslNodeType.LiteralAttribute : return VisitLiteralAttribute((XslNode) node);
|
||||
case XslNodeType.LiteralElement : return VisitLiteralElement ((XslNode) node);
|
||||
case XslNodeType.Message : return VisitMessage ((XslNode) node);
|
||||
case XslNodeType.Nop : return VisitNop ((XslNode) node);
|
||||
case XslNodeType.Number : return VisitNumber ((Number) node);
|
||||
case XslNodeType.Otherwise : return VisitOtherwise ((XslNode) node);
|
||||
case XslNodeType.Param : return VisitParam ((VarPar) node);
|
||||
case XslNodeType.PI : return VisitPI ((XslNode) node);
|
||||
case XslNodeType.Sort : return VisitSort ((Sort) node);
|
||||
case XslNodeType.Template : return VisitTemplate ((Template) node);
|
||||
case XslNodeType.Text : return VisitText ((Text) node);
|
||||
case XslNodeType.UseAttributeSet : return VisitUseAttributeSet ((XslNode) node);
|
||||
case XslNodeType.ValueOf : return VisitValueOf ((XslNode) node);
|
||||
case XslNodeType.ValueOfDoe : return VisitValueOfDoe ((XslNode) node);
|
||||
case XslNodeType.Variable : return VisitVariable ((VarPar) node);
|
||||
case XslNodeType.WithParam : return VisitWithParam ((VarPar) node);
|
||||
default : return VisitUnknown ((XslNode) node);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual T VisitApplyImports (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitApplyTemplates (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitAttribute (NodeCtor node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitAttributeSet (AttributeSet node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitCallTemplate (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitChoose (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitComment (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitCopy (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitCopyOf (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitElement (NodeCtor node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitError (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitForEach (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitIf (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitKey (Key node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitList (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitLiteralAttribute(XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitLiteralElement (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitMessage (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitNop (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitNumber (Number node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitOtherwise (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitParam (VarPar node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitPI (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitSort (Sort node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitTemplate (Template node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitText (Text node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitUseAttributeSet (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitValueOf (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitValueOfDoe (XslNode node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitVariable (VarPar node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitWithParam (VarPar node) { return VisitChildren( node ); }
|
||||
protected virtual T VisitUnknown (XslNode node) { return VisitChildren( node ); }
|
||||
|
||||
protected virtual T VisitChildren(XslNode node) {
|
||||
foreach (XslNode child in node.Content) {
|
||||
this.Visit(child);
|
||||
}
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user