PatternPlatform::Flags PatternPlatform::parseFlags(const QString &flags, const DynamicContext::Ptr &context) const { if(flags.isEmpty()) return NoFlags; const PatternFlag::Hash flagDescrs(flagDescriptions()); const int len = flags.length(); Flags retval = NoFlags; for(int i = 0; i < len; ++i) { const QChar flag(flags.at(i)); const Flag specified = flagDescrs.value(flag).flag; if(specified != NoFlags) { retval |= specified; continue; } /* Generate a nice error message. */ QString message(QtXmlPatterns::tr("%1 is an invalid flag for regular expressions. Valid flags are:") .arg(formatKeyword(flag))); /* This is formatting, so don't bother translators with it. */ message.append(QLatin1Char('\n')); const PatternFlag::Hash::const_iterator end(flagDescrs.constEnd()); PatternFlag::Hash::const_iterator it(flagDescrs.constBegin()); for(; it != end;) { // TODO handle bidi correctly // TODO format this with rich text(list/table) message.append(formatKeyword(it.key())); message.append(QLatin1String(" - ")); message.append(it.value().description); ++it; if(it != end) message.append(QLatin1Char('\n')); } context->error(message, ReportContext::FORX0001, this); return NoFlags; } return retval; }
void ContextNodeChecker::checkTargetNode(const QXmlNodeModelIndex &node, const DynamicContext::Ptr &context, const ReportContext::ErrorCode code) const { if(node.root().kind() != QXmlNodeModelIndex::Document) { context->error(QtXmlPatterns::tr("The root node of the second argument " "to function %1 must be a document " "node. %2 is not a document node.") .arg(formatFunction(context->namePool(), signature()), formatData(node)), code, this); } }
Item ErrorFN::evaluateSingleton(const DynamicContext::Ptr &context) const { QString msg; switch(m_operands.count()) { case 0: /* No args. */ { context->error(QtXmlPatterns::tr("%1 was called.").arg(formatFunction(context->namePool(), signature())), ReportContext::FOER0000, this); return Item(); } case 3: /* Fallthrough, we don't use the 'error object' param. */ case 2: msg = m_operands.at(1)->evaluateSingleton(context).stringValue(); /* Fall through. */ case 1: { const QNameValue::Ptr qName(m_operands.first()->evaluateSingleton(context).as<QNameValue>()); if(qName) context->error(msg, qName->qName(), this); else context->error(msg, ReportContext::FOER0000, this); return Item(); } default: { Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid number of arguments passed to fn:error."); return Item(); } } }
QString CommentConstructor::evaluateContent(const DynamicContext::Ptr &context) const { const Item item(m_operand->evaluateSingleton(context)); if(!item) return QString(); const QString content(item.stringValue()); if(content.contains(QLatin1String("--"))) { context->error(QtXmlPatterns::tr("A comment cannot contain %1") .arg(formatData("--")), ReportContext::XQDY0072, this); } else if(content.endsWith(QLatin1Char('-'))) { context->error(QtXmlPatterns::tr("A comment cannot end with a %1.") .arg(formatData(QLatin1Char('-'))), ReportContext::XQDY0072, this); } return content; }
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<Item>(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::Iterator::Ptr EvaluationCache<IsForGlobal>::evaluateSequence(const DynamicContext::Ptr &context) const { ItemSequenceCacheCell::Vector &cells = IsForGlobal ? context->globalItemSequenceCacheCells(m_varSlot) : context->itemSequenceCacheCells(m_varSlot); ItemSequenceCacheCell &cell = cells[m_varSlot]; if(cell.inUse) { context->error(QtXmlPatterns::tr("Circularity detected"), ReportContext::XTDE0640, this); } switch(cell.cacheState) { case ItemSequenceCacheCell::Full: { /** * We don't use makeListIterator() here because the MIPSPro compiler can't handle it. */ return Item::Iterator::Ptr(new ListIterator<Item, Item::List>(cell.cachedItems)); } case ItemSequenceCacheCell::Empty: { cell.inUse = true; cell.sourceIterator = m_operand->evaluateSequence(IsForGlobal ? topFocusContext(context) : context); cell.cacheState = ItemSequenceCacheCell::PartiallyPopulated; /* Fallthrough. */ } case ItemSequenceCacheCell::PartiallyPopulated: { cell.inUse = false; Q_ASSERT_X(cells.at(m_varSlot).sourceIterator, Q_FUNC_INFO, "This trigger for a cache bug which hasn't yet been analyzed."); return Item::Iterator::Ptr(new CachingIterator(cells, m_varSlot, IsForGlobal ? topFocusContext(context) : context)); } default: { Q_ASSERT_X(false, Q_FUNC_INFO, "This path is not supposed to be run."); return Item::Iterator::Ptr(); } } }
Template::Ptr ApplyTemplate::findTemplate(const DynamicContext::Ptr &context, const TemplateMode::Ptr &templateMode) const { const int count = templateMode->templatePatterns.count(); Template::Ptr result; /* It's redundant to initialize these values, but it suppresses false * positives with GCC. */ PatternPriority priority = 0; TemplatePattern::ID id = -1; /* Possible optimization: detecting ambiguous rule matches could be forked off to a * low prioirity thread. */ for(int i = 0; i < count; ++i) { const TemplatePattern::Ptr &candidate = templateMode->templatePatterns.at(i); if(candidate->matchPattern()->evaluateEBV(context)) { if(result) { if( candidate->id() != id && candidate->priority() == priority && candidate->templateTarget()->importPrecedence == result->importPrecedence) { context->error(QtXmlPatterns::tr("Ambiguous rule match."), ReportContext::XTRE0540, this); } else break; } else { result = candidate->templateTarget(); priority = candidate->priority(); id = candidate->id(); } } } return result; }
Item DateTimeFN::evaluateSingleton(const DynamicContext::Ptr &context) const { const Item di(m_operands.first()->evaluateSingleton(context)); if(!di) return Item(); const Item ti(m_operands.last()->evaluateSingleton(context)); if(!ti) return Item(); QDateTime date(di.as<AbstractDateTime>()->toDateTime()); Q_ASSERT(date.isValid()); QDateTime time(ti.as<AbstractDateTime>()->toDateTime()); Q_ASSERT(time.isValid()); if(date.timeSpec() == time.timeSpec() || /* Identical timezone properties. */ time.timeSpec() == Qt::LocalTime) /* time has no timezone, but date do. */ { date.setTime(time.time()); Q_ASSERT(date.isValid()); return DateTime::fromDateTime(date); } else if(date.timeSpec() == Qt::LocalTime) /* date has no timezone, but time do. */ { time.setDate(date.date()); Q_ASSERT(time.isValid()); return DateTime::fromDateTime(time); } else { context->error(QtXmlPatterns::tr("If both values have zone offsets, " "they must have the same zone offset. " "%1 and %2 are not the same.") .arg(formatData(di.stringValue()), formatData(di.stringValue())), ReportContext::FORG0008, this); return Item(); /* Silence GCC warning. */ } }
QString ProcessingInstructionConstructor::data(const DynamicContext::Ptr &context) const { const Item name(m_operand1->evaluateSingleton(context)); const Item dataArg(m_operand2->evaluateSingleton(context)); if(dataArg) { /* Perform trimming before validation, to increase speed. */ const QString value(leftTrimmed(dataArg.stringValue())); if(value.contains(QLatin1String("?>"))) { context->error(QtXmlPatterns::tr("The data of a processing instruction cannot contain the string %1").arg(formatData("?>")), ReportContext::XQDY0026, this); return QString(); } else return value; } else return QString(); }
QString ReplaceFN::parseReplacement(const int, const DynamicContext::Ptr &context) const { // TODO what if there is no groups, can one rewrite to the replacement then? const QString input(m_operands.at(2)->evaluateSingleton(context).stringValue()); QString retval; retval.reserve(input.size()); const int len = input.length(); for(int i = 0; i < len; ++i) { const QChar ch(input.at(i)); switch(ch.toAscii()) { case '$': { /* QRegExp uses '\' as opposed to '$' for marking sub groups. */ retval.append(QLatin1Char('\\')); ++i; if(i == len) { context->error(errorAtEnd('$'), ReportContext::FORX0004, this); return QString(); } const QChar nextCh(input.at(i)); if(nextCh.isDigit()) retval.append(nextCh); else { context->error(QtXmlPatterns::tr("In the replacement string, %1 must be " "followed by at least one digit when not escaped.") .arg(formatKeyword(QLatin1Char('$'))), ReportContext::FORX0004, this); return QString(); } break; } case '\\': { ++i; if(i == len) { /* error, we've reached the end. */; context->error(errorAtEnd('\\'), ReportContext::FORX0004, this); } const QChar nextCh(input.at(i)); if(nextCh == QLatin1Char('\\') || nextCh == QLatin1Char('$')) { retval.append(ch); break; } else { context->error(QtXmlPatterns::tr("In the replacement string, %1 can only be used to " "escape itself or %2, not %3") .arg(formatKeyword(QLatin1Char('\\'))) .arg(formatKeyword(QLatin1Char('$'))) .arg(formatKeyword(nextCh)), ReportContext::FORX0004, this); return QString(); } } default: retval.append(ch); } } return retval; }
Item AdjustTimezone::evaluateSingleton(const DynamicContext::Ptr &context) const { enum { /** * The maximum zone offset, @c PT14H, in milli seconds. */ MSecLimit = 14 * 60/*M*/ * 60/*S*/ * 1000/*ms*/ }; const Item arg(m_operands.first()->evaluateSingleton(context)); if(!arg) return Item(); QDateTime dt(arg.as<AbstractDateTime>()->toDateTime()); // TODO DT dt.setDateOnly(false); Q_ASSERT(dt.isValid()); DayTimeDuration::Ptr tz; if(m_operands.count() == 2) tz = DayTimeDuration::Ptr(m_operands.at(1)->evaluateSingleton(context).as<DayTimeDuration>()); else tz = context->implicitTimezone(); if(tz) { const MSecondCountProperty tzMSecs = tz->value(); if(tzMSecs % (1000 * 60) != 0) { context->error(QtXmlPatterns::tr("A zone offset must be in the " "range %1..%2 inclusive. %3 is " "out of range.") .arg(formatData("-PT14H")) .arg(formatData("PT14H")) .arg(formatData(tz->stringValue())), ReportContext::FODT0003, this); return Item(); } else if(tzMSecs > MSecLimit || tzMSecs < -MSecLimit) { context->error(QtXmlPatterns::tr("%1 is not a whole number of minutes.") .arg(formatData(tz->stringValue())), ReportContext::FODT0003, this); return Item(); } const SecondCountProperty tzSecs = tzMSecs / 1000; if(dt.timeSpec() == Qt::LocalTime) /* $arg has no time zone. */ { /* "If $arg does not have a timezone component and $timezone is not * the empty sequence, then the result is $arg with $timezone as * the timezone component." */ //dt.setTimeSpec(QDateTime::Spec(QDateTime::OffsetFromUTC, tzSecs)); dt.setUtcOffset(tzSecs); Q_ASSERT(dt.isValid()); return createValue(dt); } else { /* "If $arg has a timezone component and $timezone is not the empty sequence, * then the result is an xs:dateTime value with a timezone component of * $timezone that is equal to $arg." */ dt = dt.toUTC(); dt = dt.addSecs(tzSecs); //dt.setTimeSpec(QDateTime::Spec(QDateTime::OffsetFromUTC, tzSecs)); dt.setUtcOffset(tzSecs); Q_ASSERT(dt.isValid()); return createValue(dt); } } else { /* $timezone is the empty sequence. */ if(dt.timeSpec() == Qt::LocalTime) /* $arg has no time zone. */ { /* "If $arg does not have a timezone component and $timezone is * the empty sequence, then the result is $arg." */ return arg; } else { /* "If $arg has a timezone component and $timezone is the empty sequence, * then the result is the localized value of $arg without its timezone component." */ dt.setTimeSpec(Qt::LocalTime); return createValue(dt); } } }
DynamicContext::Ptr Template::createContext(const TemplateInvoker *const invoker, const DynamicContext::Ptr &context, const bool isCallTemplate) const { Q_ASSERT(invoker); Q_ASSERT(context); /* We have: * - xsl:params in the target template (if any) which may provide * default values. * - xsl:with-params in the caller (if any) which provides values. * * We need to, for each parameter: * - If the called template provides no default value and the caller * has no value, it's an error * - If the called template has a default value and the caller provides * none, it should be used * - In any case the caller provides a value, it needs to be used. * * Problems to look out for: * * - Each xsl:param is in scope for the subsequent xsl:params. Hence, * the evaluation of one xsl:param can depend on another xsl:param, * and so on * - The focus for xsl:params is different from the focus for * the xsl:with-params * - The xsl:with-params are not in scope for the xsl:params. */ WithParam::Hash withParams(invoker->withParams()); /** * Parameters or not, we must in any case create a new stack frame * for the template invocation since otherwise we will trash our existing * variables. Hence it's as with calling user functions. * * This is especially reproducible with recursive functions. */ DynamicContext::Ptr newStack(context->createStack()); /* We have no parameters, and we have no further error checking to * do in the case of not being xsl:apply-templates, so we need to do nothing. */ if(templateParameters.isEmpty() && (!isCallTemplate || withParams.isEmpty())) return newStack; const DynamicContext::TemplateParameterHash hashedParams(parametersAsHash()); DynamicContext::TemplateParameterHash sewnTogether(hashedParams); const DynamicContext::TemplateParameterHash::iterator end(sewnTogether.end()); for(DynamicContext::TemplateParameterHash::iterator it(sewnTogether.begin()); it != end; ++it) { Expression::Ptr ¶m = it.value(); WithParam::Ptr &withParam = withParams[it.key()]; if(withParam) param = Expression::Ptr(new DynamicContextStore(withParam->sourceExpression(), context)); else if(!param) { /* Ops, no xsl:with-param and no default value to cover up for it. */ context->error(QtXmlPatterns::tr("The parameter %1 is required, but no corresponding %2 is supplied.") .arg(formatKeyword(context->namePool(), it.key()), formatKeyword(QLatin1String("xsl:with-param"))), ReportContext::XTSE0690, this); } } if(isCallTemplate) { /* Find xsl:with-param that has no corresponding xsl:param. */ /* Optimization: candidate for threading? */ const WithParam::Hash::const_iterator end(withParams.constEnd()); for(WithParam::Hash::const_iterator it(withParams.constBegin()); it != end; ++it) { if(!hashedParams.contains(it.key())) raiseXTSE0680(context, it.key(), this); } } newStack->templateParameterStore() = sewnTogether; return newStack; }