~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to src/corelib/tools/qcollator.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the QtCore module of the Qt Toolkit.
 
7
**
 
8
** $QT_BEGIN_LICENSE:LGPL$
 
9
** Commercial License Usage
 
10
** Licensees holding valid commercial Qt licenses may use this file in
 
11
** accordance with the commercial license agreement provided with the
 
12
** Software or, alternatively, in accordance with the terms contained in
 
13
** a written agreement between you and Digia.  For licensing terms and
 
14
** conditions see http://qt.digia.com/licensing.  For further information
 
15
** use the contact form at http://qt.digia.com/contact-us.
 
16
**
 
17
** GNU Lesser General Public License Usage
 
18
** Alternatively, this file may be used under the terms of the GNU Lesser
 
19
** General Public License version 2.1 as published by the Free Software
 
20
** Foundation and appearing in the file LICENSE.LGPL included in the
 
21
** packaging of this file.  Please review the following information to
 
22
** ensure the GNU Lesser General Public License version 2.1 requirements
 
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 
24
**
 
25
** In addition, as a special exception, Digia gives you certain additional
 
26
** rights.  These rights are described in the Digia Qt LGPL Exception
 
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 
28
**
 
29
** GNU General Public License Usage
 
30
** Alternatively, this file may be used under the terms of the GNU
 
31
** General Public License version 3.0 as published by the Free Software
 
32
** Foundation and appearing in the file LICENSE.GPL included in the
 
33
** packaging of this file.  Please review the following information to
 
34
** ensure the GNU General Public License version 3.0 requirements will be
 
35
** met: http://www.gnu.org/copyleft/gpl.html.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include "qcollator_p.h"
 
43
#include "qstringlist.h"
 
44
#include "qstring.h"
 
45
 
 
46
#ifdef QT_USE_ICU
 
47
#include <unicode/utypes.h>
 
48
#include <unicode/ucol.h>
 
49
#include <unicode/ustring.h>
 
50
#include <unicode/ures.h>
 
51
#endif
 
52
 
 
53
#include "qdebug.h"
 
54
 
 
55
QT_BEGIN_NAMESPACE
 
56
 
 
57
 
 
58
class QCollatorPrivate
 
59
{
 
60
public:
 
61
    QAtomicInt ref;
 
62
    QLocale locale;
 
63
    QCollator::Collation collation;
 
64
 
 
65
#ifdef QT_USE_ICU
 
66
    UCollator *collator;
 
67
#else
 
68
    void *collator;
 
69
#endif
 
70
 
 
71
    QStringList indexCharacters;
 
72
 
 
73
    void clear() {
 
74
#ifdef QT_USE_ICU
 
75
        if (collator)
 
76
            ucol_close(collator);
 
77
#endif
 
78
        collator = 0;
 
79
        indexCharacters.clear();
 
80
    }
 
81
 
 
82
    QCollatorPrivate()
 
83
        : collation(QCollator::Default),
 
84
          collator(0)
 
85
    { ref.store(1); }
 
86
    ~QCollatorPrivate();
 
87
 
 
88
private:
 
89
    Q_DISABLE_COPY(QCollatorPrivate)
 
90
};
 
91
 
 
92
 
 
93
QCollatorPrivate::~QCollatorPrivate()
 
94
{
 
95
    clear();
 
96
}
 
97
 
 
98
static const int collationStringsCount = 13;
 
99
static const char * const collationStrings[collationStringsCount] = {
 
100
    "default",
 
101
    "big5han",
 
102
    "dictionary",
 
103
    "direct",
 
104
    "gb2312han",
 
105
    "phonebook",
 
106
    "pinyin",
 
107
    "phonetic",
 
108
    "reformed",
 
109
    "standard",
 
110
    "stroke",
 
111
    "traditional",
 
112
    "unihan"
 
113
};
 
114
 
 
115
/*!
 
116
    \class QCollator
 
117
    \inmodule QtCore
 
118
    \brief The QCollator class compares strings according to a localized collation algorithm.
 
119
 
 
120
    \internal
 
121
 
 
122
    \reentrant
 
123
    \ingroup i18n
 
124
    \ingroup string-processing
 
125
    \ingroup shared
 
126
 
 
127
    QCollator is initialized with a QLocale and an optional collation strategy. It tries to
 
128
    initialize the collator with the specified values. The collator can then be used to compare
 
129
    and sort strings in a locale dependent fashion.
 
130
 
 
131
    A QCollator object can be used together with template based sorting algorithms such as std::sort
 
132
    to sort a list of QStrings.
 
133
 
 
134
    In addition to the locale and collation strategy, several optional flags can be set that influence
 
135
    the result of the collation.
 
136
*/
 
