Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(373)

Side by Side Diff: src/extensions/i18n/collator.cc

Issue 18487004: Import the v8-i18n extension into v8 (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 // limitations under the License.
28
29 #include "collator.h"
30
31 #include "i18n-utils.h"
32 #include "unicode/coll.h"
33 #include "unicode/locid.h"
34 #include "unicode/ucol.h"
35
36 namespace v8_i18n {
37
38 static icu::Collator* InitializeCollator(
39 v8::Handle<v8::String>, v8::Handle<v8::Object>, v8::Handle<v8::Object>);
40
41 static icu::Collator* CreateICUCollator(
42 const icu::Locale&, v8::Handle<v8::Object>);
43
44 static bool SetBooleanAttribute(
45 UColAttribute, const char*, v8::Handle<v8::Object>, icu::Collator*);
46
47 static void SetResolvedSettings(
48 const icu::Locale&, icu::Collator*, v8::Handle<v8::Object>);
49
50 static void SetBooleanSetting(
51 UColAttribute, icu::Collator*, const char*, v8::Handle<v8::Object>);
52
53 icu::Collator* Collator::UnpackCollator(v8::Handle<v8::Object> obj) {
54 v8::HandleScope handle_scope;
55
56 if (obj->HasOwnProperty(v8::String::New("collator"))) {
57 return static_cast<icu::Collator*>(
58 obj->GetAlignedPointerFromInternalField(0));
59 }
60
61 return NULL;
62 }
63
64 void Collator::DeleteCollator(v8::Isolate* isolate,
65 v8::Persistent<v8::Object>* object,
66 void* param) {
67 // First delete the hidden C++ object.
68 // Unpacking should never return NULL here. That would only happen if
69 // this method is used as the weak callback for persistent handles not
70 // pointing to a collator.
71 v8::HandleScope handle_scope(isolate);
72 v8::Local<v8::Object> handle = v8::Local<v8::Object>::New(isolate, *object);
73 delete UnpackCollator(handle);
74
75 // Then dispose of the persistent handle to JS object.
76 object->Dispose(isolate);
77 }
78
79 // Throws a JavaScript exception.
80 static v8::Handle<v8::Value> ThrowUnexpectedObjectError() {
81 // Returns undefined, and schedules an exception to be thrown.
82 return v8::ThrowException(v8::Exception::Error(
83 v8::String::New("Collator method called on an object "
84 "that is not a Collator.")));
85 }
86
87 // When there's an ICU error, throw a JavaScript error with |message|.
88 static v8::Handle<v8::Value> ThrowExceptionForICUError(const char* message) {
89 return v8::ThrowException(v8::Exception::Error(v8::String::New(message)));
90 }
91
92 // static
93 void Collator::JSInternalCompare(
94 const v8::FunctionCallbackInfo<v8::Value>& args) {
95 if (args.Length() != 3 || !args[0]->IsObject() ||
96 !args[1]->IsString() || !args[2]->IsString()) {
97 v8::ThrowException(v8::Exception::SyntaxError(
98 v8::String::New("Collator and two string arguments are required.")));
99 return;
100 }
101
102 icu::Collator* collator = UnpackCollator(args[0]->ToObject());
103 if (!collator) {
104 ThrowUnexpectedObjectError();
105 return;
106 }
107
108 v8::String::Value string_value1(args[1]);
109 v8::String::Value string_value2(args[2]);
110 const UChar* string1 = reinterpret_cast<const UChar*>(*string_value1);
111 const UChar* string2 = reinterpret_cast<const UChar*>(*string_value2);
112 UErrorCode status = U_ZERO_ERROR;
113 UCollationResult result = collator->compare(
114 string1, string_value1.length(), string2, string_value2.length(), status);
115
116 if (U_FAILURE(status)) {
117 ThrowExceptionForICUError(
118 "Internal error. Unexpected failure in Collator.compare.");
119 return;
120 }
121
122 args.GetReturnValue().Set(result);
123 }
124
125 void Collator::JSCreateCollator(
126 const v8::FunctionCallbackInfo<v8::Value>& args) {
127 if (args.Length() != 3 || !args[0]->IsString() || !args[1]->IsObject() ||
128 !args[2]->IsObject()) {
129 v8::ThrowException(v8::Exception::SyntaxError(
130 v8::String::New("Internal error, wrong parameters.")));
131 return;
132 }
133
134 v8::Isolate* isolate = args.GetIsolate();
135 v8::Local<v8::ObjectTemplate> intl_collator_template =
136 Utils::GetTemplate(isolate);
137
138 // Create an empty object wrapper.
139 v8::Local<v8::Object> local_object = intl_collator_template->NewInstance();
140 // But the handle shouldn't be empty.
141 // That can happen if there was a stack overflow when creating the object.
142 if (local_object.IsEmpty()) {
143 args.GetReturnValue().Set(local_object);
144 return;
145 }
146
147 // Set collator as internal field of the resulting JS object.
148 icu::Collator* collator = InitializeCollator(
149 args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject());
150
151 if (!collator) {
152 v8::ThrowException(v8::Exception::Error(v8::String::New(
153 "Internal error. Couldn't create ICU collator.")));
154 return;
155 } else {
156 local_object->SetAlignedPointerInInternalField(0, collator);
157
158 // Make it safer to unpack later on.
159 v8::TryCatch try_catch;
160 local_object->Set(v8::String::New("collator"), v8::String::New("valid"));
161 if (try_catch.HasCaught()) {
162 v8::ThrowException(v8::Exception::Error(
163 v8::String::New("Internal error, couldn't set property.")));
164 return;
165 }
166 }
167
168 v8::Persistent<v8::Object> wrapper(isolate, local_object);
169 // Make object handle weak so we can delete iterator once GC kicks in.
170 wrapper.MakeWeak<void>(NULL, &DeleteCollator);
171 args.GetReturnValue().Set(wrapper);
172 wrapper.ClearAndLeak();
173 }
174
175 static icu::Collator* InitializeCollator(v8::Handle<v8::String> locale,
176 v8::Handle<v8::Object> options,
177 v8::Handle<v8::Object> resolved) {
178 // Convert BCP47 into ICU locale format.
179 UErrorCode status = U_ZERO_ERROR;
180 icu::Locale icu_locale;
181 char icu_result[ULOC_FULLNAME_CAPACITY];
182 int icu_length = 0;
183 v8::String::AsciiValue bcp47_locale(locale);
184 if (bcp47_locale.length() != 0) {
185 uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
186 &icu_length, &status);
187 if (U_FAILURE(status) || icu_length == 0) {
188 return NULL;
189 }
190 icu_locale = icu::Locale(icu_result);
191 }
192
193 icu::Collator* collator = CreateICUCollator(icu_locale, options);
194 if (!collator) {
195 // Remove extensions and try again.
196 icu::Locale no_extension_locale(icu_locale.getBaseName());
197 collator = CreateICUCollator(no_extension_locale, options);
198
199 // Set resolved settings (pattern, numbering system).
200 SetResolvedSettings(no_extension_locale, collator, resolved);
201 } else {
202 SetResolvedSettings(icu_locale, collator, resolved);
203 }
204
205 return collator;
206 }
207
208 static icu::Collator* CreateICUCollator(
209 const icu::Locale& icu_locale, v8::Handle<v8::Object> options) {
210 // Make collator from options.
211 icu::Collator* collator = NULL;
212 UErrorCode status = U_ZERO_ERROR;
213 collator = icu::Collator::createInstance(icu_locale, status);
214
215 if (U_FAILURE(status)) {
216 delete collator;
217 return NULL;
218 }
219
220 // Set flags first, and then override them with sensitivity if necessary.
221 SetBooleanAttribute(UCOL_NUMERIC_COLLATION, "numeric", options, collator);
222
223 // Normalization is always on, by the spec. We are free to optimize
224 // if the strings are already normalized (but we don't have a way to tell
225 // that right now).
226 collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
227
228 icu::UnicodeString case_first;
229 if (Utils::ExtractStringSetting(options, "caseFirst", &case_first)) {
230 if (case_first == UNICODE_STRING_SIMPLE("upper")) {
231 collator->setAttribute(UCOL_CASE_FIRST, UCOL_UPPER_FIRST, status);
232 } else if (case_first == UNICODE_STRING_SIMPLE("lower")) {
233 collator->setAttribute(UCOL_CASE_FIRST, UCOL_LOWER_FIRST, status);
234 } else {
235 // Default (false/off).
236 collator->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status);
237 }
238 }
239
240 icu::UnicodeString sensitivity;
241 if (Utils::ExtractStringSetting(options, "sensitivity", &sensitivity)) {
242 if (sensitivity == UNICODE_STRING_SIMPLE("base")) {
243 collator->setStrength(icu::Collator::PRIMARY);
244 } else if (sensitivity == UNICODE_STRING_SIMPLE("accent")) {
245 collator->setStrength(icu::Collator::SECONDARY);
246 } else if (sensitivity == UNICODE_STRING_SIMPLE("case")) {
247 collator->setStrength(icu::Collator::PRIMARY);
248 collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status);
249 } else {
250 // variant (default)
251 collator->setStrength(icu::Collator::TERTIARY);
252 }
253 }
254
255 bool ignore;
256 if (Utils::ExtractBooleanSetting(options, "ignorePunctuation", &ignore)) {
257 if (ignore) {
258 collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status);
259 }
260 }
261
262 return collator;
263 }
264
265 static bool SetBooleanAttribute(UColAttribute attribute,
266 const char* name,
267 v8::Handle<v8::Object> options,
268 icu::Collator* collator) {
269 UErrorCode status = U_ZERO_ERROR;
270 bool result;
271 if (Utils::ExtractBooleanSetting(options, name, &result)) {
272 collator->setAttribute(attribute, result ? UCOL_ON : UCOL_OFF, status);
273 if (U_FAILURE(status)) {
274 return false;
275 }
276 }
277
278 return true;
279 }
280
281 static void SetResolvedSettings(const icu::Locale& icu_locale,
282 icu::Collator* collator,
283 v8::Handle<v8::Object> resolved) {
284 SetBooleanSetting(UCOL_NUMERIC_COLLATION, collator, "numeric", resolved);
285
286 UErrorCode status = U_ZERO_ERROR;
287
288 switch (collator->getAttribute(UCOL_CASE_FIRST, status)) {
289 case UCOL_LOWER_FIRST:
290 resolved->Set(v8::String::New("caseFirst"), v8::String::New("lower"));
291 break;
292 case UCOL_UPPER_FIRST:
293 resolved->Set(v8::String::New("caseFirst"), v8::String::New("upper"));
294 break;
295 default:
296 resolved->Set(v8::String::New("caseFirst"), v8::String::New("false"));
297 }
298
299 switch (collator->getAttribute(UCOL_STRENGTH, status)) {
300 case UCOL_PRIMARY: {
301 resolved->Set(v8::String::New("strength"), v8::String::New("primary"));
302
303 // case level: true + s1 -> case, s1 -> base.
304 if (UCOL_ON == collator->getAttribute(UCOL_CASE_LEVEL, status)) {
305 resolved->Set(v8::String::New("sensitivity"), v8::String::New("case"));
306 } else {
307 resolved->Set(v8::String::New("sensitivity"), v8::String::New("base"));
308 }
309 break;
310 }
311 case UCOL_SECONDARY:
312 resolved->Set(v8::String::New("strength"), v8::String::New("secondary"));
313 resolved->Set(v8::String::New("sensitivity"), v8::String::New("accent"));
314 break;
315 case UCOL_TERTIARY:
316 resolved->Set(v8::String::New("strength"), v8::String::New("tertiary"));
317 resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant"));
318 break;
319 case UCOL_QUATERNARY:
320 // We shouldn't get quaternary and identical from ICU, but if we do
321 // put them into variant.
322 resolved->Set(v8::String::New("strength"), v8::String::New("quaternary"));
323 resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant"));
324 break;
325 default:
326 resolved->Set(v8::String::New("strength"), v8::String::New("identical"));
327 resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant"));
328 }
329
330 if (UCOL_SHIFTED == collator->getAttribute(UCOL_ALTERNATE_HANDLING, status)) {
331 resolved->Set(v8::String::New("ignorePunctuation"),
332 v8::Boolean::New(true));
333 } else {
334 resolved->Set(v8::String::New("ignorePunctuation"),
335 v8::Boolean::New(false));
336 }
337
338 // Set the locale
339 char result[ULOC_FULLNAME_CAPACITY];
340 status = U_ZERO_ERROR;
341 uloc_toLanguageTag(
342 icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
343 if (U_SUCCESS(status)) {
344 resolved->Set(v8::String::New("locale"), v8::String::New(result));
345 } else {
346 // This would never happen, since we got the locale from ICU.
347 resolved->Set(v8::String::New("locale"), v8::String::New("und"));
348 }
349 }
350
351 static void SetBooleanSetting(UColAttribute attribute,
352 icu::Collator* collator,
353 const char* property,
354 v8::Handle<v8::Object> resolved) {
355 UErrorCode status = U_ZERO_ERROR;
356 if (UCOL_ON == collator->getAttribute(attribute, status)) {
357 resolved->Set(v8::String::New(property), v8::Boolean::New(true));
358 } else {
359 resolved->Set(v8::String::New(property), v8::Boolean::New(false));
360 }
361 }
362
363 } // namespace v8_i18n
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698