Пример #1
0
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;
}
Пример #2
0
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);
    }
}
Пример #3
0
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();
        }
    }
}
Пример #4
0
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;
}
Пример #5
0
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;
}
Пример #6
0
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();
        }
    }
}
Пример #7
0
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;
}
Пример #8
0
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. */
    }
}
Пример #9
0
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();
}
Пример #10
0
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);
        }
    }
}
Пример #12
0
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 &param = 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;
}