|
//------------------------------------------------------------------------------
// <copyright file="QilTypeChecker.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
//------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Xml.Schema;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.Xml.Xsl.Runtime;
namespace System.Xml.Xsl.Qil {
/// <summary>
/// This class performs two functions:
/// 1. Infer XmlQueryType of Qil nodes (constant, from arguments, etc)
/// 2. Validate the arguments of Qil nodes if DEBUG is defined
/// </summary>
internal class QilTypeChecker {
public QilTypeChecker() {
}
public XmlQueryType Check(QilNode n) {
#region AUTOGENERATED
switch (n.NodeType) {
case QilNodeType.QilExpression: return CheckQilExpression((QilExpression)n);
case QilNodeType.FunctionList: return CheckFunctionList((QilList)n);
case QilNodeType.GlobalVariableList: return CheckGlobalVariableList((QilList)n);
case QilNodeType.GlobalParameterList: return CheckGlobalParameterList((QilList)n);
case QilNodeType.ActualParameterList: return CheckActualParameterList((QilList)n);
case QilNodeType.FormalParameterList: return CheckFormalParameterList((QilList)n);
case QilNodeType.SortKeyList: return CheckSortKeyList((QilList)n);
case QilNodeType.BranchList: return CheckBranchList((QilList)n);
case QilNodeType.OptimizeBarrier: return CheckOptimizeBarrier((QilUnary)n);
case QilNodeType.Unknown: return CheckUnknown(n);
case QilNodeType.DataSource: return CheckDataSource((QilDataSource)n);
case QilNodeType.Nop: return CheckNop((QilUnary)n);
case QilNodeType.Error: return CheckError((QilUnary)n);
case QilNodeType.Warning: return CheckWarning((QilUnary)n);
case QilNodeType.For: return CheckFor((QilIterator)n);
case QilNodeType.Let: return CheckLet((QilIterator)n);
case QilNodeType.Parameter: return CheckParameter((QilParameter)n);
case QilNodeType.PositionOf: return CheckPositionOf((QilUnary)n);
case QilNodeType.True: return CheckTrue(n);
case QilNodeType.False: return CheckFalse(n);
case QilNodeType.LiteralString: return CheckLiteralString((QilLiteral)n);
case QilNodeType.LiteralInt32: return CheckLiteralInt32((QilLiteral)n);
case QilNodeType.LiteralInt64: return CheckLiteralInt64((QilLiteral)n);
case QilNodeType.LiteralDouble: return CheckLiteralDouble((QilLiteral)n);
case QilNodeType.LiteralDecimal: return CheckLiteralDecimal((QilLiteral)n);
case QilNodeType.LiteralQName: return CheckLiteralQName((QilName)n);
case QilNodeType.LiteralType: return CheckLiteralType((QilLiteral)n);
case QilNodeType.LiteralObject: return CheckLiteralObject((QilLiteral)n);
case QilNodeType.And: return CheckAnd((QilBinary)n);
case QilNodeType.Or: return CheckOr((QilBinary)n);
case QilNodeType.Not: return CheckNot((QilUnary)n);
case QilNodeType.Conditional: return CheckConditional((QilTernary)n);
case QilNodeType.Choice: return CheckChoice((QilChoice)n);
case QilNodeType.Length: return CheckLength((QilUnary)n);
case QilNodeType.Sequence: return CheckSequence((QilList)n);
case QilNodeType.Union: return CheckUnion((QilBinary)n);
case QilNodeType.Intersection: return CheckIntersection((QilBinary)n);
case QilNodeType.Difference: return CheckDifference((QilBinary)n);
case QilNodeType.Average: return CheckAverage((QilUnary)n);
case QilNodeType.Sum: return CheckSum((QilUnary)n);
case QilNodeType.Minimum: return CheckMinimum((QilUnary)n);
case QilNodeType.Maximum: return CheckMaximum((QilUnary)n);
case QilNodeType.Negate: return CheckNegate((QilUnary)n);
case QilNodeType.Add: return CheckAdd((QilBinary)n);
case QilNodeType.Subtract: return CheckSubtract((QilBinary)n);
case QilNodeType.Multiply: return CheckMultiply((QilBinary)n);
case QilNodeType.Divide: return CheckDivide((QilBinary)n);
case QilNodeType.Modulo: return CheckModulo((QilBinary)n);
case QilNodeType.StrLength: return CheckStrLength((QilUnary)n);
case QilNodeType.StrConcat: return CheckStrConcat((QilStrConcat)n);
case QilNodeType.StrParseQName: return CheckStrParseQName((QilBinary)n);
case QilNodeType.Ne: return CheckNe((QilBinary)n);
case QilNodeType.Eq: return CheckEq((QilBinary)n);
case QilNodeType.Gt: return CheckGt((QilBinary)n);
case QilNodeType.Ge: return CheckGe((QilBinary)n);
case QilNodeType.Lt: return CheckLt((QilBinary)n);
case QilNodeType.Le: return CheckLe((QilBinary)n);
case QilNodeType.Is: return CheckIs((QilBinary)n);
case QilNodeType.After: return CheckAfter((QilBinary)n);
case QilNodeType.Before: return CheckBefore((QilBinary)n);
case QilNodeType.Loop: return CheckLoop((QilLoop)n);
case QilNodeType.Filter: return CheckFilter((QilLoop)n);
case QilNodeType.Sort: return CheckSort((QilLoop)n);
case QilNodeType.SortKey: return CheckSortKey((QilSortKey)n);
case QilNodeType.DocOrderDistinct: return CheckDocOrderDistinct((QilUnary)n);
case QilNodeType.Function: return CheckFunction((QilFunction)n);
case QilNodeType.Invoke: return CheckInvoke((QilInvoke)n);
case QilNodeType.Content: return CheckContent((QilUnary)n);
case QilNodeType.Attribute: return CheckAttribute((QilBinary)n);
case QilNodeType.Parent: return CheckParent((QilUnary)n);
case QilNodeType.Root: return CheckRoot((QilUnary)n);
case QilNodeType.XmlContext: return CheckXmlContext(n);
case QilNodeType.Descendant: return CheckDescendant((QilUnary)n);
case QilNodeType.DescendantOrSelf: return CheckDescendantOrSelf((QilUnary)n);
case QilNodeType.Ancestor: return CheckAncestor((QilUnary)n);
case QilNodeType.AncestorOrSelf: return CheckAncestorOrSelf((QilUnary)n);
case QilNodeType.Preceding: return CheckPreceding((QilUnary)n);
case QilNodeType.FollowingSibling: return CheckFollowingSibling((QilUnary)n);
case QilNodeType.PrecedingSibling: return CheckPrecedingSibling((QilUnary)n);
case QilNodeType.NodeRange: return CheckNodeRange((QilBinary)n);
case QilNodeType.Deref: return CheckDeref((QilBinary)n);
case QilNodeType.ElementCtor: return CheckElementCtor((QilBinary)n);
case QilNodeType.AttributeCtor: return CheckAttributeCtor((QilBinary)n);
case QilNodeType.CommentCtor: return CheckCommentCtor((QilUnary)n);
case QilNodeType.PICtor: return CheckPICtor((QilBinary)n);
case QilNodeType.TextCtor: return CheckTextCtor((QilUnary)n);
case QilNodeType.RawTextCtor: return CheckRawTextCtor((QilUnary)n);
case QilNodeType.DocumentCtor: return CheckDocumentCtor((QilUnary)n);
case QilNodeType.NamespaceDecl: return CheckNamespaceDecl((QilBinary)n);
case QilNodeType.RtfCtor: return CheckRtfCtor((QilBinary)n);
case QilNodeType.NameOf: return CheckNameOf((QilUnary)n);
case QilNodeType.LocalNameOf: return CheckLocalNameOf((QilUnary)n);
case QilNodeType.NamespaceUriOf: return CheckNamespaceUriOf((QilUnary)n);
case QilNodeType.PrefixOf: return CheckPrefixOf((QilUnary)n);
case QilNodeType.TypeAssert: return CheckTypeAssert((QilTargetType)n);
case QilNodeType.IsType: return CheckIsType((QilTargetType)n);
case QilNodeType.IsEmpty: return CheckIsEmpty((QilUnary)n);
case QilNodeType.XPathNodeValue: return CheckXPathNodeValue((QilUnary)n);
case QilNodeType.XPathFollowing: return CheckXPathFollowing((QilUnary)n);
case QilNodeType.XPathPreceding: return CheckXPathPreceding((QilUnary)n);
case QilNodeType.XPathNamespace: return CheckXPathNamespace((QilUnary)n);
case QilNodeType.XsltGenerateId: return CheckXsltGenerateId((QilUnary)n);
case QilNodeType.XsltInvokeLateBound: return CheckXsltInvokeLateBound((QilInvokeLateBound)n);
case QilNodeType.XsltInvokeEarlyBound: return CheckXsltInvokeEarlyBound((QilInvokeEarlyBound)n);
case QilNodeType.XsltCopy: return CheckXsltCopy((QilBinary)n);
case QilNodeType.XsltCopyOf: return CheckXsltCopyOf((QilUnary)n);
case QilNodeType.XsltConvert: return CheckXsltConvert((QilTargetType)n);
default: return CheckUnknown(n);
}
#endregion
}
#region meta
//-----------------------------------------------
// meta
//-----------------------------------------------
public XmlQueryType CheckQilExpression(QilExpression node) {
Check(node[0].NodeType == QilNodeType.False || node[0].NodeType == QilNodeType.True, node, "IsDebug must either be True or False");
CheckLiteralValue(node[1], typeof(XmlWriterSettings));
CheckLiteralValue(node[2], typeof(IList<WhitespaceRule>));
CheckClassAndNodeType(node[3], typeof(QilList), QilNodeType.GlobalParameterList);
CheckClassAndNodeType(node[4], typeof(QilList), QilNodeType.GlobalVariableList);
CheckLiteralValue(node[5], typeof(IList<EarlyBoundInfo>));
CheckClassAndNodeType(node[6], typeof(QilList), QilNodeType.FunctionList);
return XmlQueryTypeFactory.ItemS;
}
public XmlQueryType CheckFunctionList(QilList node) {
foreach (QilNode child in node)
CheckClassAndNodeType(child, typeof(QilFunction), QilNodeType.Function);
return node.XmlType;
}
public XmlQueryType CheckGlobalVariableList(QilList node) {
foreach (QilNode child in node)
CheckClassAndNodeType(child, typeof(QilIterator), QilNodeType.Let);
return node.XmlType;
}
public XmlQueryType CheckGlobalParameterList(QilList node) {
foreach (QilNode child in node) {
CheckClassAndNodeType(child, typeof(QilParameter), QilNodeType.Parameter);
Check(((QilParameter)child).Name != null, child, "Global parameter's name is null");
}
return node.XmlType;
}
public XmlQueryType CheckActualParameterList(QilList node) {
return node.XmlType;
}
public XmlQueryType CheckFormalParameterList(QilList node) {
foreach (QilNode child in node)
CheckClassAndNodeType(child, typeof(QilParameter), QilNodeType.Parameter);
return node.XmlType;
}
public XmlQueryType CheckSortKeyList(QilList node) {
foreach (QilNode child in node)
CheckClassAndNodeType(child, typeof(QilSortKey), QilNodeType.SortKey);
return node.XmlType;
}
public XmlQueryType CheckBranchList(QilList node) {
return node.XmlType;
}
public XmlQueryType CheckOptimizeBarrier(QilUnary node) {
return node.Child.XmlType;
}
public XmlQueryType CheckUnknown(QilNode node) {
return node.XmlType;
}
#endregion // meta
#region specials
//-----------------------------------------------
// specials
//-----------------------------------------------
public XmlQueryType CheckDataSource(QilDataSource node) {
CheckXmlType(node.Name, XmlQueryTypeFactory.StringX);
CheckXmlType(node.BaseUri, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.NodeNotRtfQ;
}
public XmlQueryType CheckNop(QilUnary node) {
return node.Child.XmlType;
}
public XmlQueryType CheckError(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.None;
}
public XmlQueryType CheckWarning(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.Empty;
}
#endregion // specials
#region variables
//-----------------------------------------------
// variables
//-----------------------------------------------
public XmlQueryType CheckFor(QilIterator node) {
return node.Binding.XmlType.Prime;
}
public XmlQueryType CheckLet(QilIterator node) {
return node.Binding.XmlType;
}
public XmlQueryType CheckParameter(QilParameter node) {
Check(node.Binding == null || node.Binding.XmlType.IsSubtypeOf(node.XmlType), node, "Parameter binding's xml type must be a subtype of the parameter's type");
return node.XmlType;
}
public XmlQueryType CheckPositionOf(QilUnary node) {
return XmlQueryTypeFactory.IntX;
}
#endregion // variables
#region literals
//-----------------------------------------------
// literals
//-----------------------------------------------
public XmlQueryType CheckTrue(QilNode node) {
return XmlQueryTypeFactory.BooleanX;
}
public XmlQueryType CheckFalse(QilNode node) {
return XmlQueryTypeFactory.BooleanX;
}
public XmlQueryType CheckLiteralString(QilLiteral node) {
CheckLiteralValue(node, typeof(string));
return XmlQueryTypeFactory.StringX;
}
public XmlQueryType CheckLiteralInt32(QilLiteral node) {
CheckLiteralValue(node, typeof(int));
return XmlQueryTypeFactory.IntX;
}
public XmlQueryType CheckLiteralInt64(QilLiteral node) {
CheckLiteralValue(node, typeof(long));
return XmlQueryTypeFactory.IntegerX;
}
public XmlQueryType CheckLiteralDouble(QilLiteral node) {
CheckLiteralValue(node, typeof(double));
return XmlQueryTypeFactory.DoubleX;
}
public XmlQueryType CheckLiteralDecimal(QilLiteral node) {
CheckLiteralValue(node, typeof(decimal));
return XmlQueryTypeFactory.DecimalX;
}
public XmlQueryType CheckLiteralQName(QilName node) {
CheckLiteralValue(node, typeof(QilName));
//
return XmlQueryTypeFactory.QNameX;
}
public XmlQueryType CheckLiteralType(QilLiteral node) {
CheckLiteralValue(node, typeof(XmlQueryType));
return (XmlQueryType) node;
}
public XmlQueryType CheckLiteralObject(QilLiteral node) {
Check(node.Value != null, node, "Literal value is null");
return XmlQueryTypeFactory.ItemS;
}
#endregion // literals
#region boolean operators
//-----------------------------------------------
// boolean operators
//-----------------------------------------------
public XmlQueryType CheckAnd(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.BooleanX);
CheckXmlType(node.Right, XmlQueryTypeFactory.BooleanX);
return XmlQueryTypeFactory.BooleanX;
}
public XmlQueryType CheckOr(QilBinary node) {
return CheckAnd(node);
}
public XmlQueryType CheckNot(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.BooleanX);
return XmlQueryTypeFactory.BooleanX;
}
#endregion // boolean operators
#region choice
//-----------------------------------------------
// choice
//-----------------------------------------------
public XmlQueryType CheckConditional(QilTernary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.BooleanX);
return XmlQueryTypeFactory.Choice(node.Center.XmlType, node.Right.XmlType);
}
public XmlQueryType CheckChoice(QilChoice node) {
CheckXmlType(node.Expression, XmlQueryTypeFactory.IntX);
CheckClassAndNodeType(node.Branches, typeof(QilList), QilNodeType.BranchList);
Check(node.Branches.Count > 0, node, "Choice must have at least one branch");
return node.Branches.XmlType;
}
#endregion // choice
#region collection operators
//-----------------------------------------------
// collection operators
//-----------------------------------------------
public XmlQueryType CheckLength(QilUnary node) {
return XmlQueryTypeFactory.IntX;
}
public XmlQueryType CheckSequence(QilList node) {
return node.XmlType;
}
public XmlQueryType CheckUnion(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.NodeNotRtfS);
CheckXmlType(node.Right, XmlQueryTypeFactory.NodeNotRtfS);
return DistinctType(XmlQueryTypeFactory.Sequence(node.Left.XmlType, node.Right.XmlType));
}
public XmlQueryType CheckIntersection(QilBinary node) {
return CheckUnion(node);
}
public XmlQueryType CheckDifference(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.NodeNotRtfS);
CheckXmlType(node.Right, XmlQueryTypeFactory.NodeNotRtfS);
return XmlQueryTypeFactory.AtMost(node.Left.XmlType, node.Left.XmlType.Cardinality);
}
public XmlQueryType CheckAverage(QilUnary node) {
XmlQueryType xmlType = node.Child.XmlType;
CheckNumericXS(node.Child);
return XmlQueryTypeFactory.PrimeProduct(xmlType, xmlType.MaybeEmpty ? XmlQueryCardinality.ZeroOrOne : XmlQueryCardinality.One);
}
public XmlQueryType CheckSum(QilUnary node) {
return CheckAverage(node);
}
public XmlQueryType CheckMinimum(QilUnary node) {
return CheckAverage(node);
}
public XmlQueryType CheckMaximum(QilUnary node) {
return CheckAverage(node);
}
#endregion // collection operators
#region arithmetic operators
//-----------------------------------------------
// arithmetic operators
//-----------------------------------------------
public XmlQueryType CheckNegate(QilUnary node) {
CheckNumericX(node.Child);
return node.Child.XmlType;
}
public XmlQueryType CheckAdd(QilBinary node) {
CheckNumericX(node.Left);
CheckNumericX(node.Right);
CheckNotDisjoint(node);
return node.Left.XmlType.TypeCode == XmlTypeCode.None ? node.Right.XmlType : node.Left.XmlType;
}
public XmlQueryType CheckSubtract(QilBinary node) {
return CheckAdd(node);
}
public XmlQueryType CheckMultiply(QilBinary node) {
return CheckAdd(node);
}
public XmlQueryType CheckDivide(QilBinary node) {
return CheckAdd(node);
}
public XmlQueryType CheckModulo(QilBinary node) {
return CheckAdd(node);
}
#endregion // arithmetic operators
#region string operators
//-----------------------------------------------
// string operators
//-----------------------------------------------
public XmlQueryType CheckStrLength(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.IntX;
}
public XmlQueryType CheckStrConcat(QilStrConcat node) {
CheckXmlType(node.Delimiter, XmlQueryTypeFactory.StringX);
CheckXmlType(node.Values, XmlQueryTypeFactory.StringXS);
return XmlQueryTypeFactory.StringX;
}
public XmlQueryType CheckStrParseQName(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.StringX);
Check(node.Right.XmlType.IsSubtypeOf(XmlQueryTypeFactory.StringX) || node.Right.XmlType.IsSubtypeOf(XmlQueryTypeFactory.NamespaceS),
node, "StrParseQName must take either a string or a list of namespace as its second argument");
return XmlQueryTypeFactory.QNameX;
}
#endregion // string operators
#region value comparison operators
//-----------------------------------------------
// value comparison operators
//-----------------------------------------------
public XmlQueryType CheckNe(QilBinary node) {
CheckAtomicX(node.Left);
CheckAtomicX(node.Right);
CheckNotDisjoint(node);
return XmlQueryTypeFactory.BooleanX;
}
public XmlQueryType CheckEq(QilBinary node) {
return CheckNe(node);
}
public XmlQueryType CheckGt(QilBinary node) {
return CheckNe(node);
}
public XmlQueryType CheckGe(QilBinary node) {
return CheckNe(node);
}
public XmlQueryType CheckLt(QilBinary node) {
return CheckNe(node);
}
public XmlQueryType CheckLe(QilBinary node) {
return CheckNe(node);
}
#endregion // value comparison operators
#region node comparison operators
//-----------------------------------------------
// node comparison operators
//-----------------------------------------------
public XmlQueryType CheckIs(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.NodeNotRtf);
CheckXmlType(node.Right, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.BooleanX;
}
public XmlQueryType CheckAfter(QilBinary node) {
return CheckIs(node);
}
public XmlQueryType CheckBefore(QilBinary node) {
return CheckIs(node);
}
#endregion // node comparison operators
#region loops
//-----------------------------------------------
// loops
//-----------------------------------------------
public XmlQueryType CheckLoop(QilLoop node) {
CheckClass(node[0], typeof(QilIterator));
Check(node.Variable.NodeType == QilNodeType.For || node.Variable.NodeType == QilNodeType.Let, node, "Loop variable must be a For or Let iterator");
XmlQueryType bodyType = node.Body.XmlType;
XmlQueryCardinality variableCard = node.Variable.NodeType == QilNodeType.Let ? XmlQueryCardinality.One : node.Variable.Binding.XmlType.Cardinality;
// Loops do not preserve DocOrderDistinct
return XmlQueryTypeFactory.PrimeProduct(bodyType, variableCard * bodyType.Cardinality);
}
public XmlQueryType CheckFilter(QilLoop node) {
CheckClass(node[0], typeof(QilIterator));
Check(node.Variable.NodeType == QilNodeType.For || node.Variable.NodeType == QilNodeType.Let, node, "Filter variable must be a For or Let iterator");
CheckXmlType(node.Body, XmlQueryTypeFactory.BooleanX);
// Attempt to restrict filter's type by checking condition
XmlQueryType filterType = FindFilterType(node.Variable, node.Body);
if (filterType != null)
return filterType;
return XmlQueryTypeFactory.AtMost(node.Variable.Binding.XmlType, node.Variable.Binding.XmlType.Cardinality);
}
#endregion // loops
#region sorting
//-----------------------------------------------
// sorting
//-----------------------------------------------
public XmlQueryType CheckSort(QilLoop node) {
XmlQueryType varType = node.Variable.Binding.XmlType;
CheckClassAndNodeType(node[0], typeof(QilIterator), QilNodeType.For);
CheckClassAndNodeType(node[1], typeof(QilList), QilNodeType.SortKeyList);
// Sort does not preserve DocOrderDistinct
return XmlQueryTypeFactory.PrimeProduct(varType, varType.Cardinality);
}
public XmlQueryType CheckSortKey(QilSortKey node) {
CheckAtomicX(node.Key);
CheckXmlType(node.Collation, XmlQueryTypeFactory.StringX);
return node.Key.XmlType;
}
public XmlQueryType CheckDocOrderDistinct(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtfS);
return DistinctType(node.Child.XmlType);
}
#endregion // sorting
#region function definition and invocation
//-----------------------------------------------
// function definition and invocation
//-----------------------------------------------
public XmlQueryType CheckFunction(QilFunction node) {
CheckClassAndNodeType(node[0], typeof(QilList), QilNodeType.FormalParameterList);
Check(node[2].NodeType == QilNodeType.False || node[2].NodeType == QilNodeType.True, node, "SideEffects must either be True or False");
Check(node.Definition.XmlType.IsSubtypeOf(node.XmlType), node, "Function definition's xml type must be a subtype of the function's return type");
return node.XmlType;
}
public XmlQueryType CheckInvoke(QilInvoke node) {
#if DEBUG
CheckClassAndNodeType(node[1], typeof(QilList), QilNodeType.ActualParameterList);
QilList actualArgs = node.Arguments;
QilList formalArgs = node.Function.Arguments;
Check(actualArgs.Count == formalArgs.Count, actualArgs, "Invoke argument count must match function's argument count");
for (int i = 0; i < actualArgs.Count; i++)
Check(actualArgs[i].XmlType.IsSubtypeOf(formalArgs[i].XmlType), actualArgs[i], "Invoke argument must be a subtype of the invoked function's argument");
#endif
return node.Function.XmlType;
}
#endregion // function definition and invocation
#region XML navigation
//-----------------------------------------------
// XML navigation
//-----------------------------------------------
public XmlQueryType CheckContent(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.AttributeOrContentS;
}
public XmlQueryType CheckAttribute(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.NodeNotRtf);
CheckXmlType(node.Right, XmlQueryTypeFactory.QNameX);
return XmlQueryTypeFactory.AttributeQ;
}
public XmlQueryType CheckParent(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.DocumentOrElementQ;
}
public XmlQueryType CheckRoot(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.NodeNotRtf;
}
public XmlQueryType CheckXmlContext(QilNode node) {
return XmlQueryTypeFactory.NodeNotRtf;
}
public XmlQueryType CheckDescendant(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.ContentS;
}
public XmlQueryType CheckDescendantOrSelf(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.Choice(node.Child.XmlType, XmlQueryTypeFactory.ContentS);
}
public XmlQueryType CheckAncestor(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.DocumentOrElementS;
}
public XmlQueryType CheckAncestorOrSelf(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.Choice(node.Child.XmlType, XmlQueryTypeFactory.DocumentOrElementS);
}
public XmlQueryType CheckPreceding(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.DocumentOrContentS;
}
public XmlQueryType CheckFollowingSibling(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.ContentS;
}
public XmlQueryType CheckPrecedingSibling(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.ContentS;
}
public XmlQueryType CheckNodeRange(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.NodeNotRtf);
CheckXmlType(node.Right, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.Choice(node.Left.XmlType, XmlQueryTypeFactory.ContentS, node.Right.XmlType);
}
public XmlQueryType CheckDeref(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.NodeNotRtf);
CheckXmlType(node.Right, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.ElementS;
}
#endregion // XML navigation
#region XML construction
//-----------------------------------------------
// XML construction
//-----------------------------------------------
public XmlQueryType CheckElementCtor(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.QNameX);
CheckXmlType(node.Right, XmlQueryTypeFactory.NodeNotRtfS);
return XmlQueryTypeFactory.UntypedElement;
}
public XmlQueryType CheckAttributeCtor(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.QNameX);
CheckXmlType(node.Right, XmlQueryTypeFactory.NodeNotRtfS);
return XmlQueryTypeFactory.UntypedAttribute;
}
public XmlQueryType CheckCommentCtor(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtfS);
return XmlQueryTypeFactory.Comment;
}
public XmlQueryType CheckPICtor(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.StringX);
CheckXmlType(node.Right, XmlQueryTypeFactory.NodeNotRtfS);
return XmlQueryTypeFactory.PI;
}
public XmlQueryType CheckTextCtor(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.Text;
}
public XmlQueryType CheckRawTextCtor(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.Text;
}
public XmlQueryType CheckDocumentCtor(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtfS);
return XmlQueryTypeFactory.UntypedDocument;
}
public XmlQueryType CheckNamespaceDecl(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.StringX);
CheckXmlType(node.Right, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.Namespace;
}
public XmlQueryType CheckRtfCtor(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.NodeNotRtfS);
CheckClassAndNodeType(node.Right, typeof(QilLiteral), QilNodeType.LiteralString);
return XmlQueryTypeFactory.Node;
}
#endregion // XML construction
#region Node properties
//-----------------------------------------------
// Node properties
//-----------------------------------------------
public XmlQueryType CheckNameOf(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.Node);
return XmlQueryTypeFactory.QNameX;
}
public XmlQueryType CheckLocalNameOf(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.Node);
return XmlQueryTypeFactory.StringX;
}
public XmlQueryType CheckNamespaceUriOf(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.Node);
return XmlQueryTypeFactory.StringX;
}
public XmlQueryType CheckPrefixOf(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.Node);
return XmlQueryTypeFactory.StringX;
}
#endregion // Node properties
#region Copy operators
//-----------------------------------------------
// Copy operators
//-----------------------------------------------
public XmlQueryType CheckDeepCopy(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return node.XmlType;
}
#endregion // Copy operators
#region Type operators
//-----------------------------------------------
// Type operators
//-----------------------------------------------
public XmlQueryType CheckTypeAssert(QilTargetType node) {
CheckClassAndNodeType(node[1], typeof(QilLiteral), QilNodeType.LiteralType);
return node.TargetType;
}
public XmlQueryType CheckIsType(QilTargetType node) {
CheckClassAndNodeType(node[1], typeof(QilLiteral), QilNodeType.LiteralType);
return XmlQueryTypeFactory.BooleanX;
}
public XmlQueryType CheckIsEmpty(QilUnary node) {
return XmlQueryTypeFactory.BooleanX;
}
#endregion // Type operators
#region XPath operators
//-----------------------------------------------
// XPath operators
//-----------------------------------------------
public XmlQueryType CheckXPathNodeValue(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeS);
return XmlQueryTypeFactory.StringX;
}
public XmlQueryType CheckXPathFollowing(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.ContentS;
}
public XmlQueryType CheckXPathPreceding(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.ContentS;
}
public XmlQueryType CheckXPathNamespace(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.NamespaceS;
}
#endregion // XPath operators
#region XSLT
//-----------------------------------------------
// XSLT
//-----------------------------------------------
public XmlQueryType CheckXsltGenerateId(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtfS);
return XmlQueryTypeFactory.StringX;
}
public XmlQueryType CheckXsltInvokeLateBound(QilInvokeLateBound node) {
CheckLiteralValue(node[0], typeof(QilName));
CheckClassAndNodeType(node[1], typeof(QilList), QilNodeType.ActualParameterList);
return XmlQueryTypeFactory.ItemS;
}
public XmlQueryType CheckXsltInvokeEarlyBound(QilInvokeEarlyBound node) {
#if DEBUG
CheckLiteralValue(node[0], typeof(QilName));
CheckLiteralValue(node[1], typeof(MethodInfo));
CheckClassAndNodeType(node[2], typeof(QilList), QilNodeType.ActualParameterList);
XmlExtensionFunction extFunc = new XmlExtensionFunction(node.Name.LocalName, node.Name.NamespaceUri, node.ClrMethod);
QilList actualArgs = node.Arguments;
Check(actualArgs.Count == extFunc.Method.GetParameters().Length, actualArgs, "InvokeEarlyBound argument count must match function's argument count");
for (int i = 0; i < actualArgs.Count; i++) {
Check(actualArgs[i].XmlType.IsSubtypeOf(extFunc.GetXmlArgumentType(i)), actualArgs[i], "InvokeEarlyBound argument must be a subtype of the invoked function's argument type");
}
#endif
return node.XmlType;
}
public XmlQueryType CheckXsltCopy(QilBinary node) {
CheckXmlType(node.Left, XmlQueryTypeFactory.NodeNotRtf);
CheckXmlType(node.Right, XmlQueryTypeFactory.NodeS);
return XmlQueryTypeFactory.Choice(node.Left.XmlType, node.Right.XmlType);
}
public XmlQueryType CheckXsltCopyOf(QilUnary node) {
CheckXmlType(node.Child, XmlQueryTypeFactory.Node);
if ((node.Child.XmlType.NodeKinds & XmlNodeKindFlags.Document) != 0)
return XmlQueryTypeFactory.NodeNotRtfS;
return node.Child.XmlType;
}
public XmlQueryType CheckXsltConvert(QilTargetType node) {
CheckClassAndNodeType(node[1], typeof(QilLiteral), QilNodeType.LiteralType);
return node.TargetType;
}
#endregion // Xslt operators
//-----------------------------------------------
// Helper functions
//-----------------------------------------------
[Conditional("DEBUG")]
private void Check(bool value, QilNode node, string message) {
if (!value)
QilValidationVisitor.SetError(node, message);
}
[Conditional("DEBUG")]
private void CheckLiteralValue(QilNode node, Type clrTypeValue) {
Check(node is QilLiteral, node, "Node must be instance of QilLiteral");
Type clrType = ((QilLiteral) node).Value.GetType();
Check(clrTypeValue.IsAssignableFrom(clrType), node, "Literal value must be of type " + clrTypeValue.Name);
}
[Conditional("DEBUG")]
private void CheckClass(QilNode node, Type clrTypeClass) {
Check(clrTypeClass.IsAssignableFrom(node.GetType()), node, "Node must be instance of " + clrTypeClass.Name);
}
[Conditional("DEBUG")]
private void CheckClassAndNodeType(QilNode node, Type clrTypeClass, QilNodeType nodeType) {
CheckClass(node, clrTypeClass);
Check(node.NodeType == nodeType, node, "Node must have QilNodeType." + nodeType);
}
[Conditional("DEBUG")]
private void CheckXmlType(QilNode node, XmlQueryType xmlType) {
Check(node.XmlType.IsSubtypeOf(xmlType), node, "Node's type " + node.XmlType + " is not a subtype of " + xmlType);
}
[Conditional("DEBUG")]
private void CheckNumericX(QilNode node) {
Check(node.XmlType.IsNumeric && node.XmlType.IsSingleton && node.XmlType.IsStrict, node, "Node's type " + node.XmlType + " must be a strict singleton numeric type");
}
[Conditional("DEBUG")]
private void CheckNumericXS(QilNode node) {
Check(node.XmlType.IsNumeric && node.XmlType.IsStrict, node, "Node's type " + node.XmlType + " must be a strict numeric type");
}
[Conditional("DEBUG")]
private void CheckAtomicX(QilNode node) {
Check(node.XmlType.IsAtomicValue && node.XmlType.IsStrict, node, "Node's type " + node.XmlType + " must be a strict atomic value type");
}
[Conditional("DEBUG")]
private void CheckNotDisjoint(QilBinary node) {
Check(node.Left.XmlType.IsSubtypeOf(node.Right.XmlType) || node.Right.XmlType.IsSubtypeOf(node.Left.XmlType), node,
"Node must not have arguments with disjoint types " + node.Left.XmlType + " and " + node.Right.XmlType);
}
private XmlQueryType DistinctType(XmlQueryType type) {
if (type.Cardinality == XmlQueryCardinality.More)
return XmlQueryTypeFactory.PrimeProduct(type, XmlQueryCardinality.OneOrMore);
if (type.Cardinality == XmlQueryCardinality.NotOne)
return XmlQueryTypeFactory.PrimeProduct(type, XmlQueryCardinality.ZeroOrMore);
return type;
}
private XmlQueryType FindFilterType(QilIterator variable, QilNode body) {
XmlQueryType leftType;
QilBinary binary;
if (body.XmlType.TypeCode == XmlTypeCode.None)
return XmlQueryTypeFactory.None;
switch (body.NodeType) {
case QilNodeType.False:
return XmlQueryTypeFactory.Empty;
case QilNodeType.IsType:
// If testing the type of "variable", then filter type can be restricted
if ((object) ((QilTargetType) body).Source == (object) variable)
return XmlQueryTypeFactory.AtMost(((QilTargetType)body).TargetType, variable.Binding.XmlType.Cardinality);
break;
case QilNodeType.And:
// Both And conditions can be used to restrict filter's type
leftType = FindFilterType(variable, ((QilBinary) body).Left);
if (leftType != null)
return leftType;
return FindFilterType(variable, ((QilBinary) body).Right);
case QilNodeType.Eq:
// Restrict cardinality if position($iterator) = $pos is found
binary = (QilBinary) body;
if (binary.Left.NodeType == QilNodeType.PositionOf) {
if ((object) ((QilUnary) binary.Left).Child == (object) variable)
return XmlQueryTypeFactory.AtMost(variable.Binding.XmlType, XmlQueryCardinality.ZeroOrOne);
}
break;
}
return null;
}
}
}
|