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