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