Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qcollator.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2013 Aleix Pol Gonzalez <aleixpol@kde.org>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:data-parser
5
6#include "qcollator_p.h"
7#include "qstringlist.h"
8#include "qstring.h"
9
10#include "qdebug.h"
11#include "qlocale_p.h"
12#include "qthreadstorage.h"
13
15QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QCollatorSortKeyPrivate)
16
17namespace {
19{
21 int generation = QLocalePrivate::s_generation.loadRelaxed();
22public:
24 GenerationalCollator(const QCollator &copy) : theCollator(copy) {}
26 {
27 int currentGeneration = QLocalePrivate::s_generation.loadRelaxed();
28 if (Q_UNLIKELY(generation != currentGeneration)) {
29 // reinitialize the collator
30 generation = currentGeneration;
31 theCollator = QCollator();
32 }
33 return theCollator;
34 }
35};
36}
37Q_GLOBAL_STATIC(QThreadStorage<GenerationalCollator>, defaultCollator)
38
39/*!
40 \class QCollator
41 \inmodule QtCore
42 \brief The QCollator class compares strings according to a localized collation algorithm.
43 \compares equality
44
45 \since 5.2
46
47 \reentrant
48 \ingroup i18n
49 \ingroup string-processing
50 \ingroup shared
51
52 QCollator is initialized with a QLocale. It can then be used to compare and
53 sort strings by using the ordering appropriate for that locale.
54
55 A QCollator object can be used together with template-based sorting
56 algorithms, such as std::sort(), to sort a list with QString entries.
57
58 \snippet code/src_corelib_text_qcollator.cpp 0
59
60 In addition to the locale, several optional flags can be set that influence
61 the result of the collation.
62
63 \section1 POSIX fallback implementation
64
65 On Unix systems, Qt is normally compiled to use ICU (except for \macos,
66 where Qt defaults to using an equivalent Apple API). However, if ICU was
67 not available at compile time or explicitly disabled, Qt will use a
68 fallback backend that uses the POSIX API only. This backend has several
69 limitations:
70
71 \list
72 \li Only the QLocale::c() and QLocale::system() locales are supported.
73 Consult the POSIX and C Standard Library manuals for the
74 \c{<locale.h>} header for more information on the system locale.
75 \li caseSensitivity() is not supported: only case-sensitive collation
76 can be performed.
77 \li The options set via numericMode(), ignorePunctuation(), and
78 options() are not supported.
79 \endlist
80
81 The use of any of the unsupported options will cause a warning to be
82 printed to the application's output.
83*/
84
85/*!
86 \since 6.13
87 \enum QCollator::CollationOption
88
89 Options that control how strings are compared by \l QCollator.
90
91 The \macos, Windows, and ICU backends support all options, with variations
92 where indicated. On non-\macos Unix (including Linux), if ICU is not
93 available a fallback (POSIX) backend is used, which supports none of
94 these options.
95
96 \value CaseInsensitive Ignore case differences when comparing strings.
97
98 \value IgnorePunctuation Ignore punctuation and symbols when comparing strings.
99
100 \value NumericSort Sort strings containing numbers by their numeric value,
101 so that for example "file10" sorts after "file9"
102 instead of between "file1" and "file2".
103
104 \value DiacriticInsensitive Ignore diacritical marks when comparing strings,
105 so that for example "e" and "é" compare as equal.
106 Depending on the locale, this may also include
107 ligature folding such as æ == ae.
108 See \l {DiacriticInsensitive behavior details}
109 below.
110
111 \section2 DiacriticInsensitive behavior details
112
113 This option is primarily intended for search and matching, where the user
114 may not type diacritics. For example, typing "resume" in a search field and
115 expecting to find "résumé".
116
117 The exact behavior depends on the locale and platform backend:
118
119 \list
120 \li Characters that are independent letters in a given alphabet are
121 not treated as diacritic variants. For example, in Swedish "å",
122 "ä" and "ö" are separate letters that sort after "z", so they
123 do not compare equal to "a" or "o", even with this option set.
124 Their position in the alphabet is also preserved. The same
125 characters may behave differently in another locale: in German,
126 "ö" and "ä" are treated as variants of "o" and "a" and do compare
127 equal to them with this option set.
128 \li Collation folding for locale-specific equivalences
129 (such as å == aa in Norwegian) is locale-dependent and works
130 consistently across backends. Note that this maps "å" to the
131 digraph "aa", not to a single "a"; "å" is an independent letter
132 and never folds to "a".
133 \li Ligature folding (such as ß == ss and æ == ae) is applied on
134 Windows and ICU, where the locale defines it; for example
135 "æ" folds to "ae" in English but is an independent letter in
136 Norwegian and does not fold. This folding is not supported by
137 the \macos backend. On Windows, some ligature folding is applied
138 implicitly, even without this option set.
139 \endlist
140
141 \sa setOptions(), options()
142*/
143
144/*!
145 \since 5.13
146
147 Constructs a QCollator using the default locale's collation locale.
148
149 The system locale, when used as default locale, may have a collation locale
150 other than itself (e.g. on Unix, if LC_COLLATE is set differently to LANG in
151 the environment). All other locales are their own collation locales.
152
153 \sa setLocale(), QLocale::collation(), QLocale::setDefault()
154*/
155QCollator::QCollator()
156 : d(nullptr)
157{
158}
159
160/*!
161 Constructs a QCollator using the given \a locale.
162
163 \sa setLocale()
164*/
165QCollator::QCollator(const QLocale &locale)
166 : d(new QCollatorPrivate(locale))
167{
168}
169
170/*!
171 Creates a copy of \a other.
172*/
173QCollator::QCollator(const QCollator &other)
174 : d(other.d)
175{
176 if (d) {
177 // Ensure clean, lest both copies try to init() at the same time:
178 d->ensureInitialized();
179 d->ref.ref();
180 }
181}
182
183/*!
184 Destroys this collator.
185*/
186QCollator::~QCollator()
187{
188 if (d && !d->ref.deref())
189 delete d;
190}
191
192/*!
193 Assigns \a other to this collator.
194*/
195QCollator &QCollator::operator=(const QCollator &other)
196{
197 if (this != &other) {
198 if (d && !d->ref.deref())
199 delete d;
200 d = other.d;
201 if (d) {
202 // Ensure clean, lest both copies try to init() at the same time:
203 d->ensureInitialized();
204 d->ref.ref();
205 }
206 }
207 return *this;
208}
209
210/*!
211 \fn QCollator::QCollator(QCollator &&other)
212
213 Move constructor. Moves from \a other into this collator.
214
215//! [partially-formed]
216 \note The moved-from object \a other is placed in a partially-formed state,
217 in which the only valid operations are destruction and assignment of a new
218 value.
219//! [partially-formed]
220*/
221
222/*!
223 \fn QCollator & QCollator::operator=(QCollator && other)
224
225 Move-assigns \a other to this QCollator instance.
226
227 \include qcollator.cpp partially-formed
228*/
229
230/*!
231 \fn void QCollator::swap(QCollator &other)
232 \memberswap{collator}
233*/
234
235/*!
236 \fn bool QCollator::operator==(const QCollator &lhs, const QCollator &rhs) noexcept
237 \fn bool QCollator::operator!=(const QCollator &lhs, const QCollator &rhs) noexcept
238 \since 6.12
239
240 Returns \c true if \a lhs and \a rhs use the same locale and collation
241 options, otherwise returns \c false.
242*/
243
244bool comparesEqual(const QCollator &lhs, const QCollator &rhs) noexcept
245{
246 if (lhs.d == rhs.d)
247 return true;
248 if (!lhs.d || !rhs.d)
249 return false;
250
251 return lhs.d->options == rhs.d->options
252 && lhs.d->locale == rhs.d->locale;
253}
254
255/*!
256 \internal
257*/
258void QCollator::detach()
259{
260 if (!d) {
261 d = new QCollatorPrivate(QLocale().collation());
262 d->init();
263 } else if (d->ref.loadRelaxed() != 1) {
264 QCollatorPrivate *x = new QCollatorPrivate(d->locale);
265 if (!d->ref.deref())
266 delete d;
267 d = x;
268 }
269 // All callers need this, because about to modify the object:
270 d->dirty = true;
271}
272
273/*!
274 Sets the locale of the collator to \a locale.
275
276 \sa locale()
277*/
278void QCollator::setLocale(const QLocale &locale)
279{
280 if (locale == this->locale())
281 return;
282
283 detach();
284 d->locale = locale;
285}
286
287/*!
288 Returns the locale of the collator.
289
290 Unless supplied to the constructor or by calling setLocale(), the system's
291 default collation locale is used.
292
293 \sa setLocale(), QLocale::collation()
294*/
295QLocale QCollator::locale() const
296{
297 return d ? d->locale : QLocale().collation();
298}
299
300/*!
301 Sets the case-sensitivity of the collator to \a cs.
302
303 \sa caseSensitivity()
304*/
305void QCollator::setCaseSensitivity(Qt::CaseSensitivity cs)
306{
307 if (cs == caseSensitivity())
308 return;
309
310 detach();
311 d->options.setFlag(CollationOption::CaseInsensitive, cs == Qt::CaseInsensitive);
312}
313
314/*!
315 Returns case sensitivity of the collator.
316
317 This defaults to case-sensitive until set.
318
319 \note In the C locale, when case-sensitive, all lower-case letters sort
320 after all upper-case letters, where most locales sort each lower-case letter
321 either immediately before or immediately after its upper-case partner. Thus
322 "Zap" sorts before "ape" in the C locale but after in most others.
323
324 \sa setCaseSensitivity()
325*/
326Qt::CaseSensitivity QCollator::caseSensitivity() const
327{
328 return d && d->options.testFlag(CollationOption::CaseInsensitive) ? Qt::CaseInsensitive
329 : Qt::CaseSensitive;
330}
331
332/*!
333 Enables numeric sorting mode when \a on is \c true.
334
335 \sa numericMode()
336*/
337void QCollator::setNumericMode(bool on)
338{
339 if (on == numericMode())
340 return;
341
342 detach();
343 d->options.setFlag(CollationOption::NumericSort, on);
344}
345
346/*!
347 Returns \c true if numeric sorting is enabled, \c false otherwise.
348
349 When \c true, numerals are recognized as numbers and sorted in arithmetic
350 order; for example, 100 sortes after 99. When \c false, numbers are sorted
351 in lexical order, so that 100 sorts before 99 (because 1 is before 9). By
352 default, this option is disabled.
353
354 \sa setNumericMode()
355*/
356bool QCollator::numericMode() const
357{
358 return d && d->options.testFlag(CollationOption::NumericSort);
359}
360
361/*!
362 Ignores punctuation and symbols if \a on is \c true, attends to them if \c false.
363
364 \sa ignorePunctuation()
365*/
366void QCollator::setIgnorePunctuation(bool on)
367{
368 if (on == ignorePunctuation())
369 return;
370
371 detach();
372 d->options.setFlag(CollationOption::IgnorePunctuation, on);
373}
374
375/*!
376 Returns whether punctuation and symbols are ignored when collating.
377
378 When \c true, strings are compared as if all punctuation and symbols were
379 removed from each string.
380
381 \sa setIgnorePunctuation()
382*/
383bool QCollator::ignorePunctuation() const
384{
385 return d && d->options.testFlag(CollationOption::IgnorePunctuation);
386}
387
388/*!
389 \since 6.13
390 Sets the collation options to \a options.
391 This allows configuring multiple collation settings at once.
392
393 \note In the C locale, the collation options have no effect.
394 Use a specific locale to enable locale-aware collation.
395
396 \sa options(), CollationOption
397*/
398void QCollator::setOptions(CollationOptions options)
399{
400 if (d && d->options == options)
401 return;
402 detach();
403 d->options = options;
404}
405
406/*!
407 \since 6.13
408 Returns the collation options currently set on the collator.
409
410 \sa setOptions(), CollationOption
411*/
412QCollator::CollationOptions QCollator::options() const
413{
414 return d ? d->options : CollationOptions{};
415}
416
417/*!
418 \since 5.13
419 \fn bool QCollator::operator()(QStringView s1, QStringView s2) const
420
421 A QCollator can be used as the comparison function of a sorting algorithm.
422 It returns \c true if \a s1 sorts before \a s2, otherwise \c false.
423
424 \sa compare()
425*/
426
427/*!
428 \since 5.13
429 \fn int QCollator::compare(QStringView s1, QStringView s2) const
430
431 Compares \a s1 with \a s2.
432
433 Returns a negative integer if \a s1 is less than \a s2, a positive integer
434 if it is greater than \a s2, and zero if they are equal.
435*/
436
437/*!
438 \fn bool QCollator::operator()(const QString &s1, const QString &s2) const
439 \overload
440 \since 5.2
441*/
442
443/*!
444 \fn int QCollator::compare(const QString &s1, const QString &s2) const
445 \overload
446 \since 5.2
447*/
448
449/*!
450 \fn int QCollator::compare(const QChar *s1, qsizetype len1, const QChar *s2, qsizetype len2) const
451 \overload
452 \since 5.2
453
454 Compares \a s1 with \a s2. \a len1 and \a len2 specify the lengths of the
455 QChar arrays pointed to by \a s1 and \a s2.
456
457 Returns a negative integer if \a s1 is less than \a s2, a positive integer
458 if it is greater than \a s2, and zero if they are equal.
459
460
461 \note In Qt versions prior to 6.4, the length arguments were of type
462 \c{int}, not \c{qsizetype}.
463*/
464
465/*!
466 \since 6.3
467
468 Compares the strings \a s1 and \a s2, returning their sorting order. This
469 function performs the same operation as compare() on a default-constructed
470 QCollator object.
471
472 \sa compare(), defaultSortKey()
473*/
474int QCollator::defaultCompare(QStringView s1, QStringView s2)
475{
476 return defaultCollator->localData().collator().compare(s1, s2);
477}
478
479/*!
480 \since 6.3
481
482 Returns the sort key for the string \a key. This function performs the same
483 operation as sortKey() on a default-constructed QCollator object.
484
485 \sa sortKey(), defaultCompare()
486*/
487QCollatorSortKey QCollator::defaultSortKey(QStringView key)
488{
489 return defaultCollator->localData().collator().sortKey(key.toString());
490}
491
492/*!
493 \fn QCollatorSortKey QCollator::sortKey(const QString &string) const
494
495 Returns a sortKey for \a string.
496
497 Creating the sort key is usually somewhat slower, than using the compare()
498 methods directly. But if the string is compared repeatedly (e.g. when
499 sorting a whole list of strings), it's usually faster to create the sort
500 keys for each string and then sort using the keys.
501
502 \note Not supported with the C (a.k.a. POSIX) locale on Darwin.
503*/
504
505/*!
506 \class QCollatorSortKey
507 \inmodule QtCore
508 \brief The QCollatorSortKey class can be used to speed up string collation.
509 \compares weak
510
511 \since 5.2
512
513 The QCollatorSortKey class is always created by QCollator::sortKey() and is
514 used for fast strings collation, for example when collating many strings.
515
516 \reentrant
517 \ingroup i18n
518 \ingroup string-processing
519 \ingroup shared
520
521 \sa QCollator, QCollator::sortKey(), compare()
522*/
523
524/*!
525 \internal
526*/
527QCollatorSortKey::QCollatorSortKey(QCollatorSortKeyPrivate *d)
528 : d(d)
529{
530}
531
532/*!
533 Constructs a copy of the \a other collator key.
534*/
535QCollatorSortKey::QCollatorSortKey(const QCollatorSortKey &other)
536 : d(other.d)
537{
538}
539
540/*!
541 \since 6.8
542 \fn QCollatorSortKey::QCollatorSortKey(QCollatorSortKey &&other)
543 Move-constructs a new QCollatorSortKey from \a other.
544
545 \include qcollator.cpp partially-formed
546*/
547
548/*!
549 Destroys the collator key.
550*/
551QCollatorSortKey::~QCollatorSortKey()
552{
553}
554
555/*!
556 Assigns \a other to this collator key.
557*/
558QCollatorSortKey& QCollatorSortKey::operator=(const QCollatorSortKey &other)
559{
560 if (this != &other) {
561 d = other.d;
562 }
563 return *this;
564}
565
566/*!
567 \fn QCollatorSortKey &QCollatorSortKey::operator=(QCollatorSortKey && other)
568
569 Move-assigns \a other to this QCollatorSortKey instance.
570
571 \include qcollator.cpp partially-formed
572*/
573
574/*!
575 \fn bool QCollatorSortKey::operator<(const QCollatorSortKey &lhs, const QCollatorSortKey &rhs)
576 \fn bool QCollatorSortKey::operator<=(const QCollatorSortKey &lhs, const QCollatorSortKey &rhs)
577 \fn bool QCollatorSortKey::operator>(const QCollatorSortKey &lhs, const QCollatorSortKey &rhs)
578 \fn bool QCollatorSortKey::operator>=(const QCollatorSortKey &lhs, const QCollatorSortKey &rhs)
579 \fn bool QCollatorSortKey::operator==(const QCollatorSortKey &lhs, const QCollatorSortKey &rhs)
580 \fn bool QCollatorSortKey::operator!=(const QCollatorSortKey &lhs, const QCollatorSortKey &rhs)
581 \since 6.12
582
583 Both keys must have been created by the same QCollator's sortKey(). Compares
584 \a lhs and \a rhs according to the QCollator that created them.
585
586 \sa QCollatorSortKey::compare()
587*/
588
589/*!
590 \fn void QCollatorSortKey::swap(QCollatorSortKey & other)
591 \memberswap{collator key}
592*/
593
594/*!
595 \fn int QCollatorSortKey::compare(const QCollatorSortKey &otherKey) const noexcept
596
597 Compares this key to \a otherKey, which must have been created by the same
598 QCollator's sortKey() as this key. The comparison is performed in accordance
599 with that QCollator's sort order.
600
601 Returns a negative value if this key sorts before \a otherKey, 0 if the
602 two keys are equal, or a positive value if this key sorts after \a otherKey.
603
604 \sa operator==(), operator!=(), operator<(), operator<=(),
605 operator>(), operator>=()
606*/
607
608QT_END_NAMESPACE
Combined button and popup list for selecting options.
GenerationalCollator(const QCollator &copy)
Definition qcollator.cpp:24