xref: /haiku/src/system/libroot/add-ons/icu/ICUCollateData.cpp (revision 9f66f05b58e3a033984f1271ac986a2c99226103)
1 /*
2  * Copyright 2010-2011, Oliver Tappe, zooey@hirschkaefer.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ICUCollateData.h"
8 
9 #include <string.h>
10 
11 #include <unicode/unistr.h>
12 
13 #include "ICUConverterManager.h"
14 
15 
16 namespace BPrivate {
17 namespace Libroot {
18 
19 
20 ICUCollateData::ICUCollateData(pthread_key_t tlsKey)
21 	:
22 	inherited(tlsKey),
23 	fCollator(NULL)
24 {
25 }
26 
27 
28 ICUCollateData::~ICUCollateData()
29 {
30 	delete fCollator;
31 }
32 
33 
34 status_t
35 ICUCollateData::SetTo(const Locale& locale, const char* posixLocaleName)
36 {
37 	status_t result = inherited::SetTo(locale, posixLocaleName);
38 
39 	if (result == B_OK) {
40 		UErrorCode icuStatus = U_ZERO_ERROR;
41 		delete fCollator;
42 		fCollator = Collator::createInstance(fLocale, icuStatus);
43 		if (!U_SUCCESS(icuStatus))
44 			return B_NO_MEMORY;
45 	}
46 
47 	return result;
48 }
49 
50 
51 status_t
52 ICUCollateData::SetToPosix()
53 {
54 	status_t result = inherited::SetToPosix();
55 
56 	if (result == B_OK) {
57 		delete fCollator;
58 		fCollator = NULL;
59 	}
60 
61 	return result;
62 }
63 
64 
65 status_t
66 ICUCollateData::Strcoll(const char* a, const char* b, int& result)
67 {
68 	if (strcmp(fPosixLocaleName, "POSIX") == 0) {
69 		// handle POSIX here as the collator ICU uses for that (english) is
70 		// incompatible in too many ways
71 		result = strcmp(a, b);
72 		for (const char* aIter = a; *aIter != 0; ++aIter) {
73 			if (*aIter < 0)
74 				return B_BAD_VALUE;
75 		}
76 		for (const char* bIter = b; *bIter != 0; ++bIter) {
77 			if (*bIter < 0)
78 				return B_BAD_VALUE;
79 		}
80 		return B_OK;
81 	}
82 
83 	status_t status = B_OK;
84 	UErrorCode icuStatus = U_ZERO_ERROR;
85 
86 	if (strcasecmp(fGivenCharset, "utf-8") == 0) {
87 		UCharIterator aIter, bIter;
88 		uiter_setUTF8(&aIter, a, -1);
89 		uiter_setUTF8(&bIter, b, -1);
90 
91 		result = fCollator->compare(aIter, bIter, icuStatus);
92 	} else {
93 		UnicodeString unicodeA;
94 		UnicodeString unicodeB;
95 
96 		if (_ToUnicodeString(a, unicodeA) != B_OK
97 			|| _ToUnicodeString(b, unicodeB) != B_OK) {
98 			status = B_BAD_VALUE;
99 		}
100 
101 		result = fCollator->compare(unicodeA, unicodeB, icuStatus);
102 	}
103 
104 	if (!U_SUCCESS(icuStatus))
105 		status = B_BAD_VALUE;
106 
107 	return status;
108 }
109 
110 
111 status_t
112 ICUCollateData::Strxfrm(char* out, const char* in, size_t size, size_t& outSize)
113 {
114 	if (strcmp(fPosixLocaleName, "POSIX") == 0) {
115 		// handle POSIX here as the collator ICU uses for that (english) is
116 		// incompatible in too many ways
117 		outSize = strlcpy(out, in, size);
118 		for (const char* inIter = in; *inIter != 0; ++inIter) {
119 			if (*inIter < 0)
120 				return B_BAD_VALUE;
121 		}
122 		return B_OK;
123 	}
124 
125 	if (in == NULL) {
126 		outSize = 0;
127 		return B_OK;
128 	}
129 
130 	UErrorCode icuStatus = U_ZERO_ERROR;
131 
132 	UnicodeString unicodeIn;
133 	if (_ToUnicodeString(in, unicodeIn) != B_OK)
134 		return B_BAD_VALUE;
135 
136 	outSize = fCollator->getSortKey(unicodeIn, (uint8_t*)out, size);
137 	if (!U_SUCCESS(icuStatus))
138 		return B_BAD_VALUE;
139 
140 	return B_OK;
141 }
142 
143 
144 status_t
145 ICUCollateData::_ToUnicodeString(const char* in, UnicodeString& out)
146 {
147 	out.remove();
148 
149 	if (in == NULL)
150 		return B_OK;
151 
152 	size_t inLen = strlen(in);
153 	if (inLen == 0)
154 		return B_OK;
155 
156 	ICUConverterRef converterRef;
157 	status_t result = _GetConverter(converterRef);
158 	if (result != B_OK)
159 		return result;
160 
161 	UErrorCode icuStatus = U_ZERO_ERROR;
162 	int32_t outLen = ucnv_toUChars(converterRef->Converter(), NULL, 0, in,
163 		inLen, &icuStatus);
164 	if (icuStatus != U_BUFFER_OVERFLOW_ERROR)
165 		return B_BAD_VALUE;
166 	if (outLen < 0)
167 		return B_ERROR;
168 	if (outLen == 0)
169 		return B_OK;
170 
171 	UChar* outBuf = out.getBuffer(outLen + 1);
172 	icuStatus = U_ZERO_ERROR;
173 	outLen = ucnv_toUChars(converterRef->Converter(), outBuf, outLen + 1, in,
174 		inLen, &icuStatus);
175 	if (!U_SUCCESS(icuStatus)) {
176 		out.releaseBuffer(0);
177 		return B_BAD_VALUE;
178 	}
179 
180 	out.releaseBuffer(outLen);
181 
182 	return B_OK;
183 }
184 
185 
186 }	// namespace Libroot
187 }	// namespace BPrivate
188