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