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 | |
13 | QT_BEGIN_NAMESPACE |
14 | |
15 | namespace { |
16 | struct GenerationalCollator |
17 | { |
18 | QCollator theCollator; |
19 | int generation = QLocalePrivate::s_generation.loadRelaxed(); |
20 | public: |
21 | GenerationalCollator() = default; |
22 | GenerationalCollator(const QCollator ©) : 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 | } |
35 | Q_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 | */ |
92 | QCollator::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 | */ |
103 | QCollator::QCollator(const QLocale &locale) |
104 | : d(new QCollatorPrivate(locale)) |
105 | { |
106 | } |
107 | |
108 | /*! |
109 | Creates a copy of \a other. |
110 | */ |
111 | QCollator::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 | */ |
124 | QCollator::~QCollator() |
125 | { |
126 | if (d && !d->ref.deref()) |
127 | delete d; |
128 | } |
129 | |
130 | /*! |
131 | Assigns \a other to this collator. |
132 | */ |
133 | QCollator &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 | */ |
178 | void 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 | */ |
195 | void 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 | */ |
212 | QLocale 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 | */ |
222 | void 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 | */ |
243 | Qt::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 | */ |
253 | void 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 | */ |
272 | bool 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 | */ |
282 | void 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 | */ |
299 | bool 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 | */ |
360 | int 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 | */ |
373 | QCollatorSortKey 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 | */ |
412 | QCollatorSortKey::QCollatorSortKey(QCollatorSortKeyPrivate *d) |
413 | : d(d) |
414 | { |
415 | } |
416 | |
417 | /*! |
418 | Constructs a copy of the \a other collator key. |
419 | */ |
420 | QCollatorSortKey::QCollatorSortKey(const QCollatorSortKey &other) |
421 | : d(other.d) |
422 | { |
423 | } |
424 | |
425 | /*! |
426 | Destroys the collator key. |
427 | */ |
428 | QCollatorSortKey::~QCollatorSortKey() |
429 | { |
430 | } |
431 | |
432 | /*! |
433 | Assigns \a other to this collator key. |
434 | */ |
435 | QCollatorSortKey& 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 | |
478 | QT_END_NAMESPACE |
479 | |