File indexing completed on 2024-04-21 14:55:40

0001 /* This file is part of the KDE libraries
0002    Copyright (C) 1999 Ian Zepp (icszepp@islc.net)
0003    Copyright (C) 2006 by Dominic Battre <dominic@battre.de>
0004    Copyright (C) 2006 by Martin Pool <mbp@canonical.com>
0005 
0006    This library is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU Library General Public
0008    License as published by the Free Software Foundation; either
0009    version 2 of the License, or (at your option) any later version.
0010 
0011    This library is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014    Library General Public License for more details.
0015 
0016    You should have received a copy of the GNU Library General Public License
0017    along with this library; see the file COPYING.LIB.  If not, write to
0018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019    Boston, MA 02110-1301, USA.
0020 */
0021 
0022 #include "kstringhandler_deprecated.h"
0023 
0024 int KStringHandler::naturalCompare(const QString &_a, const QString &_b, Qt::CaseSensitivity caseSensitivity)
0025 {
0026     // This method chops the input a and b into pieces of
0027     // digits and non-digits (a1.05 becomes a | 1 | . | 05)
0028     // and compares these pieces of a and b to each other
0029     // (first with first, second with second, ...).
0030     //
0031     // This is based on the natural sort order code code by Martin Pool
0032     // http://sourcefrog.net/projects/natsort/
0033     // Martin Pool agreed to license this under LGPL or GPL.
0034 
0035     // FIXME: Using toLower() to implement case insensitive comparison is
0036     // sub-optimal, but is needed because we compare strings with
0037     // localeAwareCompare(), which does not know about case sensitivity.
0038     // A task has been filled for this in Qt Task Tracker with ID 205990.
0039     // http://trolltech.com/developer/task-tracker/index_html?method=entry&id=205990
0040     QString a;
0041     QString b;
0042     if (caseSensitivity == Qt::CaseSensitive) {
0043         a = _a;
0044         b = _b;
0045     } else {
0046         a = _a.toLower();
0047         b = _b.toLower();
0048     }
0049 
0050     const QChar *currA = a.unicode(); // iterator over a
0051     const QChar *currB = b.unicode(); // iterator over b
0052 
0053     if (currA == currB) {
0054         return 0;
0055     }
0056 
0057     while (!currA->isNull() && !currB->isNull()) {
0058         const QChar *begSeqA = currA; // beginning of a new character sequence of a
0059         const QChar *begSeqB = currB;
0060         if (currA->unicode() == QChar::ObjectReplacementCharacter) {
0061             return 1;
0062         }
0063 
0064         if (currB->unicode() == QChar::ObjectReplacementCharacter) {
0065             return -1;
0066         }
0067 
0068         if (currA->unicode() == QChar::ReplacementCharacter) {
0069             return 1;
0070         }
0071 
0072         if (currB->unicode() == QChar::ReplacementCharacter) {
0073             return -1;
0074         }
0075 
0076         // find sequence of characters ending at the first non-character
0077         while (!currA->isNull() && !currA->isDigit() && !currA->isPunct() && !currA->isSpace()) {
0078             ++currA;
0079         }
0080 
0081         while (!currB->isNull() && !currB->isDigit() && !currB->isPunct() && !currB->isSpace()) {
0082             ++currB;
0083         }
0084 
0085         // compare these sequences
0086         const QStringRef &subA(a.midRef(begSeqA - a.unicode(), currA - begSeqA));
0087         const QStringRef &subB(b.midRef(begSeqB - b.unicode(), currB - begSeqB));
0088         const int cmp = QStringRef::localeAwareCompare(subA, subB);
0089         if (cmp != 0) {
0090             return cmp < 0 ? -1 : +1;
0091         }
0092 
0093         if (currA->isNull() || currB->isNull()) {
0094             break;
0095         }
0096 
0097         // find sequence of characters ending at the first non-character
0098         while ((currA->isPunct() || currA->isSpace()) && (currB->isPunct() || currB->isSpace())) {
0099             if (*currA != *currB) {
0100                 return (*currA < *currB) ? -1 : +1;
0101             }
0102             ++currA;
0103             ++currB;
0104             if (currA->isNull() || currB->isNull()) {
0105                 break;
0106             }
0107         }
0108 
0109         // now some digits follow...
0110         if ((*currA == QLatin1Char('0')) || (*currB == QLatin1Char('0'))) {
0111             // one digit-sequence starts with 0 -> assume we are in a fraction part
0112             // do left aligned comparison (numbers are considered left aligned)
0113             while (1) {
0114                 if (!currA->isDigit() && !currB->isDigit()) {
0115                     break;
0116                 } else if (!currA->isDigit()) {
0117                     return +1;
0118                 } else if (!currB->isDigit()) {
0119                     return -1;
0120                 } else if (*currA < *currB) {
0121                     return -1;
0122                 } else if (*currA > *currB) {
0123                     return + 1;
0124                 }
0125                 ++currA;
0126                 ++currB;
0127             }
0128         } else {
0129             // No digit-sequence starts with 0 -> assume we are looking at some integer
0130             // do right aligned comparison.
0131             //
0132             // The longest run of digits wins. That aside, the greatest
0133             // value wins, but we can't know that it will until we've scanned
0134             // both numbers to know that they have the same magnitude.
0135 
0136             bool isFirstRun = true;
0137             int weight = 0;
0138             while (1) {
0139                 if (!currA->isDigit() && !currB->isDigit()) {
0140                     if (weight != 0) {
0141                         return weight;
0142                     }
0143                     break;
0144                 } else if (!currA->isDigit()) {
0145                     if (isFirstRun) {
0146                         return *currA < *currB ? -1 : +1;
0147                     } else {
0148                         return -1;
0149                     }
0150                 } else if (!currB->isDigit()) {
0151                     if (isFirstRun) {
0152                         return *currA < *currB ? -1 : +1;
0153                     } else {
0154                         return +1;
0155                     }
0156                 } else if ((*currA < *currB) && (weight == 0)) {
0157                     weight = -1;
0158                 } else if ((*currA > *currB) && (weight == 0)) {
0159                     weight = + 1;
0160                 }
0161                 ++currA;
0162                 ++currB;
0163                 isFirstRun = false;
0164             }
0165         }
0166     }
0167 
0168     if (currA->isNull() && currB->isNull()) {
0169         return 0;
0170     }
0171 
0172     return currA->isNull() ? -1 : + 1;
0173 }
0174