e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
202 lines
6.8 KiB
C#
202 lines
6.8 KiB
C#
//------------------------------------------------------------------------------
|
|
// <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;
|
|
}
|
|
}
|
|
}
|