1/* 2** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3** Distributed under the terms of the OpenBeOS License. 4*/ 5 6 7#include <Collator.h> 8#include <UnicodeChar.h> 9#include <String.h> 10#include <Message.h> 11 12#include <ctype.h> 13 14 15class FrenchCollator : public BCollatorAddOn { 16 struct compare_context { 17 compare_context(bool ignorePunctuation) 18 : 19 inputA(ignorePunctuation), 20 inputB(ignorePunctuation) 21 { 22 } 23 24 typedef BCollatorAddOn::input_context input_context; 25 26 const char *a; 27 const char *b; 28 input_context inputA; 29 input_context inputB; 30 size_t length; 31 }; 32 33 public: 34 FrenchCollator(); 35 FrenchCollator(BMessage *archive); 36 ~FrenchCollator(); 37 38 virtual status_t GetSortKey(const char *string, BString *key, int8 strength, 39 bool ignorePunctuation); 40 virtual int Compare(const char *a, const char *b, int32 length, int8 strength, 41 bool ignorePunctuation); 42 43 // (un-)archiving API 44 virtual status_t Archive(BMessage *archive, bool deep) const; 45 static BArchivable *Instantiate(BMessage *archive); 46 47 private: 48 int CompareSecondary(compare_context &context); 49 50 typedef BCollatorAddOn _inherited; 51}; 52 53 54static const char *kSignature = "application/x-vnd.locale-collator.french"; 55 56 57inline char 58uint32_to_char(uint32 c) 59{ 60 if (c > 255) 61 return 255; 62 63 return (char)c; 64} 65 66 67// #pragma mark - 68 69 70FrenchCollator::FrenchCollator() 71{ 72} 73 74 75FrenchCollator::FrenchCollator(BMessage *archive) 76 : BCollatorAddOn(archive) 77{ 78} 79 80 81FrenchCollator::~FrenchCollator() 82{ 83} 84 85 86int 87FrenchCollator::CompareSecondary(compare_context &context) 88{ 89 if (context.length-- <= 0) 90 return 0; 91 92 uint32 charA = BUnicodeChar::ToLower(GetNextChar(&context.a, context.inputA)); 93 uint32 charB = BUnicodeChar::ToLower(GetNextChar(&context.b, context.inputB)); 94 95 // the two strings does have the same size when we get here 96 // (unfortunately, "length" is specified in bytes, not characters [if it was -1]) 97 if (charA == 0) 98 return 0; 99 100 int compare = CompareSecondary(context); 101 if (compare != 0) 102 return compare; 103 104 return (int32)charA - (int32)charB; 105} 106 107 108status_t 109FrenchCollator::GetSortKey(const char *string, BString *key, int8 strength, 110 bool ignorePunctuation) 111{ 112 if (strength == B_COLLATE_PRIMARY) 113 return _inherited::GetSortKey(string, key, strength, ignorePunctuation); 114 115 size_t length = strlen(string); 116 117 if (strength > B_COLLATE_QUATERNARY) { 118 key->SetTo(string, length); 119 return B_OK; 120 // what can we do about that? 121 } 122 123 if (strength >= B_COLLATE_QUATERNARY) { 124 // the difference between tertiary and quaternary collation strength 125 // are usually a different handling of punctuation characters 126 ignorePunctuation = false; 127 } 128 129 size_t keyLength = PrimaryKeyLength(length) + length + 1; 130 // the primary key + the secondary key + separator char 131 if (strength != B_COLLATE_SECONDARY) { 132 keyLength += length + 1; 133 // the secondary key + the tertiary key + separator char 134 } 135 136 char *begin = key->LockBuffer(keyLength); 137 if (begin == NULL) 138 return B_NO_MEMORY; 139 140 char *buffer = PutPrimaryKey(string, begin, length, ignorePunctuation); 141 *buffer++ = '\01'; 142 // separator 143 144 char *secondary = buffer; 145 146 input_context context(ignorePunctuation); 147 const char *source = string; 148 uint32 c; 149 for (uint32 i = 0; (c = GetNextChar(&source, context)) && i < length; i++) { 150 *buffer++ = uint32_to_char(BUnicodeChar::ToLower(c)); 151 // we only support Latin-1 characters here 152 // ToDo: this creates a non UTF-8 sort key - is that what we want? 153 } 154 155 // reverse key 156 157 char *end = buffer - 1; 158 while (secondary < end) { 159 char c = secondary[0]; 160 161 *secondary++ = end[0]; 162 *end-- = c; 163 } 164 165 // apply tertiary collation if necessary 166 167 if (strength != B_COLLATE_SECONDARY) { 168 *buffer++ = '\01'; 169 // separator 170 171 input_context context(ignorePunctuation); 172 source = string; 173 uint32 c; 174 for (uint32 i = 0; (c = GetNextChar(&source, context)) && i < length; i++) { 175 // ToDo: same problem as above, no UTF-8 key 176 if (BUnicodeChar::IsLower(c)) 177 *buffer++ = uint32_to_char(BUnicodeChar::ToUpper(c)); 178 else 179 *buffer++ = uint32_to_char(BUnicodeChar::ToLower(c)); 180 } 181 } 182 183 *buffer = '\0'; 184 key->UnlockBuffer(buffer - begin); 185 186 return B_OK; 187} 188 189 190int 191FrenchCollator::Compare(const char *a, const char *b, int32 length, int8 strength, 192 bool ignorePunctuation) 193{ 194 int compare = _inherited::Compare(a, b, length, B_COLLATE_PRIMARY, ignorePunctuation); 195 if (strength == B_COLLATE_PRIMARY || compare != 0) 196 return compare; 197 else if (strength >= B_COLLATE_QUATERNARY) { 198 // the difference between tertiary and quaternary collation strength 199 // are usually a different handling of punctuation characters 200 ignorePunctuation = false; 201 } 202 203 compare_context context(ignorePunctuation); 204 context.a = a; 205 context.b = b; 206 context.length = length; 207 208 switch (strength) { 209 case B_COLLATE_SECONDARY: 210 return CompareSecondary(context); 211 212 case B_COLLATE_TERTIARY: 213 case B_COLLATE_QUATERNARY: 214 { 215 // diacriticals can only change the order between equal strings 216 int32 compare = Compare(a, b, length, B_COLLATE_SECONDARY, ignorePunctuation); 217 if (compare != 0) 218 return compare; 219 220 for (int32 i = 0; i < length; i++) { 221 uint32 charA = GetNextChar(&a, context.inputA); 222 uint32 charB = GetNextChar(&b, context.inputB); 223 224 // the two strings does have the same size when we get here 225 if (charA == 0) 226 return 0; 227 228 if (charA != charB) 229 return (int32)charB - (int32)charA; 230 } 231 return 0; 232 } 233 234 case B_COLLATE_IDENTICAL: 235 default: 236 return strncmp(a, b, length); 237 } 238} 239 240 241status_t 242FrenchCollator::Archive(BMessage *archive, bool deep) const 243{ 244 status_t status = BArchivable::Archive(archive, deep); 245 246 // add the add-on signature, so that the roster can load 247 // us on demand! 248 if (status == B_OK) 249 status = archive->AddString("add_on", kSignature); 250 251 return status; 252} 253 254 255BArchivable * 256FrenchCollator::Instantiate(BMessage *archive) 257{ 258 if (validate_instantiation(archive, "FrenchCollator")) 259 return new FrenchCollator(archive); 260 261 return NULL; 262} 263 264 265// #pragma mark - 266 267 268extern "C" BCollatorAddOn * 269instantiate_collator(void) 270{ 271 return new FrenchCollator(); 272} 273