137
 
 
138
/*!
 
139
    Constructs a QCollator from \a locale and \a collation. If \a collation is not
 
140
    specified the default collation algorithm for the locale is being used. If
 
141
    \a locale is not specified QLocale::default() is being used.
 
142
 
 
143
    \sa setLocale, setCollation, setOptions
 
144
 */
 
145
QCollator::QCollator(const QLocale &locale, QCollator::Collation collation)
 
146
    : d(new QCollatorPrivate)
 
147
{
 
148
    d->locale = locale;
 
149
    if ((int)collation >= 0 && (int)collation < collationStringsCount)
 
150
        d->collation = collation;
 
151
 
 
152
    init();
 
153
}
 
154
 
 
155
/*!
 
156
    Creates a copy of \a other.
 
157
 */
 
158
QCollator::QCollator(const QCollator &other)
 
159
    : d(other.d)
 
160
{
 
161
    d->ref.ref();
 
162
}
 
163
 
 
164
/*!
 
165
    Destroys the collator.
 
166
 */
 
167
QCollator::~QCollator()
 
168
{
 
169
    if (!d->ref.deref())
 
170
        delete d;
 
171
}
 
172
 
 
173
/*!
 
174
    Assigns \a other to this collator.
 
175
 */
 
176
QCollator &QCollator::operator=(const QCollator &other)
 
177
{
 
178
    if (this != &other) {
 
179
        if (!d->ref.deref())
 
180
            delete d;
 
181
        d = other.d;
 
182
        d->ref.ref();
 
183
    }
 
184
    return *this;
 
185
}
 
186
 
 
187
 
 
188
/*!
 
189
    \internal
 
190
 */
 
191
void QCollator::init()
 
192
{
 
193
    Q_ASSERT((int)d->collation < collationStringsCount);
 
194
#ifdef QT_USE_ICU
 
195
    const char *collationString = collationStrings[(int)d->collation];
 
196
    UErrorCode status = U_ZERO_ERROR;
 
197
    QByteArray name = (d->locale.bcp47Name().replace(QLatin1Char('-'), QLatin1Char('_')) + QLatin1String("@collation=") + QLatin1String(collationString)).toLatin1();
 
198
    d->collator = ucol_open(name.constData(), &status);
 
199
    if (U_FAILURE(status))
 
200
        qWarning("Could not create collator: %d", status);
 
201
 
 
202
    // enable normalization by default
 
203
    ucol_setAttribute(d->collator, UCOL_NORMALIZATION_MODE, UCOL_ON, &status);
 
204
#endif
 
205
}
 
206
 
 
207
/*!
 
208
    \internal
 
209
 */
 
210
void QCollator::detach()
 
211
{
 
212
    if (d->ref.load() != 1) {
 
213
        QCollatorPrivate *x = new QCollatorPrivate;
 
214
        x->ref.store(1);
 
215
        x->locale = d->locale;
 
216
        x->collation = d->collation;
 
217
        x->collator = 0;
 
218
        if (!d->ref.deref())
 
219
            delete d;
 
220
        d = x;
 
221
    }
 
222
}
 
223
 
 
224
 
 
225
/*!
 
226
    Sets the locale of the collator to \a locale.
 
227
 */
 
228
void QCollator::setLocale(const QLocale &locale)
 
229
{
 
230
    if (d->ref.load() != 1)
 
231
        detach();
 
232
    d->clear();
 
233
    d->locale = locale;
 
234
 
 
235
    init();
 
236
}
 
237
 
 
238
/*!
 
239
    Returns the locale of the collator.
 
240
 */
 
241
QLocale QCollator::locale() const
 
242
{
 
243
    return d->locale;
 
244
}
 
245
 
 
246
/*!
 
247
    \enum QCollator::Collation
 
248
 
 
249
    This enum can be used to specify an alternate collation algorithm to be used instead
 
250
    of the default algorithm for the locale.
 
251
 
 
252
    Possible values are:
 
253
 
 
254
    \value Default Use the default algorithm for the locale
 
255
    \value Big5Han
 
256
    \value Direct
 
257
    \value GB2312Han
 
258
    \value PhoneBook
 
259
    \value Pinyin
 
260
    \value Phonetic
 
261
    \value Reformed
 
262
    \value Standard
 
263
    \value Stroke
 
264
    \value Traditional
 
265
    \value UniHan
 
266
*/
 
