public QilNode BuildMatcher(QilIterator it, IList <XslNode> actualArgs, QilNode otherwise) { QilNode result = otherwise; foreach (List <TemplateMatch> list in allMatches) { foreach (TemplateMatch match in list) { XmlNodeKindFlags nodeKind = match.NodeKind; QilName qname = match.QName; QilNode cond = match.Condition; if (cond != null) { // We have to clone, because the same pattern may be used // in many different xsl:apply-templates/imports functions cond = cond.DeepClone(f.BaseFactory); cond = refReplacer.Replace(cond, match.Iterator, it); } if (nodeKind != 0) { XmlQueryType nodeType; switch (nodeKind) { case XmlNodeKindFlags.Element: nodeType = T.Element; break; case XmlNodeKindFlags.Attribute: nodeType = T.Attribute; break; case XmlNodeKindFlags.Text: nodeType = T.Text; break; case XmlNodeKindFlags.Document: nodeType = T.Document; break; case XmlNodeKindFlags.Comment: nodeType = T.Comment; break; case XmlNodeKindFlags.PI: nodeType = T.PI; break; default: nodeType = null; break; } Debug.Assert(nodeType != null, "Unexpected nodeKind: " + nodeKind); QilNode typeNameCheck = f.IsType(it, nodeType); if (qname != null) { typeNameCheck = f.And(typeNameCheck, f.Eq(f.NameOf(it), qname.ShallowClone(f.BaseFactory))); } cond = (cond == null) ? typeNameCheck : f.And(typeNameCheck, cond); } result = f.Conditional(cond, invkGen.GenerateInvoke(match.TemplateFunction, actualArgs), result ); } } return(result); }
//The structure of result is a Filter, variable is current node, body is the match condition. //Previous predicate build logic in XPathPatternBuilder is match from right to left, which have 2^n complexiy when have lots of position predicates. TFS #368771 //Now change the logic to: If predicates contains position/last predicates, given the current node, filter out all the nodes that match the predicates, //and then check if current node is in the result set. public QilNode BuildPredicates(QilNode nodeset, List <QilNode> predicates) { //convert predicates to boolean type List <QilNode> convertedPredicates = new List <QilNode>(predicates.Count); foreach (var predicate in predicates) { convertedPredicates.Add(XPathBuilder.PredicateToBoolean(predicate, _f, _predicateEnvironment)); } QilLoop nodeFilter = (QilLoop)nodeset; QilIterator current = nodeFilter.Variable; //If no last() and position() in predicates, use nodeFilter.Variable to fixup current //because all the predicates only based on the input variable, no matter what other predicates are. if (_predicateEnvironment.numFixupLast == 0 && _predicateEnvironment.numFixupPosition == 0) { foreach (var predicate in convertedPredicates) { nodeFilter.Body = _f.And(nodeFilter.Body, predicate); } nodeFilter.Body = _predicateEnvironment.fixupVisitor.Fixup(nodeFilter.Body, current, null); } //If any preidcate contains last() or position() node, then the current node is based on previous predicates, //for instance, a[...][2] is match second node after filter 'a[...]' instead of second 'a'. else { //filter out the siblings QilIterator parentIter = _f.For(_f.Parent(current)); QilNode sibling = _f.Content(parentIter); //generate filter based on input filter QilLoop siblingFilter = (QilLoop)nodeset.DeepClone(_f.BaseFactory); siblingFilter.Variable.Binding = sibling; siblingFilter = (QilLoop)_f.Loop(parentIter, siblingFilter); //build predicates from left to right to get all the matching nodes QilNode matchingSet = siblingFilter; foreach (var predicate in convertedPredicates) { matchingSet = XPathBuilder.BuildOnePredicate(matchingSet, predicate, /*isReverseStep*/ false, _f, _predicateEnvironment.fixupVisitor, ref _predicateEnvironment.numFixupCurrent, ref _predicateEnvironment.numFixupPosition, ref _predicateEnvironment.numFixupLast); } //check if the matching nodes contains the current node QilIterator matchNodeIter = _f.For(matchingSet); QilNode filterCurrent = _f.Filter(matchNodeIter, _f.Is(matchNodeIter, current)); nodeFilter.Body = _f.Not(_f.IsEmpty(filterCurrent)); //for passing type check, explicit say the result is target type nodeFilter.Body = _f.And(_f.IsType(current, nodeFilter.XmlType), nodeFilter.Body); } SetPriority(nodeset, 0.5); return(nodeset); }
private QilNode MatchPattern(QilIterator it, TemplateMatch match) { QilNode cond = match.Condition; if (cond == null) { return(_f.True()); } else { // We have to clone, because the same pattern may be used // in many different xsl:apply-templates/imports functions cond = cond.DeepClone(_f.BaseFactory); return(_refReplacer.Replace(cond, match.Iterator, it)); } }
private QilNode PlaceMarker(QilNode countPattern, QilNode fromPattern, bool multiple) { /* Quotation from XSLT 2.0 spec: * Let $A be the node sequence selected by the expression ancestor-or-self::node()[matches-count(.)] (level = "multiple") ancestor-or-self::node()[matches-count(.)][1] (level = "single") * Let $F be the node sequence selected by the expression ancestor-or-self::node()[matches-from(.)][1] * Let $AF be the value of $A intersect ($F/descendant-or-self::node()) * Return the result of the expression for $af in $AF return 1+count($af/preceding-sibling::node()[matches-count(.)]) NOTE: There are some distinctions between XSLT 1.0 and XSLT 2.0 specs. In our 1.0 implementation we: 1) Assume that the 'matches-from()' function does not match root nodes by default. 2) Instead of '$A intersect ($F/descendant-or-self::node())' (which, by the way, would filter out attribute and namespace nodes from $A) we calculate '$A' if the 'from' attribute is omitted, '$A[. >> $F]' if the 'from' attribute is present. */ QilNode countPattern2, countMatches, fromMatches, A, F, AF; QilIterator i, j; countPattern2 = (countPattern != null) ? countPattern.DeepClone(_f.BaseFactory) : null; countMatches = _f.Filter(i = _f.For(_f.AncestorOrSelf(GetCurrentNode())), MatchCountPattern(countPattern, i)); if (multiple) { A = _f.DocOrderDistinct(countMatches); } else { A = _f.Filter(i = _f.For(countMatches), _f.Eq(_f.PositionOf(i), _f.Int32(1))); } if (fromPattern == null) { AF = A; } else { fromMatches = _f.Filter(i = _f.For(_f.AncestorOrSelf(GetCurrentNode())), MatchPattern(fromPattern, i)); F = _f.Filter(i = _f.For(fromMatches), _f.Eq(_f.PositionOf(i), _f.Int32(1))); AF = _f.Loop(i = _f.For(F), _f.Filter(j = _f.For(A), _f.Before(i, j))); } return _f.Loop(j = _f.For(AF), _f.Add(_f.Int32(1), _f.Length(_f.Filter(i = _f.For(_f.PrecedingSibling(j)), MatchCountPattern(countPattern2, i)))) ); }
private void CompileDataTypeAttribute(string attValue, bool fwdCompat, ref QilNode select, out QilNode select2) { const string DtText = "text"; const string DtNumber = "number"; QilNode result = CompileStringAvt(attValue); if (result != null) { if (result.NodeType == QilNodeType.LiteralString) { string dataType = (string)(QilLiteral)result; if (dataType == DtNumber) { select = _f.ConvertToNumber(select); select2 = null; return; } else if (dataType == DtText) { // fall through to default case } else { if (!fwdCompat) { // check for qname-but-not-ncname string prefix, local, nsUri; bool isValid = _compiler.ParseQName(dataType, out prefix, out local, (IErrorHelper)this); nsUri = isValid ? ResolvePrefix(/*ignoreDefaultNs:*/true, prefix) : _compiler.CreatePhantomNamespace(); if (nsUri.Length == 0) { // this is a ncname; we might report SR.Xslt_InvalidAttrValue, // but the following error message is more user friendly } ReportError(/*[XT_034]*/SR.Xslt_BistateAttribute, "data-type", DtText, DtNumber); } // fall through to default case } } else { // Precalculate its value outside of for-each loop QilIterator dt, qname; result = _f.Loop(dt = _f.Let(result), _f.Conditional(_f.Eq(dt, _f.String(DtNumber)), _f.False(), _f.Conditional(_f.Eq(dt, _f.String(DtText)), _f.True(), fwdCompat ? _f.True() : _f.Loop(qname = _f.Let(ResolveQNameDynamic(/*ignoreDefaultNs:*/true, dt)), _f.Error(_lastScope.SourceLine, SR.Xslt_BistateAttribute, "data-type", DtText, DtNumber ) ) ))); QilIterator text = _f.Let(result); _varHelper.AddVariable(text); // Make two sort keys since heterogenous sort keys are not allowed select2 = select.DeepClone(_f.BaseFactory); select = _f.Conditional(text, _f.ConvertToString(select), _f.String(string.Empty)); select2 = _f.Conditional(text, _f.Double(0), _f.ConvertToNumber(select2)); return; } } // Default case select = _f.ConvertToString(select); select2 = null; }