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
5#include "qcollator_p.h"
6#include "qstringlist.h"
7#include "qstring.h"
8
9#include "qdebug.h"
10#include "qlocale_p.h"
11#include "qthreadstorage.h"
12
13QT_BEGIN_NAMESPACE
14
15namespace {
16struct GenerationalCollator
17{
18 QCollator theCollator;
19 int generation = QLocalePrivate::s_generation.loadRelaxed();
20public:
21 GenerationalCollator() = default;
22 GenerationalCollator(const QCollator &copy) : theCollator(copy) {}
23 QCollator &collator()
24 {
25 int currentGeneration = QLocalePrivate::s_generation.loadRelaxed();
26 if (Q_UNLIKELY(generation != currentGeneration)) {
27 // reinitialize the collator
28 generation = currentGeneration;
29 theCollator = QCollator();
30 }
31 return theCollator;
32 }
33};
34}
35Q_GLOBAL_STATIC(QThreadStorage<GenerationalCollator>, defaultCollator)
36
37/*!
38 \class QCollator
39 \inmodule QtCore
40 \brief The QCollator class compares strings according to a localized collation algorithm.
41
42 \since 5.2
43
44 \reentrant
45 \ingroup i18n
46 \ingroup string-processing
47 \ingroup shared
48
49 QCollator is initialized with a QLocale. It can then be used to compare and
50 sort strings by using the ordering appropriate for that locale.
51
52 A QCollator object can be used together with template-based sorting
53 algorithms, such as std::sort(), to sort a list with QString entries.
54
55 \snippet code/src_corelib_text_qcollator.cpp 0
56
57 In addition to the locale, several optional flags can be set that influence
58 the result of the collation.
59
60 \section1 POSIX fallback implementation
61
62 On Unix systems, Qt is normally compiled to use ICU (except for \macos,
63 where Qt defaults to using an equivalent Apple API). However, if ICU was
64 not available at compile time or explicitly disabled, Qt will use a
65 fallback backend that uses the POSIX API only. This backend has several
66 limitations:
67
68 \list
69 \li Only the QLocale::c() and QLocale::system() locales are supported.
70 Consult the POSIX and C Standard Library manuals for the
71 \c{<locale.h>} header for more information on the system locale.
72 \li caseSensitivity() is not supported: only case-sensitive collation
73 can be performed.
74 \li numericMode() and ignorePunctuation() are not supported.
75 \endlist
76
77 The use of any of the unsupported options will cause a warning to be
78 printed to the application's output.
79*/
80
81/*!
82 \since 5.13
83
84 Constructs a QCollator using the default locale's collation locale.
85
86 The system locale, when used as default locale, may have a collation locale
87 other than itself (e.g. on Unix, if LC_COLLATE is set differently to LANG in
88 the environment). All other locales are their own collation locales.
89
90 \sa setLocale(), QLocale::collation(), QLocale::setDefault()
91*/
92QCollator::QCollator()
93 : d(new QCollatorPrivate(QLocale().collation()))
94{
95 d->init();
96}
97
98/*!
99 Constructs a QCollator using the given \a locale.
100
101 \sa setLocale()
102*/
103QCollator::QCollator(const QLocale &locale)
104 : d(new QCollatorPrivate(locale))
105{
106}
107
108/*!
109 Creates a copy of \a other.
110*/
111QCollator::QCollator(const QCollator &other)
112 : d(other.d)
113{
114 if (d) {
115 // Ensure clean, lest both copies try to init() at the same time:
116 d->ensureInitialized();
117 d->ref.ref();
118 }
119}
120
121/*!
122 Destroys this collator.
123*/
124QCollator::~QCollator()
125{
126 if (d && !d->ref.deref())
127 delete d;
128}
129
130/*!
131 Assigns \a other to this collator.
132*/
133QCollator &QCollator::operator=(const QCollator &other)
134{
135 if (this != &other) {
136 if (d && !d->ref.deref())
137 delete d;
138 d = other.d;
139 if (d) {
140 // Ensure clean, lest both copies try to init() at the same time:
141 d->ensureInitialized();
142 d->ref.ref();
143 }
144 }
145 return *this;
146}
147
148/*!
149 \fn QCollator::QCollator(QCollator &&other)
150
151 Move constructor. Moves from \a other into this collator.
152
153 Note that a moved-from QCollator can only be destroyed or assigned to.
154 The effect of calling other functions than the destructor or one of the
155 assignment operators is undefined.
156*/
157
158/*!
159 \fn QCollator & QCollator::operator=(QCollator && other)
160
161 Move-assigns from \a other to this collator.
162
163 Note that a moved-from QCollator can only be destroyed or assigned to.
164 The effect of calling other functions than the destructor or one of the
165 assignment operators is undefined.
166*/
167
168/*!
169 \fn void QCollator::swap(QCollator &other)
170
171 Swaps this collator with \a other. This function is very fast and
172 never fails.
173*/
174
175/*!
176 \internal
177*/
178void QCollator::detach()
179{
180 if (d->ref.loadRelaxed() != 1) {
181 QCollatorPrivate *x = new QCollatorPrivate(d->locale);
182 if (!d->ref.deref())
183 delete d;
184 d = x;
185 }
186 // All callers need this, because about to modify the object:
187 d->dirty = true;
188}
189
190/*!
191 Sets the locale of the collator to \a locale.
192
193 \sa locale()
194*/
195void QCollator::setLocale(const QLocale &locale)
196{
197 if (locale == d->locale)
198 return;
199
200 detach();
201 d->locale = locale;
202}
203
204/*!
205 Returns the locale of the collator.
206
207 Unless supplied to the constructor or by calling setLocale(), the system's
208 default collation locale is used.
209
210 \sa setLocale(), QLocale::collation()
211*/
212QLocale QCollator::locale() const
213{
214 return d->locale;
215}
216
217/*!
218 Sets the case-sensitivity of the collator to \a cs.
219
220 \sa caseSensitivity()
221*/
222void QCollator::setCaseSensitivity(Qt::CaseSensitivity cs)
223{
224 if (d->caseSensitivity == cs)
225 return;
226
227 detach();
228 d->caseSensitivity = cs;
229}
230
231/*!
232 Returns case sensitivity of the collator.
233
234 This defaults to case-sensitive until set.
235
236 \note In the C locale, when case-sensitive, all lower-case letters sort
237 after all upper-case letters, where most locales sort each lower-case letter
238 either immediately before or immediately after its upper-case partner. Thus
239 "Zap" sorts before "ape" in the C locale but after in most others.
240
241 \sa setCaseSensitivity()
242*/
243Qt::CaseSensitivity QCollator::caseSensitivity() const
244{
245 return d->caseSensitivity;
246}
247
248/*!
249 Enables numeric sorting mode when \a on is \c true.
250
251 \sa numericMode()
252*/
253void QCollator::setNumericMode(bool on)
254{
255 if (d->numericMode == on)
256 return;
257
258 detach();
259 d->numericMode = on;
260}
261
262/*!
263 Returns \c true if numeric sorting is enabled, \c false otherwise.
264
265 When \c true, numerals are recognized as numbers and sorted in arithmetic
266 order; for example, 100 sortes after 99. When \c false, numbers are sorted
267 in lexical order, so that 100 sorts before 99 (because 1 is before 9). By
268 default, this option is disabled.
269
270 \sa setNumericMode()
271*/
272bool QCollator::numericMode() const
273{
274 return d->numericMode;
275}
276
277/*!
278 Ignores punctuation and symbols if \a on is \c true, attends to them if \c false.
279
280 \sa ignorePunctuation()
281*/
282void QCollator::setIgnorePunctuation(bool on)
283{
284 if (d->ignorePunctuation == on)
285 return;
286
287 detach();
288 d->ignorePunctuation = on;
289}
290
291/*!
292 Returns whether punctuation and symbols are ignored when collating.
293
294 When \c true, strings are compared as if all punctuation and symbols were
295 removed from each string.
296
297 \sa setIgnorePunctuation()
298*/
299bool QCollator::ignorePunctuation() const
300{
301 return d->ignorePunctuation;
302}
303
304/*!
305 \since 5.13
306 \fn bool QCollator::operator()(QStringView s1, QStringView s2) const
307
308 A QCollator can be used as the comparison function of a sorting algorithm.
309 It returns \c true if \a s1 sorts before \a s2, otherwise \c false.
310
311 \sa compare()
312*/
313
314/*!
315 \since 5.13
316 \fn int QCollator::compare(QStringView s1, QStringView s2) const
317
318 Compares \a s1 with \a s2.
319
320 Returns an integer less than, equal to, or greater than zero depending on
321 whether \a s1 sorts before, with or after \a s2.
322*/
323
324/*!
325 \fn bool QCollator::operator()(const QString &s1, const QString &s2) const
326 \overload
327 \since 5.2
328*/
329
330/*!
331 \fn int QCollator::compare(const QString &s1, const QString &s2) const
332 \overload
333 \since 5.2
334*/
335
336/*!
337 \fn int QCollator::compare(const QChar *s1, qsizetype len1, const QChar *s2, qsizetype len2) const
338 \overload
339 \since 5.2
340
341 Compares \a s1 with \a s2. \a len1 and \a len2 specify the lengths of the
342 QChar arrays pointed to by \a s1 and \a s2.
343
344 Returns an integer less than, equal to, or greater than zero depending on
345 whether \a s1 sorts before, with or after \a s2.
346
347 \note In Qt versions prior to 6.4, the length arguments were of type
348 \c{int}, not \c{qsizetype}.
349*/
350
351/*!
352 \since 6.3
353
354 Compares the strings \a s1 and \a s2, returning their sorting order. This
355 function performs the same operation as compare() on a default-constructed
356 QCollator object.
357
358 \sa compare(), defaultSortKey()
359*/
360int QCollator::defaultCompare(QStringView s1, QStringView s2)
361{
362 return defaultCollator->localData().collator().compare(s1, s2);
363}
364
365/*!
366 \since 6.3
367
368 Returns the sort key for the string \a key. This function performs the same
369 operation as sortKey() on a default-constructed QCollator object.
370
371 \sa sortKey(), defaultCompare()
372*/
373QCollatorSortKey QCollator::defaultSortKey(QStringView key)
374{
375 return defaultCollator->localData().collator().sortKey(string: key.toString());
376}
377
378/*!
379 \fn QCollatorSortKey QCollator::sortKey(const QString &string) const
380
381 Returns a sortKey for \a string.
382
383 Creating the sort key is usually somewhat slower, than using the compare()
384 methods directly. But if the string is compared repeatedly (e.g. when
385 sorting a whole list of strings), it's usually faster to create the sort
386 keys for each string and then sort using the keys.
387
388 \note Not supported with the C (a.k.a. POSIX) locale on Darwin.
389*/
390
391/*!
392 \class QCollatorSortKey
393 \inmodule QtCore
394 \brief The QCollatorSortKey class can be used to speed up string collation.
395
396 \since 5.2
397
398 The QCollatorSortKey class is always created by QCollator::sortKey() and is
399 used for fast strings collation, for example when collating many strings.
400
401 \reentrant
402 \ingroup i18n
403 \ingroup string-processing
404 \ingroup shared
405
406 \sa QCollator, QCollator::sortKey(), compare()
407*/
408
409/*!
410 \internal
411*/
412QCollatorSortKey::QCollatorSortKey(QCollatorSortKeyPrivate *d)
413 : d(d)
414{
415}
416
417/*!
418 Constructs a copy of the \a other collator key.
419*/
420QCollatorSortKey::QCollatorSortKey(const QCollatorSortKey &other)
421 : d(other.d)
422{
423}
424
425/*!
426 Destroys the collator key.
427*/
428QCollatorSortKey::~QCollatorSortKey()
429{
430}
431
432/*!
433 Assigns \a other to this collator key.
434*/
435QCollatorSortKey& QCollatorSortKey::operator=(const QCollatorSortKey &other)
436{
437 if (this != &other) {
438 d = other.d;
439 }
440 return *this;
441}
442
443/*!
444 \fn QCollatorSortKey &QCollatorSortKey::operator=(QCollatorSortKey && other)
445
446 Move-assigns \a other to this collator key.
447*/
448
449/*!
450 \fn bool QCollatorSortKey::operator<(const QCollatorSortKey &lhs, const QCollatorSortKey &rhs)
451
452 Both keys must have been created by the same QCollator's sortKey(). Returns
453 \c true if \a lhs should be sorted before \a rhs, according to the QCollator
454 that created them; otherwise returns \c false.
455
456 \sa QCollatorSortKey::compare()
457*/
458
459/*!
460 \fn void QCollatorSortKey::swap(QCollatorSortKey & other)
461
462 Swaps this collator key with \a other.
463*/
464
465/*!
466 \fn int QCollatorSortKey::compare(const QCollatorSortKey &otherKey) const
467
468 Compares this key to \a otherKey, which must have been created by the same
469 QCollator's sortKey() as this key. The comparison is performed in accordance
470 with that QCollator's sort order.
471
472 Returns a negative value if this key sorts before \a otherKey, 0 if the two
473 keys are equal or a positive value if this key sorts after \a otherKey.
474
475 \sa operator<()
476*/
477
478QT_END_NAMESPACE
479

source code of qtbase/src/corelib/text/qcollator.cpp