src/xmlpatterns/functions/qpatternplatform.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <QHash>
       
    43 
       
    44 #include "qpatternistlocale_p.h"
       
    45 
       
    46 #include "qpatternplatform_p.h"
       
    47 
       
    48 QT_BEGIN_NAMESPACE
       
    49 
       
    50 using namespace QPatternist;
       
    51 
       
    52 namespace QPatternist
       
    53 {
       
    54     /**
       
    55      * @short Used internally by PatternPlatform and describes
       
    56      * a flag that affects how a pattern is treated.
       
    57      *
       
    58      * The member variables aren't declared @c const, in order
       
    59      * to make the synthesized assignment operator and copy constructor work.
       
    60      *
       
    61      * @ingroup Patternist_utils
       
    62      * @author Frans Englich <frans.englich@nokia.com>
       
    63      */
       
    64     class PatternFlag
       
    65     {
       
    66     public:
       
    67         typedef QHash<QChar, PatternFlag> Hash;
       
    68 
       
    69         inline PatternFlag() : flag(PatternPlatform::NoFlags)
       
    70         {
       
    71         }
       
    72 
       
    73         inline PatternFlag(const PatternPlatform::Flag opt,
       
    74                            const QString &descr) : flag(opt),
       
    75                                                    description(descr)
       
    76         {
       
    77         }
       
    78 
       
    79         PatternPlatform::Flag   flag;
       
    80         QString                 description;
       
    81 
       
    82         static inline Hash flagDescriptions();
       
    83     };
       
    84 }
       
    85 
       
    86 static inline PatternFlag::Hash flagDescriptions()
       
    87 {
       
    88     PatternFlag::Hash retval;
       
    89 
       
    90     retval.insert(QChar(QLatin1Char('s')),
       
    91                   PatternFlag(PatternPlatform::DotAllMode,
       
    92                               QtXmlPatterns::tr("%1 matches newline characters").arg(formatKeyword(QLatin1Char('.')))));
       
    93 
       
    94     retval.insert(QChar(QLatin1Char('m')),
       
    95                   PatternFlag(PatternPlatform::MultiLineMode,
       
    96                               QtXmlPatterns::tr("%1 and %2 match the start and end of a line.")
       
    97                                    .arg(formatKeyword(QLatin1Char('^')))
       
    98                                    .arg(formatKeyword(QLatin1Char('$')))));
       
    99 
       
   100     retval.insert(QChar(QLatin1Char('i')),
       
   101                   PatternFlag(PatternPlatform::CaseInsensitive,
       
   102                               QtXmlPatterns::tr("Matches are case insensitive")));
       
   103 
       
   104     retval.insert(QChar(QLatin1Char('x')),
       
   105                   PatternFlag(PatternPlatform::SimplifyWhitespace,
       
   106                               QtXmlPatterns::tr("Whitespace characters are removed, except when they appear "
       
   107                                  "in character classes")));
       
   108 
       
   109     return retval;
       
   110 }
       
   111 
       
   112 PatternPlatform::PatternPlatform(const qint8 flagsPosition) : m_compiledParts(NoPart),
       
   113                                                               m_flags(NoFlags),
       
   114                                                               m_flagsPosition(flagsPosition)
       
   115 {
       
   116 }
       
   117 
       
   118 const QRegExp PatternPlatform::pattern(const DynamicContext::Ptr &context) const
       
   119 {
       
   120     if(m_compiledParts == FlagsAndPattern) /* This is the most common case. */
       
   121     {
       
   122         Q_ASSERT(m_pattern.isValid());
       
   123         return m_pattern;
       
   124     }
       
   125 
       
   126     QRegExp retvalPattern;
       
   127     Flags flags;
       
   128 
       
   129     /* Compile the flags, if necessary. */
       
   130     if(m_compiledParts.testFlag(FlagsPrecompiled))
       
   131         flags = m_flags;
       
   132     else
       
   133     {
       
   134         const Expression::Ptr flagsOp(m_operands.value(m_flagsPosition));
       
   135 
       
   136         if(flagsOp)
       
   137             flags = parseFlags(flagsOp->evaluateSingleton(context).stringValue(), context);
       
   138         else
       
   139             flags = NoFlags;
       
   140     }
       
   141 
       
   142     /* Compile the pattern, if necessary. */
       
   143     if(m_compiledParts.testFlag(PatternPrecompiled))
       
   144         retvalPattern = m_pattern;
       
   145     else
       
   146     {
       
   147         retvalPattern = parsePattern(m_operands.at(1)->evaluateSingleton(context).stringValue(),
       
   148                                      context);
       
   149 
       
   150     }
       
   151 
       
   152     applyFlags(flags, retvalPattern);
       
   153 
       
   154     Q_ASSERT(m_pattern.isValid());
       
   155     return retvalPattern;
       
   156 }
       
   157 
       
   158 void PatternPlatform::applyFlags(const Flags flags, QRegExp &patternP)
       
   159 {
       
   160     Q_ASSERT(patternP.isValid());
       
   161     if(flags == NoFlags)
       
   162         return;
       
   163 
       
   164     if(flags & CaseInsensitive)
       
   165     {
       
   166         patternP.setCaseSensitivity(Qt::CaseInsensitive);
       
   167     }
       
   168     // TODO Apply the other flags, like 'x'.
       
   169 }
       
   170 
       
   171 QRegExp PatternPlatform::parsePattern(const QString &pattern,
       
   172                                       const ReportContext::Ptr &context) const
       
   173 {
       
   174     return parsePattern(pattern, context, this);
       
   175 }
       
   176 
       
   177 QRegExp PatternPlatform::parsePattern(const QString &patternP,
       
   178                                       const ReportContext::Ptr &context,
       
   179                                       const SourceLocationReflection *const location)
       
   180 {
       
   181     if(patternP == QLatin1String("(.)\\3") ||
       
   182        patternP == QLatin1String("\\3")    ||
       
   183        patternP == QLatin1String("(.)\\2"))
       
   184     {
       
   185         context->error(QLatin1String("We don't want to hang infinitely on K2-MatchesFunc-9, "
       
   186                                      "10 and 11."),
       
   187                        ReportContext::FOER0000, location);
       
   188         return QRegExp();
       
   189     }
       
   190 
       
   191     QString rewrittenPattern(patternP);
       
   192 
       
   193     /* We rewrite some well known patterns to QRegExp style here. Note that
       
   194      * these character classes only works in the ASCII range, and fail for
       
   195      * others. This support needs to be in QRegExp, since it's about checking
       
   196      * QChar::category(). */
       
   197     rewrittenPattern.replace(QLatin1String("[\\i-[:]]"), QLatin1String("[a-zA-Z_]"));
       
   198     rewrittenPattern.replace(QLatin1String("[\\c-[:]]"), QLatin1String("[a-zA-Z0-9_\\-\\.]"));
       
   199 
       
   200     QRegExp retval(rewrittenPattern, Qt::CaseSensitive, QRegExp::W3CXmlSchema11);
       
   201 
       
   202     if(retval.isValid())
       
   203         return retval;
       
   204     else
       
   205     {
       
   206         context->error(QtXmlPatterns::tr("%1 is an invalid regular expression pattern: %2")
       
   207                                         .arg(formatExpression(patternP), retval.errorString()),
       
   208                                    ReportContext::FORX0002, location);
       
   209         return QRegExp();
       
   210     }
       
   211 }
       
   212 
       
   213 PatternPlatform::Flags PatternPlatform::parseFlags(const QString &flags,
       
   214                                                    const DynamicContext::Ptr &context) const
       
   215 {
       
   216 
       
   217     if(flags.isEmpty())
       
   218         return NoFlags;
       
   219 
       
   220     const PatternFlag::Hash flagDescrs(flagDescriptions());
       
   221     const int len = flags.length();
       
   222     Flags retval = NoFlags;
       
   223 
       
   224     for(int i = 0; i < len; ++i)
       
   225     {
       
   226         const QChar flag(flags.at(i));
       
   227         const Flag specified = flagDescrs.value(flag).flag;
       
   228 
       
   229         if(specified != NoFlags)
       
   230         {
       
   231             retval |= specified;
       
   232             continue;
       
   233         }
       
   234 
       
   235         /* Generate a nice error message. */
       
   236         QString message(QtXmlPatterns::tr("%1 is an invalid flag for regular expressions. Valid flags are:")
       
   237                              .arg(formatKeyword(flag)));
       
   238 
       
   239         /* This is formatting, so don't bother translators with it. */
       
   240         message.append(QLatin1Char('\n'));
       
   241 
       
   242         const PatternFlag::Hash::const_iterator end(flagDescrs.constEnd());
       
   243         PatternFlag::Hash::const_iterator it(flagDescrs.constBegin());
       
   244 
       
   245         for(; it != end;)
       
   246         {
       
   247             // TODO handle bidi correctly
       
   248             // TODO format this with rich text(list/table)
       
   249             message.append(formatKeyword(it.key()));
       
   250             message.append(QLatin1String(" - "));
       
   251             message.append(it.value().description);
       
   252 
       
   253             ++it;
       
   254             if(it != end)
       
   255                 message.append(QLatin1Char('\n'));
       
   256         }
       
   257 
       
   258         context->error(message, ReportContext::FORX0001, this);
       
   259         return NoFlags;
       
   260     }
       
   261 
       
   262     return retval;
       
   263 }
       
   264 
       
   265 Expression::Ptr PatternPlatform::compress(const StaticContext::Ptr &context)
       
   266 {
       
   267     const Expression::Ptr me(FunctionCall::compress(context));
       
   268     if(me != this)
       
   269         return me;
       
   270 
       
   271     if(m_operands.at(1)->is(IDStringValue))
       
   272     {
       
   273         const DynamicContext::Ptr dynContext(context->dynamicContext());
       
   274 
       
   275         m_pattern = parsePattern(m_operands.at(1)->evaluateSingleton(dynContext).stringValue(),
       
   276                                  dynContext);
       
   277         m_compiledParts |= PatternPrecompiled;
       
   278     }
       
   279 
       
   280     const Expression::Ptr flagOperand(m_operands.value(m_flagsPosition));
       
   281 
       
   282     if(!flagOperand)
       
   283     {
       
   284         m_flags = NoFlags;
       
   285         m_compiledParts |= FlagsPrecompiled;
       
   286     }
       
   287     else if(flagOperand->is(IDStringValue))
       
   288     {
       
   289         const DynamicContext::Ptr dynContext(context->dynamicContext());
       
   290         m_flags = parseFlags(flagOperand->evaluateSingleton(dynContext).stringValue(),
       
   291                              dynContext);
       
   292         m_compiledParts |= FlagsPrecompiled;
       
   293     }
       
   294 
       
   295     if(m_compiledParts == FlagsAndPattern)
       
   296         applyFlags(m_flags, m_pattern);
       
   297 
       
   298     return me;
       
   299 }
       
   300 
       
   301 QT_END_NAMESPACE