267
 
 
268
/*!
 
269
    Sets the collation algorithm to be used.
 
270
 
 
271
    \sa QCollator::Collation
 
272
 */
 
273
void QCollator::setCollation(QCollator::Collation collation)
 
274
{
 
275
    if ((int)collation < 0 || (int)collation >= collationStringsCount)
 
276
        return;
 
277
 
 
278
    if (d->ref.load() != 1)
 
279
        detach();
 
280
    d->clear();
 
281
    d->collation = collation;
 
282
 
 
283
    init();
 
284
}
 
285
/*!
 
286
    Returns the currently used collation algorithm.
 
287
 
 
288
    \sa QCollator::Collation
 
289
 */
 
290
QCollator::Collation QCollator::collation() const
 
291
{
 
292
    return d->collation;
 
293
}
 
294
 
 
295
/*!
 
296
    Returns a unique identifer for this collation object.
 
297
 
 
298
    This method is helpful to save and restore defined collation
 
299
    objects.
 
300
 
 
301
    \sa fromIdentifier
 
302
 */
 
303
QString QCollator::identifier() const
 
304
{
 
305
    QString id = d->locale.bcp47Name();
 
306
    if (d->collation != QCollator::Default) {
 
307
        id += QLatin1String("@collation=");
 
308
        id += QLatin1String(collationStrings[d->collation]);
 
309
    }
 
310
    // this ensures the ID is compatible with ICU
 
311
    id.replace('-', '_');
 
312
    return id;
 
313
}
 
314
 
 
315
/*!
 
316
    Creates a QCollator from a unique identifier and returns it.
 
317
 
 
318
    \sa identifier
 
319
 */
 
320
QCollator QCollator::fromIdentifier(const QString &identifier)
 
321
{
 
322
    QString localeString = identifier;
 
323
    QString collationString;
 
324
    int at = identifier.indexOf(QLatin1Char('@'));
 
325
    if (at >= 0) {
 
326
        localeString = identifier.left(at);
 
327
        collationString = identifier.mid(at + strlen("@collation="));
 
328
    }
 
329
 
 
330
    QLocale locale(localeString);
 
331
    Collation collation = Default;
 
332
    if (!collationString.isEmpty()) {
 
333
        for (int i = 0; i < collationStringsCount; ++i) {
 
334
            if (QLatin1String(collationStrings[i]) == collationString) {
 
335
                collation = Collation(i);
 
336
                break;
 
337
            }
 
338
        }
 
339
    }
 
340
    return QCollator(locale, collation);
 
341
}
 
342
 
 
343
/*!
 
344
     \enum QCollator::CasePreference
 
345
 
 
346
    This enum can be used to tailor the case preference during collation.
 
347
 
 
348
    \value CasePreferenceOff No case preference, use what is the standard for the locale
 
349
    \value CasePreferenceUpper Sort upper case characters before lower case
 
350
    \value CasePreferenceLower Sort lower case characters before upper case
 
351
*/
 
352
 
 
353
/*!
 
354
    Sets the case preference of the collator.
 
355
 
 
356
    \sa QCollator::CasePreference
 
357
 */
 
358
void QCollator::setCasePreference(CasePreference c)
 
359
{
 
360
    if (d->ref.load() != 1)
 
361
        detach();
 
362
 
 
363
#ifdef QT_USE_ICU
 
364
    UColAttributeValue val = UCOL_OFF;
 
365
    if (c == QCollator::CasePreferenceUpper)
 
366
        val = UCOL_UPPER_FIRST;
 
367
    else if (c == QCollator::CasePreferenceLower)
 
368
        val = UCOL_LOWER_FIRST;
 
369
 
 
370
    UErrorCode status = U_ZERO_ERROR;
 
371
    ucol_setAttribute(d->collator, UCOL_CASE_FIRST, val, &status);
 
372
    if (U_FAILURE(status))
 
373
        qWarning("ucol_setAttribute: Case First failed: %d", status);
 
374
#else
 
375
    Q_UNUSED(c);
 
376
#endif
 
377
}
 
378
 
 
379
/*!
 
380
    Returns case preference of the collator.
 
381
 
 
382
    \sa QCollator::CasePreference
 
383
 */
 
