26a4d8386abffa905de1ae81887e355c2294b499
[WebKit-https.git] / Source / WTF / wtf / unicode / icu / CollatorICU.cpp
1 /*
2  * Copyright (C) 2008, 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include <wtf/unicode/Collator.h>
31
32 // FIXME: Merge this with CollatorDefault.cpp into a single Collator.cpp source file.
33
34 #if !UCONFIG_NO_COLLATION
35
36 #include <mutex>
37 #include <unicode/ucol.h>
38 #include <wtf/Lock.h>
39 #include <wtf/StringExtras.h>
40 #include <wtf/text/StringView.h>
41
42 #if OS(DARWIN) && USE(CF)
43 #include <CoreFoundation/CoreFoundation.h>
44 #include <wtf/RetainPtr.h>
45 #endif
46
47 namespace WTF {
48
49 static UCollator* cachedCollator;
50 static char* cachedCollatorLocale;
51 static bool cachedCollatorShouldSortLowercaseFirst;
52
53 static StaticLock cachedCollatorMutex;
54
55 #if !(OS(DARWIN) && USE(CF))
56
57 static inline const char* resolveDefaultLocale(const char* locale)
58 {
59     return locale;
60 }
61
62 #else
63
64 static inline char* copyShortASCIIString(CFStringRef string)
65 {
66     // OK to have a fixed size buffer and to only handle ASCII since we only use this for locale names.
67     char buffer[256];
68     if (!string || !CFStringGetCString(string, buffer, sizeof(buffer), kCFStringEncodingASCII))
69         return strdup("");
70     return strdup(buffer);
71 }
72
73 static char* copyDefaultLocale()
74 {
75 #if !PLATFORM(IOS)
76     return copyShortASCIIString(static_cast<CFStringRef>(CFLocaleGetValue(adoptCF(CFLocaleCopyCurrent()).get(), kCFLocaleCollatorIdentifier)));
77 #else
78     // FIXME: Documentation claims the code above would work on iOS 4.0 and later. After test that works, we should remove this and use that instead.
79     return copyShortASCIIString(adoptCF(static_cast<CFStringRef>(CFPreferencesCopyValue(CFSTR("AppleCollationOrder"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost))).get());
80 #endif
81 }
82
83 static inline const char* resolveDefaultLocale(const char* locale)
84 {
85     if (locale)
86         return locale;
87     // Since iOS and OS X don't set UNIX locale to match the user's selected locale, the ICU default locale is not the right one.
88     // So, instead of passing null to ICU, we pass the name of the user's selected locale.
89     static char* defaultLocale;
90     static std::once_flag initializeDefaultLocaleOnce;
91     std::call_once(initializeDefaultLocaleOnce, []{
92         defaultLocale = copyDefaultLocale();
93     });
94     return defaultLocale;
95 }
96
97 #endif
98
99 static inline bool localesMatch(const char* a, const char* b)
100 {
101     // Two null locales are equal, other locales are compared with strcmp.
102     return a == b || (a && b && !strcmp(a, b));
103 }
104
105 Collator::Collator(const char* locale, bool shouldSortLowercaseFirst)
106 {
107     UErrorCode status = U_ZERO_ERROR;
108
109     {
110         std::lock_guard<StaticLock> lock(cachedCollatorMutex);
111         if (cachedCollator && localesMatch(cachedCollatorLocale, locale) && cachedCollatorShouldSortLowercaseFirst == shouldSortLowercaseFirst) {
112             m_collator = cachedCollator;
113             m_locale = cachedCollatorLocale;
114             m_shouldSortLowercaseFirst = shouldSortLowercaseFirst;
115             cachedCollator = nullptr;
116             cachedCollatorLocale = nullptr;
117             return;
118         }
119     }
120
121     m_collator = ucol_open(resolveDefaultLocale(locale), &status);
122     if (U_FAILURE(status)) {
123         status = U_ZERO_ERROR;
124         m_collator = ucol_open("", &status); // Fall back to Unicode Collation Algorithm.
125     }
126     ASSERT(U_SUCCESS(status));
127
128     ucol_setAttribute(m_collator, UCOL_CASE_FIRST, shouldSortLowercaseFirst ? UCOL_LOWER_FIRST : UCOL_UPPER_FIRST, &status);
129     ASSERT(U_SUCCESS(status));
130
131     ucol_setAttribute(m_collator, UCOL_NORMALIZATION_MODE, UCOL_ON, &status);
132     ASSERT(U_SUCCESS(status));
133
134     m_locale = locale ? fastStrDup(locale) : nullptr;
135     m_shouldSortLowercaseFirst = shouldSortLowercaseFirst;
136 }
137
138 Collator::~Collator()
139 {
140     std::lock_guard<StaticLock> lock(cachedCollatorMutex);
141     if (cachedCollator) {
142         ucol_close(cachedCollator);
143         fastFree(cachedCollatorLocale);
144     }
145     cachedCollator = m_collator;
146     cachedCollatorLocale = m_locale;
147     cachedCollatorShouldSortLowercaseFirst = m_shouldSortLowercaseFirst;
148 }
149
150 static int32_t getIndexLatin1(UCharIterator* iterator, UCharIteratorOrigin origin)
151 {
152     switch (origin) {
153     case UITER_START:
154         return iterator->start;
155     case UITER_CURRENT:
156         return iterator->index;
157     case UITER_LIMIT:
158         return iterator->limit;
159     case UITER_ZERO:
160         return 0;
161     case UITER_LENGTH:
162         return iterator->length;
163     }
164     ASSERT_NOT_REACHED();
165     return U_SENTINEL;
166 }
167
168 static int32_t moveLatin1(UCharIterator* iterator, int32_t delta, UCharIteratorOrigin origin)
169 {
170     return iterator->index = getIndexLatin1(iterator, origin) + delta;
171 }
172
173 static UBool hasNextLatin1(UCharIterator* iterator)
174 {
175     return iterator->index < iterator->limit;
176 }
177
178 static UBool hasPreviousLatin1(UCharIterator* iterator)
179 {
180     return iterator->index > iterator->start;
181 }
182
183 static UChar32 currentLatin1(UCharIterator* iterator)
184 {
185     ASSERT(iterator->index >= iterator->start);
186     if (iterator->index >= iterator->limit)
187         return U_SENTINEL;
188     return static_cast<const LChar*>(iterator->context)[iterator->index];
189 }
190
191 static UChar32 nextLatin1(UCharIterator* iterator)
192 {
193     ASSERT(iterator->index >= iterator->start);
194     if (iterator->index >= iterator->limit)
195         return U_SENTINEL;
196     return static_cast<const LChar*>(iterator->context)[iterator->index++];
197 }
198
199 static UChar32 previousLatin1(UCharIterator* iterator)
200 {
201     if (iterator->index <= iterator->start)
202         return U_SENTINEL;
203     return static_cast<const LChar*>(iterator->context)[--iterator->index];
204 }
205
206 static uint32_t getStateLatin1(const UCharIterator* iterator)
207 {
208     return iterator->index;
209 }
210
211 static void setStateLatin1(UCharIterator* iterator, uint32_t state, UErrorCode*)
212 {
213     iterator->index = state;
214 }
215
216 static UCharIterator createLatin1Iterator(const LChar* characters, int length)
217 {
218     UCharIterator iterator;
219     iterator.context = characters;
220     iterator.length = length;
221     iterator.start = 0;
222     iterator.index = 0;
223     iterator.limit = length;
224     iterator.reservedField = 0;
225     iterator.getIndex = getIndexLatin1;
226     iterator.move = moveLatin1;
227     iterator.hasNext = hasNextLatin1;
228     iterator.hasPrevious = hasPreviousLatin1;
229     iterator.current = currentLatin1;
230     iterator.next = nextLatin1;
231     iterator.previous = previousLatin1;
232     iterator.reservedFn = nullptr;
233     iterator.getState = getStateLatin1;
234     iterator.setState = setStateLatin1;
235     return iterator;
236 }
237
238 UCharIterator createIterator(StringView string)
239 {
240     if (string.is8Bit())
241         return createLatin1Iterator(string.characters8(), string.length());
242     UCharIterator iterator;
243     uiter_setString(&iterator, string.characters16(), string.length());
244     return iterator;
245 }
246
247 int Collator::collate(StringView a, StringView b) const
248 {
249     UCharIterator iteratorA = createIterator(a);
250     UCharIterator iteratorB = createIterator(b);
251     UErrorCode status = U_ZERO_ERROR;
252     int result = ucol_strcollIter(m_collator, &iteratorA, &iteratorB, &status);
253     ASSERT(U_SUCCESS(status));
254     return result;
255 }
256
257 static UCharIterator createIteratorUTF8(const char* string)
258 {
259     UCharIterator iterator;
260     uiter_setUTF8(&iterator, string, strlen(string));
261     return iterator;
262 }
263
264 int Collator::collateUTF8(const char* a, const char* b) const
265 {
266     UCharIterator iteratorA = createIteratorUTF8(a);
267     UCharIterator iteratorB = createIteratorUTF8(b);
268     UErrorCode status = U_ZERO_ERROR;
269     int result = ucol_strcollIter(m_collator, &iteratorA, &iteratorB, &status);
270     ASSERT(U_SUCCESS(status));
271     return result;
272 }
273
274 } // namespace WTF
275
276 #endif