/src/xmlpatterns/functions/qpatternplatform.cpp

https://bitbucket.org/ultra_iter/qt-vtl · C++ · 301 lines · 187 code · 53 blank · 61 comment · 29 complexity · 40845ca7aeb06edbbf53cb357da3b6c5 MD5 · raw file

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