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