|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
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 static class QilTypeChecker
{
public static XmlQueryType Check(QilNode n)
{
#region AUTOGENERATED
return n.NodeType switch
{
QilNodeType.QilExpression => CheckQilExpression((QilExpression)n),
QilNodeType.FunctionList => CheckFunctionList((QilList)n),
QilNodeType.GlobalVariableList => CheckGlobalVariableList((QilList)n),
QilNodeType.GlobalParameterList => CheckGlobalParameterList((QilList)n),
QilNodeType.ActualParameterList => CheckActualParameterList((QilList)n),
QilNodeType.FormalParameterList => CheckFormalParameterList((QilList)n),
QilNodeType.SortKeyList => CheckSortKeyList((QilList)n),
QilNodeType.BranchList => CheckBranchList((QilList)n),
QilNodeType.OptimizeBarrier => CheckOptimizeBarrier((QilUnary)n),
QilNodeType.Unknown => CheckUnknown(n),
QilNodeType.DataSource => CheckDataSource((QilDataSource)n),
QilNodeType.Nop => CheckNop((QilUnary)n),
QilNodeType.Error => CheckError((QilUnary)n),
QilNodeType.Warning => CheckWarning((QilUnary)n),
QilNodeType.For => CheckFor((QilIterator)n),
QilNodeType.Let => CheckLet((QilIterator)n),
QilNodeType.Parameter => CheckParameter((QilParameter)n),
QilNodeType.PositionOf => CheckPositionOf(),
QilNodeType.True => CheckTrue(),
QilNodeType.False => CheckFalse(),
QilNodeType.LiteralString => CheckLiteralString((QilLiteral)n),
QilNodeType.LiteralInt32 => CheckLiteralInt32((QilLiteral)n),
QilNodeType.LiteralInt64 => CheckLiteralInt64((QilLiteral)n),
QilNodeType.LiteralDouble => CheckLiteralDouble((QilLiteral)n),
QilNodeType.LiteralDecimal => CheckLiteralDecimal((QilLiteral)n),
QilNodeType.LiteralQName => CheckLiteralQName((QilName)n),
QilNodeType.LiteralType => CheckLiteralType((QilLiteral)n),
QilNodeType.LiteralObject => CheckLiteralObject((QilLiteral)n),
QilNodeType.And => CheckAnd((QilBinary)n),
QilNodeType.Or => CheckOr((QilBinary)n),
QilNodeType.Not => CheckNot((QilUnary)n),
QilNodeType.Conditional => CheckConditional((QilTernary)n),
QilNodeType.Choice => CheckChoice((QilChoice)n),
QilNodeType.Length => CheckLength(),
QilNodeType.Sequence => CheckSequence((QilList)n),
QilNodeType.Union => CheckUnion((QilBinary)n),
QilNodeType.Intersection => CheckIntersection((QilBinary)n),
QilNodeType.Difference => CheckDifference((QilBinary)n),
QilNodeType.Average => CheckAverage((QilUnary)n),
QilNodeType.Sum => CheckSum((QilUnary)n),
QilNodeType.Minimum => CheckMinimum((QilUnary)n),
QilNodeType.Maximum => CheckMaximum((QilUnary)n),
QilNodeType.Negate => CheckNegate((QilUnary)n),
QilNodeType.Add => CheckAdd((QilBinary)n),
QilNodeType.Subtract => CheckSubtract((QilBinary)n),
QilNodeType.Multiply => CheckMultiply((QilBinary)n),
QilNodeType.Divide => CheckDivide((QilBinary)n),
QilNodeType.Modulo => CheckModulo((QilBinary)n),
QilNodeType.StrLength => CheckStrLength((QilUnary)n),
QilNodeType.StrConcat => CheckStrConcat((QilStrConcat)n),
QilNodeType.StrParseQName => CheckStrParseQName((QilBinary)n),
QilNodeType.Ne => CheckNe((QilBinary)n),
QilNodeType.Eq => CheckEq((QilBinary)n),
QilNodeType.Gt => CheckGt((QilBinary)n),
QilNodeType.Ge => CheckGe((QilBinary)n),
QilNodeType.Lt => CheckLt((QilBinary)n),
QilNodeType.Le => CheckLe((QilBinary)n),
QilNodeType.Is => CheckIs((QilBinary)n),
QilNodeType.After => CheckAfter((QilBinary)n),
QilNodeType.Before => CheckBefore((QilBinary)n),
QilNodeType.Loop => CheckLoop((QilLoop)n),
QilNodeType.Filter => CheckFilter((QilLoop)n),
QilNodeType.Sort => CheckSort((QilLoop)n),
QilNodeType.SortKey => CheckSortKey((QilSortKey)n),
QilNodeType.DocOrderDistinct => CheckDocOrderDistinct((QilUnary)n),
QilNodeType.Function => CheckFunction((QilFunction)n),
QilNodeType.Invoke => CheckInvoke((QilInvoke)n),
QilNodeType.Content => CheckContent((QilUnary)n),
QilNodeType.Attribute => CheckAttribute((QilBinary)n),
QilNodeType.Parent => CheckParent((QilUnary)n),
QilNodeType.Root => CheckRoot((QilUnary)n),
QilNodeType.XmlContext => CheckXmlContext(),
QilNodeType.Descendant => CheckDescendant((QilUnary)n),
QilNodeType.DescendantOrSelf => CheckDescendantOrSelf((QilUnary)n),
QilNodeType.Ancestor => CheckAncestor((QilUnary)n),
QilNodeType.AncestorOrSelf => CheckAncestorOrSelf((QilUnary)n),
QilNodeType.Preceding => CheckPreceding((QilUnary)n),
QilNodeType.FollowingSibling => CheckFollowingSibling((QilUnary)n),
QilNodeType.PrecedingSibling => CheckPrecedingSibling((QilUnary)n),
QilNodeType.NodeRange => CheckNodeRange((QilBinary)n),
QilNodeType.Deref => CheckDeref((QilBinary)n),
QilNodeType.ElementCtor => CheckElementCtor((QilBinary)n),
QilNodeType.AttributeCtor => CheckAttributeCtor((QilBinary)n),
QilNodeType.CommentCtor => CheckCommentCtor((QilUnary)n),
QilNodeType.PICtor => CheckPICtor((QilBinary)n),
QilNodeType.TextCtor => CheckTextCtor((QilUnary)n),
QilNodeType.RawTextCtor => CheckRawTextCtor((QilUnary)n),
QilNodeType.DocumentCtor => CheckDocumentCtor((QilUnary)n),
QilNodeType.NamespaceDecl => CheckNamespaceDecl((QilBinary)n),
QilNodeType.RtfCtor => CheckRtfCtor((QilBinary)n),
QilNodeType.NameOf => CheckNameOf((QilUnary)n),
QilNodeType.LocalNameOf => CheckLocalNameOf((QilUnary)n),
QilNodeType.NamespaceUriOf => CheckNamespaceUriOf((QilUnary)n),
QilNodeType.PrefixOf => CheckPrefixOf((QilUnary)n),
QilNodeType.TypeAssert => CheckTypeAssert((QilTargetType)n),
QilNodeType.IsType => CheckIsType((QilTargetType)n),
QilNodeType.IsEmpty => CheckIsEmpty(),
QilNodeType.XPathNodeValue => CheckXPathNodeValue((QilUnary)n),
QilNodeType.XPathFollowing => CheckXPathFollowing((QilUnary)n),
QilNodeType.XPathPreceding => CheckXPathPreceding((QilUnary)n),
QilNodeType.XPathNamespace => CheckXPathNamespace((QilUnary)n),
QilNodeType.XsltGenerateId => CheckXsltGenerateId((QilUnary)n),
QilNodeType.XsltInvokeLateBound => CheckXsltInvokeLateBound((QilInvokeLateBound)n),
QilNodeType.XsltInvokeEarlyBound => CheckXsltInvokeEarlyBound((QilInvokeEarlyBound)n),
QilNodeType.XsltCopy => CheckXsltCopy((QilBinary)n),
QilNodeType.XsltCopyOf => CheckXsltCopyOf((QilUnary)n),
QilNodeType.XsltConvert => CheckXsltConvert((QilTargetType)n),
_ => CheckUnknown(n),
};
#endregion
}
#region meta
//-----------------------------------------------
// meta
//-----------------------------------------------
public static 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 static XmlQueryType CheckFunctionList(QilList node)
{
foreach (QilNode child in node)
CheckClassAndNodeType(child, typeof(QilFunction), QilNodeType.Function);
return node.XmlType;
}
public static XmlQueryType CheckGlobalVariableList(QilList node)
{
foreach (QilNode child in node)
CheckClassAndNodeType(child, typeof(QilIterator), QilNodeType.Let);
return node.XmlType;
}
public static 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 static XmlQueryType CheckActualParameterList(QilList node)
{
return node.XmlType;
}
public static XmlQueryType CheckFormalParameterList(QilList node)
{
foreach (QilNode child in node)
CheckClassAndNodeType(child, typeof(QilParameter), QilNodeType.Parameter);
return node.XmlType;
}
public static XmlQueryType CheckSortKeyList(QilList node)
{
foreach (QilNode child in node)
CheckClassAndNodeType(child, typeof(QilSortKey), QilNodeType.SortKey);
return node.XmlType;
}
public static XmlQueryType CheckBranchList(QilList node)
{
return node.XmlType;
}
public static XmlQueryType CheckOptimizeBarrier(QilUnary node)
{
return node.Child.XmlType!;
}
public static XmlQueryType CheckUnknown(QilNode node)
{
return node.XmlType!;
}
#endregion // meta
#region specials
//-----------------------------------------------
// specials
//-----------------------------------------------
public static XmlQueryType CheckDataSource(QilDataSource node)
{
CheckXmlType(node.Name, XmlQueryTypeFactory.StringX);
CheckXmlType(node.BaseUri, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.NodeNotRtfQ;
}
public static XmlQueryType CheckNop(QilUnary node)
{
return node.Child.XmlType!;
}
public static XmlQueryType CheckError(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.None;
}
public static XmlQueryType CheckWarning(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.Empty;
}
#endregion // specials
#region variables
//-----------------------------------------------
// variables
//-----------------------------------------------
public static XmlQueryType CheckFor(QilIterator node)
{
return node.Binding!.XmlType!.Prime;
}
public static XmlQueryType CheckLet(QilIterator node)
{
return node.Binding!.XmlType!;
}
public static 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 static XmlQueryType CheckPositionOf()
{
return XmlQueryTypeFactory.IntX;
}
#endregion // variables
#region literals
//-----------------------------------------------
// literals
//-----------------------------------------------
public static XmlQueryType CheckTrue()
{
return XmlQueryTypeFactory.BooleanX;
}
public static XmlQueryType CheckFalse()
{
return XmlQueryTypeFactory.BooleanX;
}
public static XmlQueryType CheckLiteralString(QilLiteral node)
{
CheckLiteralValue(node, typeof(string));
return XmlQueryTypeFactory.StringX;
}
public static XmlQueryType CheckLiteralInt32(QilLiteral node)
{
CheckLiteralValue(node, typeof(int));
return XmlQueryTypeFactory.IntX;
}
public static XmlQueryType CheckLiteralInt64(QilLiteral node)
{
CheckLiteralValue(node, typeof(long));
return XmlQueryTypeFactory.IntegerX;
}
public static XmlQueryType CheckLiteralDouble(QilLiteral node)
{
CheckLiteralValue(node, typeof(double));
return XmlQueryTypeFactory.DoubleX;
}
public static XmlQueryType CheckLiteralDecimal(QilLiteral node)
{
CheckLiteralValue(node, typeof(decimal));
return XmlQueryTypeFactory.DecimalX;
}
public static XmlQueryType CheckLiteralQName(QilName node)
{
CheckLiteralValue(node, typeof(QilName));
// BUGBUG: Xslt constructs invalid QNames, so don't check this
//Check(ValidateNames.ValidateName(node.Prefix, node.LocalName, node.NamespaceUri, XPathNodeType.Element, ValidateNames.Flags.All), node, "QName is not valid");
return XmlQueryTypeFactory.QNameX;
}
public static XmlQueryType CheckLiteralType(QilLiteral node)
{
CheckLiteralValue(node, typeof(XmlQueryType));
return (XmlQueryType)node;
}
public static XmlQueryType CheckLiteralObject(QilLiteral node)
{
Check(node.Value != null, node, "Literal value is null");
return XmlQueryTypeFactory.ItemS;
}
#endregion // literals
#region boolean operators
//-----------------------------------------------
// boolean operators
//-----------------------------------------------
public static XmlQueryType CheckAnd(QilBinary node)
{
CheckXmlType(node.Left, XmlQueryTypeFactory.BooleanX);
CheckXmlType(node.Right, XmlQueryTypeFactory.BooleanX);
return XmlQueryTypeFactory.BooleanX;
}
public static XmlQueryType CheckOr(QilBinary node)
{
return CheckAnd(node);
}
public static XmlQueryType CheckNot(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.BooleanX);
return XmlQueryTypeFactory.BooleanX;
}
#endregion // boolean operators
#region choice
//-----------------------------------------------
// choice
//-----------------------------------------------
public static XmlQueryType CheckConditional(QilTernary node)
{
CheckXmlType(node.Left, XmlQueryTypeFactory.BooleanX);
return XmlQueryTypeFactory.Choice(node.Center.XmlType!, node.Right.XmlType!);
}
public static 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 static XmlQueryType CheckLength()
{
return XmlQueryTypeFactory.IntX;
}
public static XmlQueryType CheckSequence(QilList node)
{
return node.XmlType;
}
public static 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 static XmlQueryType CheckIntersection(QilBinary node)
{
return CheckUnion(node);
}
public static 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 static XmlQueryType CheckAverage(QilUnary node)
{
XmlQueryType xmlType = node.Child.XmlType!;
CheckNumericXS(node.Child);
return XmlQueryTypeFactory.PrimeProduct(xmlType, xmlType.MaybeEmpty ? XmlQueryCardinality.ZeroOrOne : XmlQueryCardinality.One);
}
public static XmlQueryType CheckSum(QilUnary node)
{
return CheckAverage(node);
}
public static XmlQueryType CheckMinimum(QilUnary node)
{
return CheckAverage(node);
}
public static XmlQueryType CheckMaximum(QilUnary node)
{
return CheckAverage(node);
}
#endregion // collection operators
#region arithmetic operators
//-----------------------------------------------
// arithmetic operators
//-----------------------------------------------
public static XmlQueryType CheckNegate(QilUnary node)
{
CheckNumericX(node.Child);
return node.Child.XmlType!;
}
public static 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 static XmlQueryType CheckSubtract(QilBinary node)
{
return CheckAdd(node);
}
public static XmlQueryType CheckMultiply(QilBinary node)
{
return CheckAdd(node);
}
public static XmlQueryType CheckDivide(QilBinary node)
{
return CheckAdd(node);
}
public static XmlQueryType CheckModulo(QilBinary node)
{
return CheckAdd(node);
}
#endregion // arithmetic operators
#region string operators
//-----------------------------------------------
// string operators
//-----------------------------------------------
public static XmlQueryType CheckStrLength(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.IntX;
}
public static XmlQueryType CheckStrConcat(QilStrConcat node)
{
CheckXmlType(node.Delimiter, XmlQueryTypeFactory.StringX);
CheckXmlType(node.Values, XmlQueryTypeFactory.StringXS);
return XmlQueryTypeFactory.StringX;
}
public static 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 static XmlQueryType CheckNe(QilBinary node)
{
CheckAtomicX(node.Left);
CheckAtomicX(node.Right);
CheckNotDisjoint(node);
return XmlQueryTypeFactory.BooleanX;
}
public static XmlQueryType CheckEq(QilBinary node)
{
return CheckNe(node);
}
public static XmlQueryType CheckGt(QilBinary node)
{
return CheckNe(node);
}
public static XmlQueryType CheckGe(QilBinary node)
{
return CheckNe(node);
}
public static XmlQueryType CheckLt(QilBinary node)
{
return CheckNe(node);
}
public static XmlQueryType CheckLe(QilBinary node)
{
return CheckNe(node);
}
#endregion // value comparison operators
#region node comparison operators
//-----------------------------------------------
// node comparison operators
//-----------------------------------------------
public static XmlQueryType CheckIs(QilBinary node)
{
CheckXmlType(node.Left, XmlQueryTypeFactory.NodeNotRtf);
CheckXmlType(node.Right, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.BooleanX;
}
public static XmlQueryType CheckAfter(QilBinary node)
{
return CheckIs(node);
}
public static XmlQueryType CheckBefore(QilBinary node)
{
return CheckIs(node);
}
#endregion // node comparison operators
#region loops
//-----------------------------------------------
// loops
//-----------------------------------------------
public static 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 static 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 static 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 static XmlQueryType CheckSortKey(QilSortKey node)
{
CheckAtomicX(node.Key);
CheckXmlType(node.Collation, XmlQueryTypeFactory.StringX);
return node.Key.XmlType!;
}
public static 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 static 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 static 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 static XmlQueryType CheckContent(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.AttributeOrContentS;
}
public static XmlQueryType CheckAttribute(QilBinary node)
{
CheckXmlType(node.Left, XmlQueryTypeFactory.NodeNotRtf);
CheckXmlType(node.Right, XmlQueryTypeFactory.QNameX);
return XmlQueryTypeFactory.AttributeQ;
}
public static XmlQueryType CheckParent(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.DocumentOrElementQ;
}
public static XmlQueryType CheckRoot(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.NodeNotRtf;
}
public static XmlQueryType CheckXmlContext()
{
return XmlQueryTypeFactory.NodeNotRtf;
}
public static XmlQueryType CheckDescendant(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.ContentS;
}
public static XmlQueryType CheckDescendantOrSelf(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.Choice(node.Child.XmlType!, XmlQueryTypeFactory.ContentS);
}
public static XmlQueryType CheckAncestor(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.DocumentOrElementS;
}
public static XmlQueryType CheckAncestorOrSelf(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.Choice(node.Child.XmlType!, XmlQueryTypeFactory.DocumentOrElementS);
}
public static XmlQueryType CheckPreceding(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.DocumentOrContentS;
}
public static XmlQueryType CheckFollowingSibling(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.ContentS;
}
public static XmlQueryType CheckPrecedingSibling(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.ContentS;
}
public static 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 static 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 static XmlQueryType CheckElementCtor(QilBinary node)
{
CheckXmlType(node.Left, XmlQueryTypeFactory.QNameX);
CheckXmlType(node.Right, XmlQueryTypeFactory.NodeNotRtfS);
return XmlQueryTypeFactory.UntypedElement;
}
public static XmlQueryType CheckAttributeCtor(QilBinary node)
{
CheckXmlType(node.Left, XmlQueryTypeFactory.QNameX);
CheckXmlType(node.Right, XmlQueryTypeFactory.NodeNotRtfS);
return XmlQueryTypeFactory.UntypedAttribute;
}
public static XmlQueryType CheckCommentCtor(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtfS);
return XmlQueryTypeFactory.Comment;
}
public static XmlQueryType CheckPICtor(QilBinary node)
{
CheckXmlType(node.Left, XmlQueryTypeFactory.StringX);
CheckXmlType(node.Right, XmlQueryTypeFactory.NodeNotRtfS);
return XmlQueryTypeFactory.PI;
}
public static XmlQueryType CheckTextCtor(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.Text;
}
public static XmlQueryType CheckRawTextCtor(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.Text;
}
public static XmlQueryType CheckDocumentCtor(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtfS);
return XmlQueryTypeFactory.UntypedDocument;
}
public static XmlQueryType CheckNamespaceDecl(QilBinary node)
{
CheckXmlType(node.Left, XmlQueryTypeFactory.StringX);
CheckXmlType(node.Right, XmlQueryTypeFactory.StringX);
return XmlQueryTypeFactory.Namespace;
}
public static 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 static XmlQueryType CheckNameOf(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.Node);
return XmlQueryTypeFactory.QNameX;
}
public static XmlQueryType CheckLocalNameOf(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.Node);
return XmlQueryTypeFactory.StringX;
}
public static XmlQueryType CheckNamespaceUriOf(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.Node);
return XmlQueryTypeFactory.StringX;
}
public static XmlQueryType CheckPrefixOf(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.Node);
return XmlQueryTypeFactory.StringX;
}
#endregion // Node properties
#region Copy operators
#endregion // Copy operators
#region Type operators
//-----------------------------------------------
// Type operators
//-----------------------------------------------
public static XmlQueryType CheckTypeAssert(QilTargetType node)
{
CheckClassAndNodeType(node[1], typeof(QilLiteral), QilNodeType.LiteralType);
return node.TargetType;
}
public static XmlQueryType CheckIsType(QilTargetType node)
{
CheckClassAndNodeType(node[1], typeof(QilLiteral), QilNodeType.LiteralType);
return XmlQueryTypeFactory.BooleanX;
}
public static XmlQueryType CheckIsEmpty()
{
return XmlQueryTypeFactory.BooleanX;
}
#endregion // Type operators
#region XPath operators
//-----------------------------------------------
// XPath operators
//-----------------------------------------------
public static XmlQueryType CheckXPathNodeValue(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeS);
return XmlQueryTypeFactory.StringX;
}
public static XmlQueryType CheckXPathFollowing(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.ContentS;
}
public static XmlQueryType CheckXPathPreceding(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.ContentS;
}
public static XmlQueryType CheckXPathNamespace(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtf);
return XmlQueryTypeFactory.NamespaceS;
}
#endregion // XPath operators
#region XSLT
//-----------------------------------------------
// XSLT
//-----------------------------------------------
public static XmlQueryType CheckXsltGenerateId(QilUnary node)
{
CheckXmlType(node.Child, XmlQueryTypeFactory.NodeNotRtfS);
return XmlQueryTypeFactory.StringX;
}
public static XmlQueryType CheckXsltInvokeLateBound(QilInvokeLateBound node)
{
CheckLiteralValue(node[0], typeof(QilName));
CheckClassAndNodeType(node[1], typeof(QilList), QilNodeType.ActualParameterList);
return XmlQueryTypeFactory.ItemS;
}
public static 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 static XmlQueryType CheckXsltCopy(QilBinary node)
{
CheckXmlType(node.Left, XmlQueryTypeFactory.NodeNotRtf);
CheckXmlType(node.Right, XmlQueryTypeFactory.NodeS);
return XmlQueryTypeFactory.Choice(node.Left.XmlType!, node.Right.XmlType!);
}
public static 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 static XmlQueryType CheckXsltConvert(QilTargetType node)
{
CheckClassAndNodeType(node[1], typeof(QilLiteral), QilNodeType.LiteralType);
return node.TargetType;
}
#endregion // Xslt operators
//-----------------------------------------------
// Helper functions
//-----------------------------------------------
[Conditional("DEBUG")]
private static void Check(bool value, QilNode node, string message)
{
if (!value)
QilValidationVisitor.SetError(node, message);
}
[Conditional("DEBUG")]
private static 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 static void CheckClass(QilNode node, Type clrTypeClass)
{
Check(clrTypeClass.IsAssignableFrom(node.GetType()), node, $"Node must be instance of {clrTypeClass.Name}");
}
[Conditional("DEBUG")]
private static void CheckClassAndNodeType(QilNode node, Type clrTypeClass, QilNodeType nodeType)
{
CheckClass(node, clrTypeClass);
Check(node.NodeType == nodeType, node, $"Node must have QilNodeType.{nodeType}");
}
[Conditional("DEBUG")]
private static 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 static 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 static 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 static 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 static 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 static 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 static 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;
}
}
}
|