536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
854 lines
35 KiB
C#
854 lines
35 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="ContainerAction.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
// <owner current="true" primary="true">Microsoft</owner>
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace System.Xml.Xsl.XsltOld {
|
|
using Res = System.Xml.Utils.Res;
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Text;
|
|
using System.Globalization;
|
|
using System.Xml;
|
|
using System.Xml.XPath;
|
|
using System.Xml.Xsl.Runtime;
|
|
using MS.Internal.Xml.XPath;
|
|
using System.Collections;
|
|
using System.Runtime.Versioning;
|
|
|
|
internal class NamespaceInfo {
|
|
internal String prefix;
|
|
internal String nameSpace;
|
|
internal int stylesheetId;
|
|
|
|
internal NamespaceInfo(String prefix, String nameSpace, int stylesheetId) {
|
|
this.prefix = prefix;
|
|
this.nameSpace = nameSpace;
|
|
this.stylesheetId = stylesheetId;
|
|
}
|
|
}
|
|
|
|
internal class ContainerAction : CompiledAction {
|
|
internal ArrayList containedActions;
|
|
internal CopyCodeAction lastCopyCodeAction; // non null if last action is CopyCodeAction;
|
|
|
|
private int maxid = 0;
|
|
|
|
// Local execution states
|
|
protected const int ProcessingChildren = 1;
|
|
|
|
internal override void Compile(Compiler compiler) {
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
internal void CompileStylesheetAttributes(Compiler compiler) {
|
|
NavigatorInput input = compiler.Input;
|
|
string element = input.LocalName;
|
|
string badAttribute = null;
|
|
string version = null;
|
|
|
|
if (input.MoveToFirstAttribute()) {
|
|
do {
|
|
string nspace = input.NamespaceURI;
|
|
string name = input.LocalName;
|
|
|
|
if (nspace.Length != 0) continue;
|
|
|
|
if (Ref.Equal(name, input.Atoms.Version)) {
|
|
version = input.Value;
|
|
if (1 <= XmlConvert.ToXPathDouble(version)) {
|
|
compiler.ForwardCompatibility = (version != "1.0");
|
|
}
|
|
else {
|
|
// XmlConvert.ToXPathDouble(version) an be NaN!
|
|
if (! compiler.ForwardCompatibility) {
|
|
throw XsltException.Create(Res.Xslt_InvalidAttrValue, "version", version);
|
|
}
|
|
}
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.ExtensionElementPrefixes)) {
|
|
compiler.InsertExtensionNamespace(input.Value);
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.ExcludeResultPrefixes)) {
|
|
compiler.InsertExcludedNamespace(input.Value);
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Id)) {
|
|
// Do nothing here.
|
|
}
|
|
else {
|
|
// We can have version atribute later. For now remember this attribute and continue
|
|
badAttribute = name;
|
|
}
|
|
}
|
|
while( input.MoveToNextAttribute());
|
|
input.ToParent();
|
|
}
|
|
|
|
if (version == null) {
|
|
throw XsltException.Create(Res.Xslt_MissingAttribute, "version");
|
|
}
|
|
|
|
if (badAttribute != null && ! compiler.ForwardCompatibility) {
|
|
throw XsltException.Create(Res.Xslt_InvalidAttribute, badAttribute, element);
|
|
}
|
|
}
|
|
|
|
internal void CompileSingleTemplate(Compiler compiler) {
|
|
NavigatorInput input = compiler.Input;
|
|
|
|
//
|
|
// find mandatory version attribute and launch compilation of single template
|
|
//
|
|
|
|
string version = null;
|
|
|
|
if (input.MoveToFirstAttribute()) {
|
|
do {
|
|
string nspace = input.NamespaceURI;
|
|
string name = input.LocalName;
|
|
|
|
if (Ref.Equal(nspace, input.Atoms.UriXsl) &&
|
|
Ref.Equal(name, input.Atoms.Version)) {
|
|
version = input.Value;
|
|
}
|
|
}
|
|
while(input.MoveToNextAttribute());
|
|
input.ToParent();
|
|
}
|
|
|
|
if (version == null) {
|
|
if (Ref.Equal(input.LocalName, input.Atoms.Stylesheet) &&
|
|
input.NamespaceURI == XmlReservedNs.NsWdXsl) {
|
|
throw XsltException.Create(Res.Xslt_WdXslNamespace);
|
|
}
|
|
throw XsltException.Create(Res.Xslt_WrongStylesheetElement);
|
|
}
|
|
|
|
compiler.AddTemplate(compiler.CreateSingleTemplateAction());
|
|
}
|
|
|
|
/*
|
|
* CompileTopLevelElements
|
|
*/
|
|
protected void CompileDocument(Compiler compiler, bool inInclude) {
|
|
NavigatorInput input = compiler.Input;
|
|
|
|
// SkipToElement :
|
|
while (input.NodeType != XPathNodeType.Element) {
|
|
if (! compiler.Advance()) {
|
|
throw XsltException.Create(Res.Xslt_WrongStylesheetElement);
|
|
}
|
|
}
|
|
|
|
Debug.Assert(compiler.Input.NodeType == XPathNodeType.Element);
|
|
if (Ref.Equal(input.NamespaceURI, input.Atoms.UriXsl)) {
|
|
if (
|
|
! Ref.Equal(input.LocalName, input.Atoms.Stylesheet) &&
|
|
! Ref.Equal(input.LocalName, input.Atoms.Transform)
|
|
) {
|
|
throw XsltException.Create(Res.Xslt_WrongStylesheetElement);
|
|
}
|
|
compiler.PushNamespaceScope();
|
|
CompileStylesheetAttributes(compiler);
|
|
CompileTopLevelElements(compiler);
|
|
if (! inInclude) {
|
|
CompileImports(compiler);
|
|
}
|
|
}
|
|
else {
|
|
// single template
|
|
compiler.PushLiteralScope();
|
|
CompileSingleTemplate(compiler);
|
|
}
|
|
|
|
compiler.PopScope();
|
|
}
|
|
|
|
internal Stylesheet CompileImport(Compiler compiler, Uri uri, int id) {
|
|
NavigatorInput input = compiler.ResolveDocument(uri);
|
|
compiler.PushInputDocument(input);
|
|
|
|
try {
|
|
compiler.PushStylesheet(new Stylesheet());
|
|
compiler.Stylesheetid = id;
|
|
CompileDocument(compiler, /*inInclude*/ false);
|
|
}
|
|
catch (XsltCompileException) {
|
|
throw;
|
|
}
|
|
catch (Exception e) {
|
|
throw new XsltCompileException(e, input.BaseURI, input.LineNumber, input.LinePosition);
|
|
}
|
|
finally {
|
|
compiler.PopInputDocument();
|
|
}
|
|
return compiler.PopStylesheet();
|
|
}
|
|
|
|
private void CompileImports(Compiler compiler) {
|
|
ArrayList imports = compiler.CompiledStylesheet.Imports;
|
|
// We can't reverce imports order. Template lookup relyes on it after compilation
|
|
int saveStylesheetId = compiler.Stylesheetid;
|
|
for (int i = imports.Count - 1; 0 <= i; i --) { // Imports should be compiled in reverse order
|
|
Uri uri = imports[i] as Uri;
|
|
Debug.Assert(uri != null);
|
|
imports[i] = CompileImport(compiler, uri, ++ this.maxid);
|
|
}
|
|
compiler.Stylesheetid = saveStylesheetId;
|
|
}
|
|
|
|
// 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.
|
|
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
void CompileInclude(Compiler compiler) {
|
|
Uri uri = compiler.ResolveUri(compiler.GetSingleAttribute(compiler.Input.Atoms.Href));
|
|
string resolved = uri.ToString();
|
|
if (compiler.IsCircularReference(resolved)) {
|
|
throw XsltException.Create(Res.Xslt_CircularInclude, resolved);
|
|
}
|
|
|
|
NavigatorInput input = compiler.ResolveDocument(uri);
|
|
compiler.PushInputDocument(input);
|
|
|
|
try {
|
|
CompileDocument(compiler, /*inInclude*/ true);
|
|
}
|
|
catch (XsltCompileException) {
|
|
throw;
|
|
}
|
|
catch (Exception e) {
|
|
throw new XsltCompileException(e, input.BaseURI, input.LineNumber, input.LinePosition);
|
|
}
|
|
finally {
|
|
compiler.PopInputDocument();
|
|
}
|
|
CheckEmpty(compiler);
|
|
}
|
|
|
|
internal void CompileNamespaceAlias(Compiler compiler) {
|
|
NavigatorInput input = compiler.Input;
|
|
string element = input.LocalName;
|
|
string namespace1 = null, namespace2 = null;
|
|
string prefix1 = null , prefix2 = null;
|
|
if (input.MoveToFirstAttribute()) {
|
|
do {
|
|
string nspace = input.NamespaceURI;
|
|
string name = input.LocalName;
|
|
|
|
if (nspace.Length != 0) continue;
|
|
|
|
if (Ref.Equal(name,input.Atoms.StylesheetPrefix)) {
|
|
prefix1 = input.Value;
|
|
namespace1 = compiler.GetNsAlias(ref prefix1);
|
|
}
|
|
else if (Ref.Equal(name,input.Atoms.ResultPrefix)){
|
|
prefix2 = input.Value;
|
|
namespace2 = compiler.GetNsAlias(ref prefix2);
|
|
}
|
|
else {
|
|
if (! compiler.ForwardCompatibility) {
|
|
throw XsltException.Create(Res.Xslt_InvalidAttribute, name, element);
|
|
}
|
|
}
|
|
}
|
|
while(input.MoveToNextAttribute());
|
|
input.ToParent();
|
|
}
|
|
|
|
CheckRequiredAttribute(compiler, namespace1, "stylesheet-prefix");
|
|
CheckRequiredAttribute(compiler, namespace2, "result-prefix" );
|
|
CheckEmpty(compiler);
|
|
|
|
//String[] resultarray = { prefix2, namespace2 };
|
|
compiler.AddNamespaceAlias( namespace1, new NamespaceInfo(prefix2, namespace2, compiler.Stylesheetid));
|
|
}
|
|
|
|
internal void CompileKey(Compiler compiler){
|
|
NavigatorInput input = compiler.Input;
|
|
string element = input.LocalName;
|
|
int MatchKey = Compiler.InvalidQueryKey;
|
|
int UseKey = Compiler.InvalidQueryKey;
|
|
|
|
XmlQualifiedName Name = null;
|
|
if (input.MoveToFirstAttribute()) {
|
|
do {
|
|
string nspace = input.NamespaceURI;
|
|
string name = input.LocalName;
|
|
string value = input.Value;
|
|
|
|
if (nspace.Length != 0) continue;
|
|
|
|
if (Ref.Equal(name, input.Atoms.Name)) {
|
|
Name = compiler.CreateXPathQName(value);
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Match)) {
|
|
MatchKey = compiler.AddQuery(value, /*allowVars:*/false, /*allowKey*/false, /*pattern*/true);
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Use)) {
|
|
UseKey = compiler.AddQuery(value, /*allowVars:*/false, /*allowKey*/false, /*pattern*/false);
|
|
}
|
|
else {
|
|
if (! compiler.ForwardCompatibility) {
|
|
throw XsltException.Create(Res.Xslt_InvalidAttribute, name, element);
|
|
}
|
|
}
|
|
}
|
|
while(input.MoveToNextAttribute());
|
|
input.ToParent();
|
|
}
|
|
|
|
CheckRequiredAttribute(compiler, MatchKey != Compiler.InvalidQueryKey, "match");
|
|
CheckRequiredAttribute(compiler, UseKey != Compiler.InvalidQueryKey, "use" );
|
|
CheckRequiredAttribute(compiler, Name != null , "name" );
|
|
// It is a breaking change to check for emptiness, SQLBUDT 324364
|
|
//CheckEmpty(compiler);
|
|
|
|
compiler.InsertKey(Name, MatchKey, UseKey);
|
|
}
|
|
|
|
protected void CompileDecimalFormat(Compiler compiler){
|
|
NumberFormatInfo info = new NumberFormatInfo();
|
|
DecimalFormat format = new DecimalFormat(info, '#', '0', ';');
|
|
XmlQualifiedName Name = null;
|
|
NavigatorInput input = compiler.Input;
|
|
if (input.MoveToFirstAttribute()) {
|
|
do {
|
|
if (input.Prefix.Length != 0) continue;
|
|
|
|
string name = input.LocalName;
|
|
string value = input.Value;
|
|
|
|
if (Ref.Equal(name, input.Atoms.Name)) {
|
|
Name = compiler.CreateXPathQName(value);
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.DecimalSeparator)) {
|
|
info.NumberDecimalSeparator = value;
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.GroupingSeparator)) {
|
|
info.NumberGroupSeparator = value;
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Infinity)) {
|
|
info.PositiveInfinitySymbol = value;
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.MinusSign)) {
|
|
info.NegativeSign = value;
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.NaN)) {
|
|
info.NaNSymbol = value;
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Percent)) {
|
|
info.PercentSymbol = value;
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.PerMille)) {
|
|
info.PerMilleSymbol = value;
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Digit)) {
|
|
if (CheckAttribute(value.Length == 1, compiler)) {
|
|
format.digit = value[0];
|
|
}
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.ZeroDigit)) {
|
|
if (CheckAttribute(value.Length == 1, compiler)) {
|
|
format.zeroDigit = value[0];
|
|
}
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.PatternSeparator)) {
|
|
if (CheckAttribute(value.Length == 1, compiler)) {
|
|
format.patternSeparator = value[0];
|
|
}
|
|
}
|
|
}
|
|
while(input.MoveToNextAttribute());
|
|
input.ToParent();
|
|
}
|
|
info.NegativeInfinitySymbol = String.Concat(info.NegativeSign, info.PositiveInfinitySymbol);
|
|
if (Name == null) {
|
|
Name = new XmlQualifiedName();
|
|
}
|
|
compiler.AddDecimalFormat(Name, format);
|
|
CheckEmpty(compiler);
|
|
}
|
|
|
|
internal bool CheckAttribute(bool valid, Compiler compiler) {
|
|
if (! valid) {
|
|
if (! compiler.ForwardCompatibility) {
|
|
throw XsltException.Create(Res.Xslt_InvalidAttrValue, compiler.Input.LocalName, compiler.Input.Value);
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
protected void CompileSpace(Compiler compiler, bool preserve){
|
|
String value = compiler.GetSingleAttribute(compiler.Input.Atoms.Elements);
|
|
String[] elements = XmlConvert.SplitString(value);
|
|
for (int i = 0; i < elements.Length; i++){
|
|
double defaultPriority = NameTest(elements[i]);
|
|
compiler.CompiledStylesheet.AddSpace(compiler, elements[i], defaultPriority, preserve);
|
|
}
|
|
CheckEmpty(compiler);
|
|
}
|
|
|
|
double NameTest(String name) {
|
|
if (name == "*") {
|
|
return -0.5;
|
|
}
|
|
int idx = name.Length - 2;
|
|
if (0 <= idx && name[idx] == ':' && name[idx + 1] == '*') {
|
|
if (! PrefixQName.ValidatePrefix(name.Substring(0, idx))) {
|
|
throw XsltException.Create(Res.Xslt_InvalidAttrValue, "elements", name);
|
|
}
|
|
return -0.25;
|
|
}
|
|
else {
|
|
string prefix, localname;
|
|
PrefixQName.ParseQualifiedName(name, out prefix, out localname);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
|
|
[ResourceExposure(ResourceScope.None)]
|
|
protected void CompileTopLevelElements(Compiler compiler) {
|
|
// Navigator positioned at parent root, need to move to child and then back
|
|
if (compiler.Recurse() == false) {
|
|
return;
|
|
}
|
|
|
|
NavigatorInput input = compiler.Input;
|
|
bool notFirstElement = false;
|
|
do {
|
|
switch (input.NodeType) {
|
|
case XPathNodeType.Element:
|
|
string name = input.LocalName;
|
|
string nspace = input.NamespaceURI;
|
|
|
|
if (Ref.Equal(nspace, input.Atoms.UriXsl)) {
|
|
if (Ref.Equal(name, input.Atoms.Import)) {
|
|
if (notFirstElement) {
|
|
throw XsltException.Create(Res.Xslt_NotFirstImport);
|
|
}
|
|
// We should compile imports in reverse order after all toplevel elements.
|
|
// remember it now and return to it in CompileImpoorts();
|
|
Uri uri = compiler.ResolveUri(compiler.GetSingleAttribute(compiler.Input.Atoms.Href));
|
|
string resolved = uri.ToString();
|
|
if (compiler.IsCircularReference(resolved)) {
|
|
throw XsltException.Create(Res.Xslt_CircularInclude, resolved);
|
|
}
|
|
compiler.CompiledStylesheet.Imports.Add(uri);
|
|
CheckEmpty(compiler);
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Include)) {
|
|
notFirstElement = true;
|
|
CompileInclude(compiler);
|
|
}
|
|
else {
|
|
notFirstElement = true;
|
|
compiler.PushNamespaceScope();
|
|
if (Ref.Equal(name, input.Atoms.StripSpace)) {
|
|
CompileSpace(compiler, false);
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.PreserveSpace)) {
|
|
CompileSpace(compiler, true);
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Output)) {
|
|
CompileOutput(compiler);
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Key)) {
|
|
CompileKey(compiler);
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.DecimalFormat)) {
|
|
CompileDecimalFormat(compiler);
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.NamespaceAlias)) {
|
|
CompileNamespaceAlias(compiler);
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.AttributeSet)) {
|
|
compiler.AddAttributeSet(compiler.CreateAttributeSetAction());
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Variable)) {
|
|
VariableAction action = compiler.CreateVariableAction(VariableType.GlobalVariable);
|
|
if (action != null) {
|
|
AddAction(action);
|
|
}
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Param)) {
|
|
VariableAction action = compiler.CreateVariableAction(VariableType.GlobalParameter);
|
|
if (action != null) {
|
|
AddAction(action);
|
|
}
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Template)) {
|
|
compiler.AddTemplate(compiler.CreateTemplateAction());
|
|
}
|
|
else {
|
|
if (!compiler.ForwardCompatibility) {
|
|
throw compiler.UnexpectedKeyword();
|
|
}
|
|
}
|
|
compiler.PopScope();
|
|
}
|
|
}
|
|
#if !DISABLE_XSLT_SCRIPT
|
|
else if (nspace == input.Atoms.UrnMsxsl && name == input.Atoms.Script) {
|
|
AddScript(compiler);
|
|
}
|
|
#endif
|
|
else {
|
|
if (nspace.Length == 0) {
|
|
throw XsltException.Create(Res.Xslt_NullNsAtTopLevel, input.Name);
|
|
}
|
|
// Ignoring non-recognized namespace per XSLT spec 2.2
|
|
}
|
|
break;
|
|
|
|
case XPathNodeType.ProcessingInstruction:
|
|
case XPathNodeType.Comment:
|
|
case XPathNodeType.Whitespace:
|
|
case XPathNodeType.SignificantWhitespace:
|
|
break;
|
|
|
|
default:
|
|
throw XsltException.Create(Res.Xslt_InvalidContents, "stylesheet");
|
|
}
|
|
}
|
|
while (compiler.Advance());
|
|
|
|
compiler.ToParent();
|
|
}
|
|
|
|
protected void CompileTemplate(Compiler compiler) {
|
|
do {
|
|
CompileOnceTemplate(compiler);
|
|
}
|
|
while (compiler.Advance());
|
|
}
|
|
|
|
protected void CompileOnceTemplate(Compiler compiler) {
|
|
NavigatorInput input = compiler.Input;
|
|
|
|
if (input.NodeType == XPathNodeType.Element) {
|
|
string nspace = input.NamespaceURI;
|
|
|
|
if (Ref.Equal(nspace, input.Atoms.UriXsl)) {
|
|
compiler.PushNamespaceScope();
|
|
CompileInstruction(compiler);
|
|
compiler.PopScope();
|
|
}
|
|
else {
|
|
compiler.PushLiteralScope();
|
|
compiler.InsertExtensionNamespace();
|
|
if (compiler.IsExtensionNamespace(nspace)) {
|
|
AddAction(compiler.CreateNewInstructionAction());
|
|
}
|
|
else {
|
|
CompileLiteral(compiler);
|
|
}
|
|
compiler.PopScope();
|
|
}
|
|
}
|
|
else {
|
|
CompileLiteral(compiler);
|
|
}
|
|
}
|
|
|
|
void CompileInstruction(Compiler compiler) {
|
|
NavigatorInput input = compiler.Input;
|
|
CompiledAction action = null;
|
|
|
|
Debug.Assert(Ref.Equal(input.NamespaceURI, input.Atoms.UriXsl));
|
|
|
|
string name = input.LocalName;
|
|
|
|
if (Ref.Equal(name, input.Atoms.ApplyImports)) {
|
|
action = compiler.CreateApplyImportsAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.ApplyTemplates)) {
|
|
action = compiler.CreateApplyTemplatesAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Attribute)) {
|
|
action = compiler.CreateAttributeAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.CallTemplate)) {
|
|
action = compiler.CreateCallTemplateAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Choose)) {
|
|
action = compiler.CreateChooseAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Comment)) {
|
|
action = compiler.CreateCommentAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Copy)) {
|
|
action = compiler.CreateCopyAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.CopyOf)) {
|
|
action = compiler.CreateCopyOfAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Element)) {
|
|
action = compiler.CreateElementAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Fallback)) {
|
|
return;
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.ForEach)) {
|
|
action = compiler.CreateForEachAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.If)) {
|
|
action = compiler.CreateIfAction(IfAction.ConditionType.ConditionIf);
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Message)) {
|
|
action = compiler.CreateMessageAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Number)) {
|
|
action = compiler.CreateNumberAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.ProcessingInstruction)) {
|
|
action = compiler.CreateProcessingInstructionAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Text)) {
|
|
action = compiler.CreateTextAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.ValueOf)) {
|
|
action = compiler.CreateValueOfAction();
|
|
}
|
|
else if (Ref.Equal(name, input.Atoms.Variable)) {
|
|
action = compiler.CreateVariableAction(VariableType.LocalVariable);
|
|
}
|
|
else {
|
|
if (compiler.ForwardCompatibility)
|
|
action = compiler.CreateNewInstructionAction();
|
|
else
|
|
throw compiler.UnexpectedKeyword();
|
|
}
|
|
|
|
Debug.Assert(action != null);
|
|
|
|
AddAction(action);
|
|
}
|
|
|
|
void CompileLiteral(Compiler compiler) {
|
|
NavigatorInput input = compiler.Input;
|
|
|
|
switch (input.NodeType) {
|
|
case XPathNodeType.Element:
|
|
this.AddEvent(compiler.CreateBeginEvent());
|
|
CompileLiteralAttributesAndNamespaces(compiler);
|
|
|
|
if (compiler.Recurse()) {
|
|
CompileTemplate(compiler);
|
|
compiler.ToParent();
|
|
}
|
|
|
|
this.AddEvent(new EndEvent(XPathNodeType.Element));
|
|
break;
|
|
|
|
case XPathNodeType.Text:
|
|
case XPathNodeType.SignificantWhitespace:
|
|
this.AddEvent(compiler.CreateTextEvent());
|
|
break;
|
|
case XPathNodeType.Whitespace:
|
|
case XPathNodeType.ProcessingInstruction:
|
|
case XPathNodeType.Comment:
|
|
break;
|
|
|
|
default:
|
|
Debug.Assert(false, "Unexpected node type.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CompileLiteralAttributesAndNamespaces(Compiler compiler) {
|
|
NavigatorInput input = compiler.Input;
|
|
|
|
if (input.Navigator.MoveToAttribute("use-attribute-sets", input.Atoms.UriXsl)) {
|
|
AddAction(compiler.CreateUseAttributeSetsAction());
|
|
input.Navigator.MoveToParent();
|
|
}
|
|
compiler.InsertExcludedNamespace();
|
|
|
|
if (input.MoveToFirstNamespace()) {
|
|
do {
|
|
string uri = input.Value;
|
|
|
|
if (uri == XmlReservedNs.NsXslt) {
|
|
continue;
|
|
}
|
|
if (
|
|
compiler.IsExcludedNamespace(uri) ||
|
|
compiler.IsExtensionNamespace(uri) ||
|
|
compiler.IsNamespaceAlias(uri)
|
|
) {
|
|
continue;
|
|
}
|
|
this.AddEvent(new NamespaceEvent(input));
|
|
}
|
|
while (input.MoveToNextNamespace());
|
|
input.ToParent();
|
|
}
|
|
|
|
if (input.MoveToFirstAttribute()) {
|
|
do {
|
|
|
|
// Skip everything from Xslt namespace
|
|
if (Ref.Equal(input.NamespaceURI, input.Atoms.UriXsl)) {
|
|
continue;
|
|
}
|
|
|
|
// Add attribute events
|
|
this.AddEvent (compiler.CreateBeginEvent());
|
|
this.AddEvents(compiler.CompileAvt(input.Value));
|
|
this.AddEvent (new EndEvent(XPathNodeType.Attribute));
|
|
}
|
|
while (input.MoveToNextAttribute());
|
|
input.ToParent();
|
|
}
|
|
}
|
|
|
|
void CompileOutput(Compiler compiler) {
|
|
Debug.Assert((object) this == (object) compiler.RootAction);
|
|
compiler.RootAction.Output.Compile(compiler);
|
|
}
|
|
|
|
internal void AddAction(Action action) {
|
|
if (this.containedActions == null) {
|
|
this.containedActions = new ArrayList();
|
|
}
|
|
this.containedActions.Add(action);
|
|
lastCopyCodeAction = null;
|
|
}
|
|
|
|
private void EnsureCopyCodeAction() {
|
|
if(lastCopyCodeAction == null) {
|
|
CopyCodeAction copyCode = new CopyCodeAction();
|
|
AddAction(copyCode);
|
|
lastCopyCodeAction = copyCode;
|
|
}
|
|
}
|
|
|
|
protected void AddEvent(Event copyEvent) {
|
|
EnsureCopyCodeAction();
|
|
lastCopyCodeAction.AddEvent(copyEvent);
|
|
}
|
|
|
|
protected void AddEvents(ArrayList copyEvents) {
|
|
EnsureCopyCodeAction();
|
|
lastCopyCodeAction.AddEvents(copyEvents);
|
|
}
|
|
|
|
#if !DISABLE_XSLT_SCRIPT
|
|
private void AddScript(Compiler compiler) {
|
|
NavigatorInput input = compiler.Input;
|
|
|
|
ScriptingLanguage lang = ScriptingLanguage.JScript;
|
|
string implementsNamespace = null;
|
|
if (input.MoveToFirstAttribute()) {
|
|
do {
|
|
if (input.LocalName == input.Atoms.Language) {
|
|
string langName = input.Value;
|
|
if (
|
|
String.Compare(langName, "jscript" , StringComparison.OrdinalIgnoreCase) == 0 ||
|
|
String.Compare(langName, "javascript", StringComparison.OrdinalIgnoreCase) == 0
|
|
) {
|
|
lang = ScriptingLanguage.JScript;
|
|
} else if (
|
|
String.Compare(langName, "c#" , StringComparison.OrdinalIgnoreCase) == 0 ||
|
|
String.Compare(langName, "csharp", StringComparison.OrdinalIgnoreCase) == 0
|
|
) {
|
|
lang = ScriptingLanguage.CSharp;
|
|
}
|
|
#if !FEATURE_PAL // visualbasic
|
|
else if (
|
|
String.Compare(langName, "vb" , StringComparison.OrdinalIgnoreCase) == 0 ||
|
|
String.Compare(langName, "visualbasic", StringComparison.OrdinalIgnoreCase) == 0
|
|
) {
|
|
lang = ScriptingLanguage.VisualBasic;
|
|
}
|
|
#endif // !FEATURE_PAL
|
|
else {
|
|
throw XsltException.Create(Res.Xslt_ScriptInvalidLanguage, langName);
|
|
}
|
|
}
|
|
else if (input.LocalName == input.Atoms.ImplementsPrefix) {
|
|
if(! PrefixQName.ValidatePrefix(input.Value)) {
|
|
throw XsltException.Create(Res.Xslt_InvalidAttrValue, input.LocalName, input.Value);
|
|
}
|
|
implementsNamespace = compiler.ResolveXmlNamespace(input.Value);
|
|
}
|
|
}
|
|
while (input.MoveToNextAttribute());
|
|
input.ToParent();
|
|
}
|
|
if (implementsNamespace == null) {
|
|
throw XsltException.Create(Res.Xslt_MissingAttribute, input.Atoms.ImplementsPrefix);
|
|
}
|
|
if (!input.Recurse() || input.NodeType != XPathNodeType.Text) {
|
|
throw XsltException.Create(Res.Xslt_ScriptEmpty);
|
|
}
|
|
compiler.AddScript(input.Value, lang, implementsNamespace, input.BaseURI, input.LineNumber);
|
|
input.ToParent();
|
|
}
|
|
#endif
|
|
|
|
internal override void Execute(Processor processor, ActionFrame frame) {
|
|
Debug.Assert(processor != null && frame != null);
|
|
|
|
switch (frame.State) {
|
|
case Initialized:
|
|
if (this.containedActions != null && this.containedActions.Count > 0) {
|
|
processor.PushActionFrame(frame);
|
|
frame.State = ProcessingChildren;
|
|
}
|
|
else {
|
|
frame.Finished();
|
|
}
|
|
break; // Allow children to run
|
|
|
|
case ProcessingChildren:
|
|
frame.Finished();
|
|
break;
|
|
|
|
default:
|
|
Debug.Fail("Invalid Container action execution state");
|
|
break;
|
|
}
|
|
}
|
|
|
|
internal Action GetAction(int actionIndex) {
|
|
Debug.Assert(actionIndex == 0 || this.containedActions != null);
|
|
|
|
if (this.containedActions != null && actionIndex < this.containedActions.Count) {
|
|
return (Action) this.containedActions[actionIndex];
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal void CheckDuplicateParams(XmlQualifiedName name) {
|
|
if (this.containedActions != null) {
|
|
foreach(CompiledAction action in this.containedActions) {
|
|
WithParamAction param = action as WithParamAction;
|
|
if (param != null && param.Name == name) {
|
|
throw XsltException.Create(Res.Xslt_DuplicateWithParam, name.ToString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override void ReplaceNamespaceAlias(Compiler compiler){
|
|
if (this.containedActions == null) {
|
|
return;
|
|
}
|
|
int count = this.containedActions.Count;
|
|
for(int i= 0; i < this.containedActions.Count; i++) {
|
|
((Action)this.containedActions[i]).ReplaceNamespaceAlias(compiler);
|
|
}
|
|
}
|
|
}
|
|
}
|