384
QCollator::CasePreference QCollator::casePreference() const
 
385
{
 
386
#ifdef QT_USE_ICU
 
387
    UErrorCode status = U_ZERO_ERROR;
 
388
    switch (ucol_getAttribute(d->collator, UCOL_CASE_FIRST, &status)) {
 
389
    case UCOL_UPPER_FIRST:
 
390
        return QCollator::CasePreferenceUpper;
 
391
    case UCOL_LOWER_FIRST:
 
392
        return QCollator::CasePreferenceLower;
 
393
    case UCOL_OFF:
 
394
    default:
 
395
        break;
 
396
    }
 
397
#endif
 
398
    return QCollator::CasePreferenceOff;
 
399
}
 
400
 
 
401
/*!
 
402
    Enables numeric sorting mode when \a on is set to true.
 
403
 
 
404
    This will enable proper sorting of numeric digits, so that e.g. 100 sorts after 99.
 
405
 
 
406
    By default this mode is off.
 
407
 */
 
408
void QCollator::setNumericMode(bool on)
 
409
{
 
410
    if (d->ref.load() != 1)
 
411
        detach();
 
412
 
 
413
#ifdef QT_USE_ICU
 
414
    UErrorCode status = U_ZERO_ERROR;
 
415
    ucol_setAttribute(d->collator, UCOL_NUMERIC_COLLATION, on ? UCOL_ON : UCOL_OFF, &status);
 
416
    if (U_FAILURE(status))
 
417
        qWarning("ucol_setAttribute: numeric collation failed: %d", status);
 
418
#else
 
419
    Q_UNUSED(on);
 
420
#endif
 
421
}
 
422
 
 
423
/*!
 
424
    Returns true if numeric sorting is enabled, false otherwise.
 
425
 
 
426
    \sa setNumericMode
 
427
 */
 
428
bool QCollator::numericMode() const
 
429
{
 
430
#ifdef QT_USE_ICU
 
431
    UErrorCode status;
 
432
    if (ucol_getAttribute(d->collator, UCOL_NUMERIC_COLLATION, &status) == UCOL_ON)
 
433
        return true;
 
434
#endif
 
435
    return false;
 
436
}
 
437
 
 
438
/*!
 
439
    If set to true, punctuation characters and symbols are ignored when determining sort order.
 
440
 
 
441
    The default is locale dependent.
 
442
 */
 
443
void QCollator::setIgnorePunctuation(bool on)
 
444
{
 
445
    if (d->ref.load() != 1)
 
446
        detach();
 
447
 
 
448
#ifdef QT_USE_ICU
 
449
    UErrorCode status;
 
450
    ucol_setAttribute(d->collator, UCOL_ALTERNATE_HANDLING, on ? UCOL_SHIFTED : UCOL_NON_IGNORABLE, &status);
 
451
    if (U_FAILURE(status))
 
452
        qWarning("ucol_setAttribute: Alternate handling failed: %d", status);
 
453
#else
 
454
    Q_UNUSED(on);
 
455
#endif
 
456
}
 
457
 
 
458
/*!
 
459
    Returns true if punctuation characters and symbols are ignored when determining sort order.
 
460
 
 
461
    \sa setIgnorePunctuation
 
462
 */
 
463
bool QCollator::ignorePunctuation() const
 
464
{
 
465
#ifdef QT_USE_ICU
 
466
    UErrorCode status;
 
467
    if (ucol_getAttribute(d->collator, UCOL_ALTERNATE_HANDLING, &status) == UCOL_SHIFTED)
 
468
        return true;
 
469
#endif
 
470
    return false;
 
471
}
 
472
 
 
473
/*!
 
474
    Compares \a s1 with \a s2. Returns -1, 0 or 1 depending on whether \a s1 is
 
475
    smaller, equal or larger than \a s2.
 
476
 */
 
477
int QCollator::compare(const QString &s1, const QString &s2) const
 
478
{
 
479
    return compare(s1.constData(), s1.size(), s2.constData(), s2.size());
 
480
}
 
481
 
 
482
/*!
 
483
    \overload
 
484
 
 
485
    Compares \a s1 with \a s2. Returns -1, 0 or 1 depending on whether \a s1 is
 
486
    smaller, equal or larger than \a s2.
 
487
 */
 
488
int QCollator::compare(const QStringRef &s1, const QStringRef &s2) const
 
