1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
|
/**
* NaturalCollate
* Simple helper class for natural sorting in Vala.
*
* (c) Tobia Tesan <tobia.tesan@gmail.com>, 2014
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Lesser GNU General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; see the file COPYING. If not,
* see <http://www.gnu.org/licenses/>.
*/
namespace NaturalCollate {
private const unichar SUPERDIGIT = ':';
private const unichar NUM_SENTINEL = 0x2; // glib uses these, so do we
private const string COLLATION_SENTINEL = "\x01\x01\x01";
private static int read_number(owned string s, ref long byte_index) {
/*
* Given a string in the form [numerals]*[everythingelse]*
* returns the int value of the first block and increments index
* by its length as a side effect.
* Notice that "numerals" is not just 0-9 but everything else
* Unicode considers a numeral (see: string::isdigit())
*/
int number = 0;
while (s.length != 0 && s.get_char(0).isdigit()) {
number = number*10;
number += s.get_char(0).digit_value();
var second_char = s.index_of_nth_char(1);
s = s.substring(second_char);
byte_index += second_char;
}
return number;
}
public static int compare(string str1, string str2) {
return strcmp(collate_key(str1), collate_key(str2));
}
public static string collate_key(owned string str) {
/*
* Computes a collate key.
* Has roughly the same effect as g_utf8_collate_key_for_file, except that it doesn't
* handle the dot as a special char.
*/
assert (str.validate());
string result = "";
bool eos = (str.length == 0);
while (!eos) {
assert(str.validate());
long position = 0L;
while (!(str.get_char(position).to_string() in "0123456789")) {
// We only care about plain old 0123456789, aping what g_utf8_collate_key_for_filename does
position++;
}
// (0... position( is a bunch of non-numerical chars, so we compute and append the collate key...
result = result + (str.substring(0, position).collate_key());
// ...then throw them away
str = str.substring(position);
eos = (str.length == 0);
position = 0L;
if (!eos) {
// We have some numbers to handle in front of us
int number = read_number(str, ref position);
str = str.substring(position);
int number_of_superdigits = number.to_string().length;
string to_append = "";
for (int i = 1; i < number_of_superdigits; i++) {
// We append n - 1 superdigits where n is the number of digits
to_append = to_append + SUPERDIGIT.to_string();
}
to_append = to_append + (number.to_string()); // We append the actual number
result = result +
COLLATION_SENTINEL +
NUM_SENTINEL.to_string() +
to_append;
}
eos = (str.length == 0);
}
result = result + NUM_SENTINEL.to_string();
// No specific reason except that glib does it
return result;
}
}
|