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