489
{
 
490
    return compare(s1.constData(), s1.size(), s2.constData(), s2.size());
 
491
}
 
492
 
 
493
/*!
 
494
    \overload
 
495
 
 
496
    Compares \a s1 with \a s2. \a len1 and \a len2 specify the length of the
 
497
    QChar arrays pointer to by \a s1 and \a s2.
 
498
 
 
499
    Returns -1, 0 or 1 depending on whether \a s1 is smaller, equal or larger than \a s2.
 
500
 */
 
501
int QCollator::compare(const QChar *s1, int len1, const QChar *s2, int len2) const
 
502
{
 
503
#ifdef QT_USE_ICU
 
504
    const UCollationResult result =
 
505
            ucol_strcoll(d->collator, (const UChar *)s1, len1, (const UChar *)s2, len2);
 
506
    return result;
 
507
#else
 
508
    return QString::compare_helper((const QChar *)s1, len1, (const QChar *)s2, len2, Qt::CaseInsensitive);
 
509
#endif
 
510
}
 
511
 
 
512
/*!
 
513
    Returns a sortKey for \a string. The sortkey can be used as a placeholder
 
514
    for the string that can be then sorted using regular strcmp based sorting.
 
515
 
 
516
    Creating the sort key is usually somewhat slower, then using the compare()
 
517
    methods directly. But if the string is compared repeatedly (e.g. when sorting
 
518
    a whole list of strings), it's usually faster to create the sort keys for each
 
519
    string and then sort using the keys.
 
520
 */
 
521
QByteArray QCollator::sortKey(const QString &string) const
 
522
{
 
523
#ifdef QT_USE_ICU
 
524
    QByteArray result(16 + string.size() + (string.size() >> 2), Qt::Uninitialized);
 
525
    int size = ucol_getSortKey(d->collator, (const UChar *)string.constData(),
 
526
                               string.size(), (uint8_t *)result.data(), result.size());
 
527
    if (size > result.size()) {
 
528
        result.resize(size);
 
529
        size = ucol_getSortKey(d->collator, (const UChar *)string.constData(),
 
530
                               string.size(), (uint8_t *)result.data(), result.size());
 
531
    }
 
532
    result.truncate(size);
 
533
    return result;
 
534
#else
 
535
    return string.toLower().toUtf8();
 
536
#endif
 
537
}
 
538
 
 
539
static QStringList englishIndexCharacters()
 
540
{
 
541
    QString chars = QString::fromLatin1("A B C D E F G H I J K L M N O P Q R S T U V W X Y Z");
 
542
    return chars.split(QLatin1Char(' '), QString::SkipEmptyParts);
 
543
}
 
544
 
 
545
/*!
 
546
    Returns a string list of primary index characters. This is useful when presenting the
 
547
    sorted list in a user interface with section headers.
 
548
*/
 
549
QStringList QCollator::indexCharacters() const
 
550
{
 
551
    if (!d->indexCharacters.isEmpty())
 
552
        return d->indexCharacters;
 
553
 
 
554
#ifdef QT_USE_ICU
 
555
    QByteArray id = identifier().toLatin1();
 
556
 
 
557
    UErrorCode status = U_ZERO_ERROR;
 
558
    UResourceBundle *res = ures_open(NULL, id, &status);
 
559
 
 
560
    if (U_FAILURE(status)) {
 
561
        d->indexCharacters = englishIndexCharacters();
 
562
    } else {
 
563
 
 
564
        qint32 len = 0;
 
565
        status = U_ZERO_ERROR;
 
566
        const UChar *val = ures_getStringByKey(res, "ExemplarCharactersIndex", &len, &status);
 
567
        if (U_FAILURE(status)) {
 
568
            d->indexCharacters = englishIndexCharacters();
 
569
        } else {
 
570
            QString chars(reinterpret_cast<const QChar *>(val), len);
 
571
            chars.remove('[');
 
572
            chars.remove(']');
 
573
            chars.remove('{');
 
574
            chars.remove('}');
 
575
            d->indexCharacters = chars.split(QLatin1Char(' '), QString::SkipEmptyParts);
 
576
        }
 
577
    }
 
578
 
 
579
    ures_close(res);
 
580
#else
 
581
    d->indexCharacters = englishIndexCharacters();
 
582
#endif
 
583
 
 
584
    return d->indexCharacters;
 
585
}
 
586
 
 
587
QT_END_NAMESPACE