/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtXmlPatterns module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qbuiltintypes_p.h" #include "qcommonsequencetypes_p.h" #include "qgenericsequencetype_p.h" #include "qnodesort_p.h" #include "qpatternistlocale_p.h" #include "qsequencemappingiterator_p.h" #include "qtypechecker_p.h" #include "qpath_p.h" QT_BEGIN_NAMESPACE using namespace QPatternist; Path::Path(const Expression::Ptr &operand1, const Expression::Ptr &operand2, const Kind kind) : PairContainer(operand1, operand2) , m_hasCreatedSorter(kind != RegularPath) , m_isLast(false) , m_checkXPTY0018(kind == RegularPath) , m_kind(kind) { } /*! \internal */ Path::~Path() { } Item::Iterator::Ptr Path::mapToSequence(const Item &item, const DynamicContext::Ptr &context) const { /* item is the focus here. That is in /1, item is . However, * we don't use it, since the context item is accessed through * DynamicContext::focusIterator() and friends. */ Q_ASSERT(item); Q_UNUSED(item); /* Needed when compiling in release mode. */ return m_operand2->evaluateSequence(context); } Item::Iterator::Ptr Path::evaluateSequence(const DynamicContext::Ptr &context) const { /* Note, we use the old context for m_operand1. */ const Item::Iterator::Ptr source(m_operand1->evaluateSequence(context)); const DynamicContext::Ptr focus(context->createFocus()); focus->setFocusIterator(source); const Item::Iterator::Ptr result(makeSequenceMappingIterator(ConstPtr(this), source, focus)); if(m_checkXPTY0018) { /* This is an expensive code path, but it should happen very rarely. */ enum FoundItem { FoundNone, FoundNode, FoundAtomicValue } hasFound = FoundNone; Item::List whenChecked; Item next(result->next()); while(next) { const FoundItem found = next.isAtomicValue() ? FoundAtomicValue : FoundNode; if(hasFound != FoundNone && hasFound != found) { /* It's an atomic value and we've already found a node. Mixed content. */ context->error(QtXmlPatterns::tr("The last step in a path must contain either nodes " "or atomic values. It cannot be a mixture between the two."), ReportContext::XPTY0018, this); } else hasFound = found; whenChecked.append(next); next = result->next(); } return makeListIterator(whenChecked); } else return result; } Item Path::evaluateSingleton(const DynamicContext::Ptr &context) const { /* This function is called if both operands' cardinality is exactly-one. Therefore * we manually go forward in the focus by calling next(). * * We don't check for XPTY0018, only in evaluateSequence(), since if we're guaranteed * to evaluate to one item, we can only evaluate to one node or one atomic value. */ /* Note, we use the old context for m_operand1. */ const Item::Iterator::Ptr source(m_operand1->evaluateSequence(context)); const DynamicContext::Ptr focus(context->createFocus()); focus->setFocusIterator(source); /* This test is needed because if the focus is empty, we don't want to(nor can't) evaluate * the next step. */ // TODO Why are we at all invoked then? if(source->next()) return m_operand2->evaluateSingleton(focus); else return Item(); } void Path::evaluateToSequenceReceiver(const DynamicContext::Ptr &context) const { /* Note, we use the old context for m_operand1. */ const Item::Iterator::Ptr source(m_operand1->evaluateSequence(context)); const DynamicContext::Ptr focus(context->createFocus()); focus->setFocusIterator(source); while(source->next()) m_operand2->evaluateToSequenceReceiver(focus); } Expression::Ptr Path::compress(const StaticContext::Ptr &context) { const Expression::Ptr me(PairContainer::compress(context)); /* "./expr" is by now equal to "expr" since we've done * focus/type checks, and a node sorter has been inserted. */ if(m_operand1->is(IDContextItem)) return m_operand2; /* We do this as late as we can, such that we pick up the most recent type * from the operand. */ if(m_isLast && m_kind != XSLTForEach && m_operand2->staticType()->itemType() == BuiltinTypes::item) m_checkXPTY0018 = true; return me; } Expression::Ptr Path::typeCheck(const StaticContext::Ptr &context, const SequenceType::Ptr &reqType) { m_operand2->announceFocusType(newFocusType()); /* Here we apply the function conversion first, and with the error code * that we want -- XPTY0019 instead of XPTY0004. Unfortunately * PairContainer::typeCheck() will do the type check again, which is * redundant in the case of when we're not XSLTForEach. * * If we're XSLTForEach, it means we're a synthetic "map" expression for * implementing various XSL-T expressions, and hence don't have the * constraint of XPTY0019. * * It's important that typeCheck() is run for the operands(of course), and the call to * PairContainer::typeCheck() ensures that below, in the case that we're XSL-T code. * * The type we expect, CommonSequenceTypes::ZeroOrMoreNodes() needs to be in sync with * what we return in expectedOperandTypes(). */ if(m_kind != XSLTForEach) { m_operand1 = TypeChecker::applyFunctionConversion(m_operand1, CommonSequenceTypes::ZeroOrMoreNodes, context, m_kind == ForApplyTemplate ? ReportContext::XTTE0520 : ReportContext::XPTY0019); } /* If our step ends with atomic values, we cannot sort. * * We must smack the NodeSortExpression ontop before calling typeCheck(), since the latter * may insert an Atomizer, as possibly mandated by reqType. By doing it after, the Atomizer * will be a parent to NodeSortExpression, as opposed to a child. */ if(!m_hasCreatedSorter) { m_hasCreatedSorter = true; return NodeSortExpression::wrapAround(Expression::Ptr(this), context)->typeCheck(context, reqType); } else return PairContainer::typeCheck(context, reqType); } SequenceType::List Path::expectedOperandTypes() const { SequenceType::List result; /* This value needs to be in sync with what we pass to * applyFunctionConversion() in typeCheck() above. * * We don't have the XPTY0019 restriction when we're synthetic XSL-T code. */ if(m_kind == XSLTForEach) result.append(CommonSequenceTypes::ZeroOrMoreItems); else result.append(CommonSequenceTypes::ZeroOrMoreNodes); result.append(CommonSequenceTypes::ZeroOrMoreItems); return result; } SequenceType::Ptr Path::staticType() const { const SequenceType::Ptr opType(m_operand2->staticType()); /* For each parent step, we evaluate the child step. So multiply the two * cardinalities. */ return makeGenericSequenceType(opType->itemType(), m_operand1->staticType()->cardinality() * opType->cardinality()); } ExpressionVisitorResult::Ptr Path::accept(const ExpressionVisitor::Ptr &visitor) const { return visitor->visit(this); } Expression::Properties Path::properties() const { return CreatesFocusForLast | ((m_operand1->properties() | m_operand2->properties()) & (RequiresCurrentItem | DisableElimination)); } ItemType::Ptr Path::newFocusType() const { return m_operand1->staticType()->itemType(); } Expression::ID Path::id() const { return IDPath; } QT_END_NAMESPACE