1// Copyright (C) 2020 Giuseppe D'Angelo <dangelog@gmail.com>.
2// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
3// Copyright (C) 2021 The Qt Company Ltd.
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6#ifndef QREGULAREXPRESSION_H
7#define QREGULAREXPRESSION_H
8
9#include <QtCore/qglobal.h>
10#include <QtCore/qstring.h>
11#include <QtCore/qstringview.h>
12#include <QtCore/qshareddata.h>
13#include <QtCore/qvariant.h>
14
15#include <iterator>
16
17QT_REQUIRE_CONFIG(regularexpression);
18
19QT_BEGIN_NAMESPACE
20
21class QRegularExpressionMatch;
22class QRegularExpressionMatchIterator;
23struct QRegularExpressionPrivate;
24class QRegularExpression;
25
26QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QRegularExpressionPrivate, Q_CORE_EXPORT)
27
28Q_CORE_EXPORT size_t qHash(const QRegularExpression &key, size_t seed = 0) noexcept;
29
30class Q_CORE_EXPORT QRegularExpression
31{
32public:
33 enum PatternOption {
34 NoPatternOption = 0x0000,
35 CaseInsensitiveOption = 0x0001,
36 DotMatchesEverythingOption = 0x0002,
37 MultilineOption = 0x0004,
38 ExtendedPatternSyntaxOption = 0x0008,
39 InvertedGreedinessOption = 0x0010,
40 DontCaptureOption = 0x0020,
41 UseUnicodePropertiesOption = 0x0040,
42 // Formerly (no-ops deprecated in 5.12, removed 6.0):
43 // OptimizeOnFirstUsageOption = 0x0080,
44 // DontAutomaticallyOptimizeOption = 0x0100,
45 };
46 Q_DECLARE_FLAGS(PatternOptions, PatternOption)
47
48 PatternOptions patternOptions() const;
49 void setPatternOptions(PatternOptions options);
50
51 QRegularExpression();
52 explicit QRegularExpression(const QString &pattern, PatternOptions options = NoPatternOption);
53 QRegularExpression(const QRegularExpression &re) noexcept;
54 QRegularExpression(QRegularExpression &&re) = default;
55 ~QRegularExpression();
56 QRegularExpression &operator=(const QRegularExpression &re) noexcept;
57 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QRegularExpression)
58
59 void swap(QRegularExpression &other) noexcept { d.swap(other&: other.d); }
60
61 QString pattern() const;
62 void setPattern(const QString &pattern);
63
64 [[nodiscard]]
65 bool isValid() const;
66 qsizetype patternErrorOffset() const;
67 QString errorString() const;
68
69 int captureCount() const;
70 QStringList namedCaptureGroups() const;
71
72 enum MatchType {
73 NormalMatch = 0,
74 PartialPreferCompleteMatch,
75 PartialPreferFirstMatch,
76 NoMatch
77 };
78
79 enum MatchOption {
80 NoMatchOption = 0x0000,
81 AnchorAtOffsetMatchOption = 0x0001,
82 AnchoredMatchOption Q_DECL_ENUMERATOR_DEPRECATED_X(
83 "Use AnchorAtOffsetMatchOption instead") = AnchorAtOffsetMatchOption, // Rename@Qt6.0
84 DontCheckSubjectStringMatchOption = 0x0002
85 };
86 Q_DECLARE_FLAGS(MatchOptions, MatchOption)
87
88 [[nodiscard]]
89 QRegularExpressionMatch match(const QString &subject,
90 qsizetype offset = 0,
91 MatchType matchType = NormalMatch,
92 MatchOptions matchOptions = NoMatchOption) const;
93
94#if QT_DEPRECATED_SINCE(6, 8)
95 [[nodiscard]]
96 QT_DEPRECATED_VERSION_X_6_8("Use matchView instead.")
97 QRegularExpressionMatch match(QStringView subjectView,
98 qsizetype offset = 0,
99 MatchType matchType = NormalMatch,
100 MatchOptions matchOptions = NoMatchOption) const;
101#endif
102
103 [[nodiscard]]
104 QRegularExpressionMatch matchView(QStringView subjectView,
105 qsizetype offset = 0,
106 MatchType matchType = NormalMatch,
107 MatchOptions matchOptions = NoMatchOption) const;
108
109 [[nodiscard]]
110 QRegularExpressionMatchIterator globalMatch(const QString &subject,
111 qsizetype offset = 0,
112 MatchType matchType = NormalMatch,
113 MatchOptions matchOptions = NoMatchOption) const;
114
115#if QT_DEPRECATED_SINCE(6, 8)
116 [[nodiscard]]
117 QT_DEPRECATED_VERSION_X_6_8("Use globalMatchView instead.")
118 QRegularExpressionMatchIterator globalMatch(QStringView subjectView,
119 qsizetype offset = 0,
120 MatchType matchType = NormalMatch,
121 MatchOptions matchOptions = NoMatchOption) const;
122#endif
123
124 [[nodiscard]]
125 QRegularExpressionMatchIterator globalMatchView(QStringView subjectView,
126 qsizetype offset = 0,
127 MatchType matchType = NormalMatch,
128 MatchOptions matchOptions = NoMatchOption) const;
129
130 void optimize() const;
131
132 enum WildcardConversionOption {
133 DefaultWildcardConversion = 0x0,
134 UnanchoredWildcardConversion = 0x1,
135 NonPathWildcardConversion = 0x2,
136 };
137 Q_DECLARE_FLAGS(WildcardConversionOptions, WildcardConversionOption)
138
139 static QString escape(const QString &str)
140 {
141 return escape(str: qToStringViewIgnoringNull(s: str));
142 }
143
144 static QString wildcardToRegularExpression(const QString &str, WildcardConversionOptions options = DefaultWildcardConversion)
145 {
146 return wildcardToRegularExpression(str: qToStringViewIgnoringNull(s: str), options);
147 }
148
149 static inline QString anchoredPattern(const QString &expression)
150 {
151 return anchoredPattern(expression: qToStringViewIgnoringNull(s: expression));
152 }
153
154 static QString escape(QStringView str);
155 static QString wildcardToRegularExpression(QStringView str, WildcardConversionOptions options = DefaultWildcardConversion);
156 static QString anchoredPattern(QStringView expression);
157
158 static QRegularExpression fromWildcard(QStringView pattern, Qt::CaseSensitivity cs = Qt::CaseInsensitive,
159 WildcardConversionOptions options = DefaultWildcardConversion);
160
161 bool operator==(const QRegularExpression &re) const;
162 inline bool operator!=(const QRegularExpression &re) const { return !operator==(re); }
163
164private:
165 friend struct QRegularExpressionPrivate;
166 friend class QRegularExpressionMatch;
167 friend struct QRegularExpressionMatchPrivate;
168 friend class QRegularExpressionMatchIterator;
169 friend Q_CORE_EXPORT size_t qHash(const QRegularExpression &key, size_t seed) noexcept;
170
171 QRegularExpression(QRegularExpressionPrivate &dd);
172 QExplicitlySharedDataPointer<QRegularExpressionPrivate> d;
173};
174
175Q_DECLARE_SHARED(QRegularExpression)
176Q_DECLARE_OPERATORS_FOR_FLAGS(QRegularExpression::PatternOptions)
177Q_DECLARE_OPERATORS_FOR_FLAGS(QRegularExpression::MatchOptions)
178Q_DECLARE_OPERATORS_FOR_FLAGS(QRegularExpression::WildcardConversionOptions)
179
180#ifndef QT_NO_DATASTREAM
181Q_CORE_EXPORT QDataStream &operator<<(QDataStream &out, const QRegularExpression &re);
182Q_CORE_EXPORT QDataStream &operator>>(QDataStream &in, QRegularExpression &re);
183#endif
184
185#ifndef QT_NO_DEBUG_STREAM
186Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QRegularExpression &re);
187Q_CORE_EXPORT QDebug operator<<(QDebug debug, QRegularExpression::PatternOptions patternOptions);
188#endif
189
190struct QRegularExpressionMatchPrivate;
191QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QRegularExpressionMatchPrivate, Q_CORE_EXPORT)
192
193class Q_CORE_EXPORT QRegularExpressionMatch
194{
195public:
196 QRegularExpressionMatch();
197 ~QRegularExpressionMatch();
198 QRegularExpressionMatch(const QRegularExpressionMatch &match);
199 QRegularExpressionMatch(QRegularExpressionMatch &&match) = default;
200 QRegularExpressionMatch &operator=(const QRegularExpressionMatch &match);
201 QRegularExpressionMatch &operator=(QRegularExpressionMatch &&match) noexcept
202 { d.swap(other&: match.d); return *this; }
203 void swap(QRegularExpressionMatch &other) noexcept { d.swap(other&: other.d); }
204
205 QRegularExpression regularExpression() const;
206 QRegularExpression::MatchType matchType() const;
207 QRegularExpression::MatchOptions matchOptions() const;
208
209 bool hasMatch() const;
210 bool hasPartialMatch() const;
211
212 bool isValid() const;
213
214 int lastCapturedIndex() const;
215
216 bool hasCaptured(const QString &name) const
217 { return hasCaptured(name: QStringView(name)); }
218 bool hasCaptured(QStringView name) const;
219 bool hasCaptured(int nth) const;
220
221 QString captured(int nth = 0) const;
222 QStringView capturedView(int nth = 0) const;
223
224 QString captured(const QString &name) const
225 { return captured(name: QStringView(name)); }
226 QString captured(QStringView name) const;
227 QStringView capturedView(QStringView name) const;
228
229 QStringList capturedTexts() const;
230
231 qsizetype capturedStart(int nth = 0) const;
232 qsizetype capturedLength(int nth = 0) const;
233 qsizetype capturedEnd(int nth = 0) const;
234
235 qsizetype capturedStart(const QString &name) const
236 { return capturedStart(name: QStringView(name)); }
237 qsizetype capturedLength(const QString &name) const
238 { return capturedLength(name: QStringView(name)); }
239 qsizetype capturedEnd(const QString &name) const
240 { return capturedEnd(name: QStringView(name)); }
241
242 qsizetype capturedStart(QStringView name) const;
243 qsizetype capturedLength(QStringView name) const;
244 qsizetype capturedEnd(QStringView name) const;
245
246private:
247 friend class QRegularExpression;
248 friend struct QRegularExpressionMatchPrivate;
249 friend class QRegularExpressionMatchIterator;
250
251 QRegularExpressionMatch(QRegularExpressionMatchPrivate &dd);
252 QExplicitlySharedDataPointer<QRegularExpressionMatchPrivate> d;
253};
254
255Q_DECLARE_SHARED(QRegularExpressionMatch)
256
257#ifndef QT_NO_DEBUG_STREAM
258Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QRegularExpressionMatch &match);
259#endif
260
261namespace QtPrivate {
262class QRegularExpressionMatchIteratorRangeBasedForIterator;
263class QRegularExpressionMatchIteratorRangeBasedForIteratorSentinel {};
264}
265
266struct QRegularExpressionMatchIteratorPrivate;
267QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QRegularExpressionMatchIteratorPrivate, Q_CORE_EXPORT)
268
269class Q_CORE_EXPORT QRegularExpressionMatchIterator
270{
271public:
272 QRegularExpressionMatchIterator();
273 ~QRegularExpressionMatchIterator();
274 QRegularExpressionMatchIterator(const QRegularExpressionMatchIterator &iterator);
275 QRegularExpressionMatchIterator(QRegularExpressionMatchIterator &&iterator) = default;
276 QRegularExpressionMatchIterator &operator=(const QRegularExpressionMatchIterator &iterator);
277 QRegularExpressionMatchIterator &operator=(QRegularExpressionMatchIterator &&iterator) noexcept
278 { d.swap(other&: iterator.d); return *this; }
279 void swap(QRegularExpressionMatchIterator &other) noexcept { d.swap(other&: other.d); }
280
281 bool isValid() const;
282
283 bool hasNext() const;
284 QRegularExpressionMatch next();
285 QRegularExpressionMatch peekNext() const;
286
287 QRegularExpression regularExpression() const;
288 QRegularExpression::MatchType matchType() const;
289 QRegularExpression::MatchOptions matchOptions() const;
290
291private:
292 friend class QRegularExpression;
293 friend Q_CORE_EXPORT QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator);
294 friend QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIteratorSentinel end(const QRegularExpressionMatchIterator &) { return {}; }
295
296 QRegularExpressionMatchIterator(QRegularExpressionMatchIteratorPrivate &dd);
297 QExplicitlySharedDataPointer<QRegularExpressionMatchIteratorPrivate> d;
298};
299
300namespace QtPrivate {
301
302// support for range-based for loop
303class QRegularExpressionMatchIteratorRangeBasedForIterator
304{
305public:
306 using value_type = QRegularExpressionMatch;
307 using difference_type = int;
308 using reference_type = const QRegularExpressionMatch &;
309 using pointer_type = const QRegularExpressionMatch *;
310 using iterator_category = std::forward_iterator_tag;
311
312 QRegularExpressionMatchIteratorRangeBasedForIterator()
313 : m_atEnd(true)
314 {
315 }
316
317 explicit QRegularExpressionMatchIteratorRangeBasedForIterator(const QRegularExpressionMatchIterator &iterator)
318 : m_matchIterator(iterator),
319 m_currentMatch(),
320 m_atEnd(false)
321 {
322 ++*this;
323 }
324
325 const QRegularExpressionMatch &operator*() const
326 {
327 Q_ASSERT_X(!m_atEnd, Q_FUNC_INFO, "operator* called on an iterator already at the end");
328 return m_currentMatch;
329 }
330
331 QRegularExpressionMatchIteratorRangeBasedForIterator &operator++()
332 {
333 Q_ASSERT_X(!m_atEnd, Q_FUNC_INFO, "operator++ called on an iterator already at the end");
334 if (m_matchIterator.hasNext()) {
335 m_currentMatch = m_matchIterator.next();
336 } else {
337 m_currentMatch = QRegularExpressionMatch();
338 m_atEnd = true;
339 }
340
341 return *this;
342 }
343
344 QRegularExpressionMatchIteratorRangeBasedForIterator operator++(int)
345 {
346 QRegularExpressionMatchIteratorRangeBasedForIterator i = *this;
347 ++*this;
348 return i;
349 }
350
351private:
352 // [input.iterators] imposes operator== on us. Unfortunately, it's not
353 // trivial to implement, so just do the bare minimum to satifisfy
354 // Cpp17EqualityComparable.
355 friend bool operator==(const QRegularExpressionMatchIteratorRangeBasedForIterator &lhs,
356 const QRegularExpressionMatchIteratorRangeBasedForIterator &rhs) noexcept
357 {
358 return (&lhs == &rhs);
359 }
360
361 friend bool operator!=(const QRegularExpressionMatchIteratorRangeBasedForIterator &lhs,
362 const QRegularExpressionMatchIteratorRangeBasedForIterator &rhs) noexcept
363 {
364 return !(lhs == rhs);
365 }
366
367 // This is what we really use in a range-based for.
368 friend bool operator==(const QRegularExpressionMatchIteratorRangeBasedForIterator &lhs,
369 QRegularExpressionMatchIteratorRangeBasedForIteratorSentinel) noexcept
370 {
371 return lhs.m_atEnd;
372 }
373
374 friend bool operator!=(const QRegularExpressionMatchIteratorRangeBasedForIterator &lhs,
375 QRegularExpressionMatchIteratorRangeBasedForIteratorSentinel) noexcept
376 {
377 return !lhs.m_atEnd;
378 }
379
380 QRegularExpressionMatchIterator m_matchIterator;
381 QRegularExpressionMatch m_currentMatch;
382 bool m_atEnd;
383};
384
385} // namespace QtPrivate
386
387Q_DECLARE_SHARED(QRegularExpressionMatchIterator)
388
389QT_END_NAMESPACE
390
391#endif // QREGULAREXPRESSION_H
392