• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 // limitations under the License.
5 
6 #include "src/i18n.h"
7 
8 #include "unicode/brkiter.h"
9 #include "unicode/calendar.h"
10 #include "unicode/coll.h"
11 #include "unicode/curramt.h"
12 #include "unicode/dcfmtsym.h"
13 #include "unicode/decimfmt.h"
14 #include "unicode/dtfmtsym.h"
15 #include "unicode/dtptngen.h"
16 #include "unicode/locid.h"
17 #include "unicode/numfmt.h"
18 #include "unicode/numsys.h"
19 #include "unicode/rbbi.h"
20 #include "unicode/smpdtfmt.h"
21 #include "unicode/timezone.h"
22 #include "unicode/uchar.h"
23 #include "unicode/ucol.h"
24 #include "unicode/ucurr.h"
25 #include "unicode/unum.h"
26 #include "unicode/uversion.h"
27 
28 namespace v8 {
29 namespace internal {
30 
31 namespace {
32 
ExtractStringSetting(Isolate * isolate,Handle<JSObject> options,const char * key,icu::UnicodeString * setting)33 bool ExtractStringSetting(Isolate* isolate,
34                           Handle<JSObject> options,
35                           const char* key,
36                           icu::UnicodeString* setting) {
37   Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
38   Handle<Object> object = Object::GetProperty(options, str).ToHandleChecked();
39   if (object->IsString()) {
40     v8::String::Utf8Value utf8_string(
41         v8::Utils::ToLocal(Handle<String>::cast(object)));
42     *setting = icu::UnicodeString::fromUTF8(*utf8_string);
43     return true;
44   }
45   return false;
46 }
47 
48 
ExtractIntegerSetting(Isolate * isolate,Handle<JSObject> options,const char * key,int32_t * value)49 bool ExtractIntegerSetting(Isolate* isolate,
50                            Handle<JSObject> options,
51                            const char* key,
52                            int32_t* value) {
53   Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
54   Handle<Object> object = Object::GetProperty(options, str).ToHandleChecked();
55   if (object->IsNumber()) {
56     object->ToInt32(value);
57     return true;
58   }
59   return false;
60 }
61 
62 
ExtractBooleanSetting(Isolate * isolate,Handle<JSObject> options,const char * key,bool * value)63 bool ExtractBooleanSetting(Isolate* isolate,
64                            Handle<JSObject> options,
65                            const char* key,
66                            bool* value) {
67   Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
68   Handle<Object> object = Object::GetProperty(options, str).ToHandleChecked();
69   if (object->IsBoolean()) {
70     *value = object->BooleanValue();
71     return true;
72   }
73   return false;
74 }
75 
76 
CreateICUDateFormat(Isolate * isolate,const icu::Locale & icu_locale,Handle<JSObject> options)77 icu::SimpleDateFormat* CreateICUDateFormat(
78     Isolate* isolate,
79     const icu::Locale& icu_locale,
80     Handle<JSObject> options) {
81   // Create time zone as specified by the user. We have to re-create time zone
82   // since calendar takes ownership.
83   icu::TimeZone* tz = NULL;
84   icu::UnicodeString timezone;
85   if (ExtractStringSetting(isolate, options, "timeZone", &timezone)) {
86     tz = icu::TimeZone::createTimeZone(timezone);
87   } else {
88     tz = icu::TimeZone::createDefault();
89   }
90 
91   // Create a calendar using locale, and apply time zone to it.
92   UErrorCode status = U_ZERO_ERROR;
93   icu::Calendar* calendar =
94       icu::Calendar::createInstance(tz, icu_locale, status);
95 
96   // Make formatter from skeleton. Calendar and numbering system are added
97   // to the locale as Unicode extension (if they were specified at all).
98   icu::SimpleDateFormat* date_format = NULL;
99   icu::UnicodeString skeleton;
100   if (ExtractStringSetting(isolate, options, "skeleton", &skeleton)) {
101     icu::DateTimePatternGenerator* generator =
102         icu::DateTimePatternGenerator::createInstance(icu_locale, status);
103     icu::UnicodeString pattern;
104     if (U_SUCCESS(status)) {
105       pattern = generator->getBestPattern(skeleton, status);
106       delete generator;
107     }
108 
109     date_format = new icu::SimpleDateFormat(pattern, icu_locale, status);
110     if (U_SUCCESS(status)) {
111       date_format->adoptCalendar(calendar);
112     }
113   }
114 
115   if (U_FAILURE(status)) {
116     delete calendar;
117     delete date_format;
118     date_format = NULL;
119   }
120 
121   return date_format;
122 }
123 
124 
SetResolvedDateSettings(Isolate * isolate,const icu::Locale & icu_locale,icu::SimpleDateFormat * date_format,Handle<JSObject> resolved)125 void SetResolvedDateSettings(Isolate* isolate,
126                              const icu::Locale& icu_locale,
127                              icu::SimpleDateFormat* date_format,
128                              Handle<JSObject> resolved) {
129   Factory* factory = isolate->factory();
130   UErrorCode status = U_ZERO_ERROR;
131   icu::UnicodeString pattern;
132   date_format->toPattern(pattern);
133   JSObject::SetProperty(
134       resolved,
135       factory->NewStringFromStaticAscii("pattern"),
136       factory->NewStringFromTwoByte(
137         Vector<const uint16_t>(
138             reinterpret_cast<const uint16_t*>(pattern.getBuffer()),
139             pattern.length())).ToHandleChecked(),
140       NONE,
141       SLOPPY).Assert();
142 
143   // Set time zone and calendar.
144   const icu::Calendar* calendar = date_format->getCalendar();
145   const char* calendar_name = calendar->getType();
146   JSObject::SetProperty(
147       resolved,
148       factory->NewStringFromStaticAscii("calendar"),
149       factory->NewStringFromAsciiChecked(calendar_name),
150       NONE,
151       SLOPPY).Assert();
152 
153   const icu::TimeZone& tz = calendar->getTimeZone();
154   icu::UnicodeString time_zone;
155   tz.getID(time_zone);
156 
157   icu::UnicodeString canonical_time_zone;
158   icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
159   if (U_SUCCESS(status)) {
160     if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
161       JSObject::SetProperty(
162           resolved,
163           factory->NewStringFromStaticAscii("timeZone"),
164           factory->NewStringFromStaticAscii("UTC"),
165           NONE,
166           SLOPPY).Assert();
167     } else {
168       JSObject::SetProperty(
169           resolved,
170           factory->NewStringFromStaticAscii("timeZone"),
171           factory->NewStringFromTwoByte(
172             Vector<const uint16_t>(
173                 reinterpret_cast<const uint16_t*>(
174                     canonical_time_zone.getBuffer()),
175                 canonical_time_zone.length())).ToHandleChecked(),
176           NONE,
177           SLOPPY).Assert();
178     }
179   }
180 
181   // Ugly hack. ICU doesn't expose numbering system in any way, so we have
182   // to assume that for given locale NumberingSystem constructor produces the
183   // same digits as NumberFormat/Calendar would.
184   status = U_ZERO_ERROR;
185   icu::NumberingSystem* numbering_system =
186       icu::NumberingSystem::createInstance(icu_locale, status);
187   if (U_SUCCESS(status)) {
188     const char* ns = numbering_system->getName();
189     JSObject::SetProperty(
190         resolved,
191         factory->NewStringFromStaticAscii("numberingSystem"),
192         factory->NewStringFromAsciiChecked(ns),
193         NONE,
194         SLOPPY).Assert();
195   } else {
196     JSObject::SetProperty(
197         resolved,
198         factory->NewStringFromStaticAscii("numberingSystem"),
199         factory->undefined_value(),
200         NONE,
201         SLOPPY).Assert();
202   }
203   delete numbering_system;
204 
205   // Set the locale
206   char result[ULOC_FULLNAME_CAPACITY];
207   status = U_ZERO_ERROR;
208   uloc_toLanguageTag(
209       icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
210   if (U_SUCCESS(status)) {
211     JSObject::SetProperty(
212         resolved,
213         factory->NewStringFromStaticAscii("locale"),
214         factory->NewStringFromAsciiChecked(result),
215         NONE,
216         SLOPPY).Assert();
217   } else {
218     // This would never happen, since we got the locale from ICU.
219     JSObject::SetProperty(
220         resolved,
221         factory->NewStringFromStaticAscii("locale"),
222         factory->NewStringFromStaticAscii("und"),
223         NONE,
224         SLOPPY).Assert();
225   }
226 }
227 
228 
229 template<int internal_fields, EternalHandles::SingletonHandle field>
GetEternal(Isolate * isolate)230 Handle<ObjectTemplateInfo> GetEternal(Isolate* isolate) {
231   if (isolate->eternal_handles()->Exists(field)) {
232     return Handle<ObjectTemplateInfo>::cast(
233         isolate->eternal_handles()->GetSingleton(field));
234   }
235   v8::Local<v8::ObjectTemplate> raw_template =
236       v8::ObjectTemplate::New(reinterpret_cast<v8::Isolate*>(isolate));
237   raw_template->SetInternalFieldCount(internal_fields);
238   return Handle<ObjectTemplateInfo>::cast(
239       isolate->eternal_handles()->CreateSingleton(
240         isolate,
241         *v8::Utils::OpenHandle(*raw_template),
242         field));
243 }
244 
245 
CreateICUNumberFormat(Isolate * isolate,const icu::Locale & icu_locale,Handle<JSObject> options)246 icu::DecimalFormat* CreateICUNumberFormat(
247     Isolate* isolate,
248     const icu::Locale& icu_locale,
249     Handle<JSObject> options) {
250   // Make formatter from options. Numbering system is added
251   // to the locale as Unicode extension (if it was specified at all).
252   UErrorCode status = U_ZERO_ERROR;
253   icu::DecimalFormat* number_format = NULL;
254   icu::UnicodeString style;
255   icu::UnicodeString currency;
256   if (ExtractStringSetting(isolate, options, "style", &style)) {
257     if (style == UNICODE_STRING_SIMPLE("currency")) {
258       icu::UnicodeString display;
259       ExtractStringSetting(isolate, options, "currency", &currency);
260       ExtractStringSetting(isolate, options, "currencyDisplay", &display);
261 
262 #if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6)
263       icu::NumberFormat::EStyles format_style;
264       if (display == UNICODE_STRING_SIMPLE("code")) {
265         format_style = icu::NumberFormat::kIsoCurrencyStyle;
266       } else if (display == UNICODE_STRING_SIMPLE("name")) {
267         format_style = icu::NumberFormat::kPluralCurrencyStyle;
268       } else {
269         format_style = icu::NumberFormat::kCurrencyStyle;
270       }
271 #else  // ICU version is 4.8 or above (we ignore versions below 4.0).
272       UNumberFormatStyle format_style;
273       if (display == UNICODE_STRING_SIMPLE("code")) {
274         format_style = UNUM_CURRENCY_ISO;
275       } else if (display == UNICODE_STRING_SIMPLE("name")) {
276         format_style = UNUM_CURRENCY_PLURAL;
277       } else {
278         format_style = UNUM_CURRENCY;
279       }
280 #endif
281 
282       number_format = static_cast<icu::DecimalFormat*>(
283           icu::NumberFormat::createInstance(icu_locale, format_style,  status));
284     } else if (style == UNICODE_STRING_SIMPLE("percent")) {
285       number_format = static_cast<icu::DecimalFormat*>(
286           icu::NumberFormat::createPercentInstance(icu_locale, status));
287       if (U_FAILURE(status)) {
288         delete number_format;
289         return NULL;
290       }
291       // Make sure 1.1% doesn't go into 2%.
292       number_format->setMinimumFractionDigits(1);
293     } else {
294       // Make a decimal instance by default.
295       number_format = static_cast<icu::DecimalFormat*>(
296           icu::NumberFormat::createInstance(icu_locale, status));
297     }
298   }
299 
300   if (U_FAILURE(status)) {
301     delete number_format;
302     return NULL;
303   }
304 
305   // Set all options.
306   if (!currency.isEmpty()) {
307     number_format->setCurrency(currency.getBuffer(), status);
308   }
309 
310   int32_t digits;
311   if (ExtractIntegerSetting(
312           isolate, options, "minimumIntegerDigits", &digits)) {
313     number_format->setMinimumIntegerDigits(digits);
314   }
315 
316   if (ExtractIntegerSetting(
317           isolate, options, "minimumFractionDigits", &digits)) {
318     number_format->setMinimumFractionDigits(digits);
319   }
320 
321   if (ExtractIntegerSetting(
322           isolate, options, "maximumFractionDigits", &digits)) {
323     number_format->setMaximumFractionDigits(digits);
324   }
325 
326   bool significant_digits_used = false;
327   if (ExtractIntegerSetting(
328           isolate, options, "minimumSignificantDigits", &digits)) {
329     number_format->setMinimumSignificantDigits(digits);
330     significant_digits_used = true;
331   }
332 
333   if (ExtractIntegerSetting(
334           isolate, options, "maximumSignificantDigits", &digits)) {
335     number_format->setMaximumSignificantDigits(digits);
336     significant_digits_used = true;
337   }
338 
339   number_format->setSignificantDigitsUsed(significant_digits_used);
340 
341   bool grouping;
342   if (ExtractBooleanSetting(isolate, options, "useGrouping", &grouping)) {
343     number_format->setGroupingUsed(grouping);
344   }
345 
346   // Set rounding mode.
347   number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp);
348 
349   return number_format;
350 }
351 
352 
SetResolvedNumberSettings(Isolate * isolate,const icu::Locale & icu_locale,icu::DecimalFormat * number_format,Handle<JSObject> resolved)353 void SetResolvedNumberSettings(Isolate* isolate,
354                                const icu::Locale& icu_locale,
355                                icu::DecimalFormat* number_format,
356                                Handle<JSObject> resolved) {
357   Factory* factory = isolate->factory();
358   icu::UnicodeString pattern;
359   number_format->toPattern(pattern);
360   JSObject::SetProperty(
361       resolved,
362       factory->NewStringFromStaticAscii("pattern"),
363       factory->NewStringFromTwoByte(
364         Vector<const uint16_t>(
365             reinterpret_cast<const uint16_t*>(pattern.getBuffer()),
366             pattern.length())).ToHandleChecked(),
367       NONE,
368       SLOPPY).Assert();
369 
370   // Set resolved currency code in options.currency if not empty.
371   icu::UnicodeString currency(number_format->getCurrency());
372   if (!currency.isEmpty()) {
373     JSObject::SetProperty(
374         resolved,
375         factory->NewStringFromStaticAscii("currency"),
376         factory->NewStringFromTwoByte(
377           Vector<const uint16_t>(
378               reinterpret_cast<const uint16_t*>(currency.getBuffer()),
379               currency.length())).ToHandleChecked(),
380         NONE,
381         SLOPPY).Assert();
382   }
383 
384   // Ugly hack. ICU doesn't expose numbering system in any way, so we have
385   // to assume that for given locale NumberingSystem constructor produces the
386   // same digits as NumberFormat/Calendar would.
387   UErrorCode status = U_ZERO_ERROR;
388   icu::NumberingSystem* numbering_system =
389       icu::NumberingSystem::createInstance(icu_locale, status);
390   if (U_SUCCESS(status)) {
391     const char* ns = numbering_system->getName();
392     JSObject::SetProperty(
393         resolved,
394         factory->NewStringFromStaticAscii("numberingSystem"),
395         factory->NewStringFromAsciiChecked(ns),
396         NONE,
397         SLOPPY).Assert();
398   } else {
399     JSObject::SetProperty(
400         resolved,
401         factory->NewStringFromStaticAscii("numberingSystem"),
402         factory->undefined_value(),
403         NONE,
404         SLOPPY).Assert();
405   }
406   delete numbering_system;
407 
408   JSObject::SetProperty(
409       resolved,
410       factory->NewStringFromStaticAscii("useGrouping"),
411       factory->ToBoolean(number_format->isGroupingUsed()),
412       NONE,
413       SLOPPY).Assert();
414 
415   JSObject::SetProperty(
416       resolved,
417       factory->NewStringFromStaticAscii("minimumIntegerDigits"),
418       factory->NewNumberFromInt(number_format->getMinimumIntegerDigits()),
419       NONE,
420       SLOPPY).Assert();
421 
422   JSObject::SetProperty(
423       resolved,
424       factory->NewStringFromStaticAscii("minimumFractionDigits"),
425       factory->NewNumberFromInt(number_format->getMinimumFractionDigits()),
426       NONE,
427       SLOPPY).Assert();
428 
429   JSObject::SetProperty(
430       resolved,
431       factory->NewStringFromStaticAscii("maximumFractionDigits"),
432       factory->NewNumberFromInt(number_format->getMaximumFractionDigits()),
433       NONE,
434       SLOPPY).Assert();
435 
436   Handle<String> key =
437       factory->NewStringFromStaticAscii("minimumSignificantDigits");
438   if (JSReceiver::HasOwnProperty(resolved, key)) {
439     JSObject::SetProperty(
440         resolved,
441         factory->NewStringFromStaticAscii("minimumSignificantDigits"),
442         factory->NewNumberFromInt(number_format->getMinimumSignificantDigits()),
443         NONE,
444         SLOPPY).Assert();
445   }
446 
447   key = factory->NewStringFromStaticAscii("maximumSignificantDigits");
448   if (JSReceiver::HasOwnProperty(resolved, key)) {
449     JSObject::SetProperty(
450         resolved,
451         factory->NewStringFromStaticAscii("maximumSignificantDigits"),
452         factory->NewNumberFromInt(number_format->getMaximumSignificantDigits()),
453         NONE,
454         SLOPPY).Assert();
455   }
456 
457   // Set the locale
458   char result[ULOC_FULLNAME_CAPACITY];
459   status = U_ZERO_ERROR;
460   uloc_toLanguageTag(
461       icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
462   if (U_SUCCESS(status)) {
463     JSObject::SetProperty(
464         resolved,
465         factory->NewStringFromStaticAscii("locale"),
466         factory->NewStringFromAsciiChecked(result),
467         NONE,
468         SLOPPY).Assert();
469   } else {
470     // This would never happen, since we got the locale from ICU.
471     JSObject::SetProperty(
472         resolved,
473         factory->NewStringFromStaticAscii("locale"),
474         factory->NewStringFromStaticAscii("und"),
475         NONE,
476         SLOPPY).Assert();
477   }
478 }
479 
480 
CreateICUCollator(Isolate * isolate,const icu::Locale & icu_locale,Handle<JSObject> options)481 icu::Collator* CreateICUCollator(
482     Isolate* isolate,
483     const icu::Locale& icu_locale,
484     Handle<JSObject> options) {
485   // Make collator from options.
486   icu::Collator* collator = NULL;
487   UErrorCode status = U_ZERO_ERROR;
488   collator = icu::Collator::createInstance(icu_locale, status);
489 
490   if (U_FAILURE(status)) {
491     delete collator;
492     return NULL;
493   }
494 
495   // Set flags first, and then override them with sensitivity if necessary.
496   bool numeric;
497   if (ExtractBooleanSetting(isolate, options, "numeric", &numeric)) {
498     collator->setAttribute(
499         UCOL_NUMERIC_COLLATION, numeric ? UCOL_ON : UCOL_OFF, status);
500   }
501 
502   // Normalization is always on, by the spec. We are free to optimize
503   // if the strings are already normalized (but we don't have a way to tell
504   // that right now).
505   collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
506 
507   icu::UnicodeString case_first;
508   if (ExtractStringSetting(isolate, options, "caseFirst", &case_first)) {
509     if (case_first == UNICODE_STRING_SIMPLE("upper")) {
510       collator->setAttribute(UCOL_CASE_FIRST, UCOL_UPPER_FIRST, status);
511     } else if (case_first == UNICODE_STRING_SIMPLE("lower")) {
512       collator->setAttribute(UCOL_CASE_FIRST, UCOL_LOWER_FIRST, status);
513     } else {
514       // Default (false/off).
515       collator->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status);
516     }
517   }
518 
519   icu::UnicodeString sensitivity;
520   if (ExtractStringSetting(isolate, options, "sensitivity", &sensitivity)) {
521     if (sensitivity == UNICODE_STRING_SIMPLE("base")) {
522       collator->setStrength(icu::Collator::PRIMARY);
523     } else if (sensitivity == UNICODE_STRING_SIMPLE("accent")) {
524       collator->setStrength(icu::Collator::SECONDARY);
525     } else if (sensitivity == UNICODE_STRING_SIMPLE("case")) {
526       collator->setStrength(icu::Collator::PRIMARY);
527       collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status);
528     } else {
529       // variant (default)
530       collator->setStrength(icu::Collator::TERTIARY);
531     }
532   }
533 
534   bool ignore;
535   if (ExtractBooleanSetting(isolate, options, "ignorePunctuation", &ignore)) {
536     if (ignore) {
537       collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status);
538     }
539   }
540 
541   return collator;
542 }
543 
544 
SetResolvedCollatorSettings(Isolate * isolate,const icu::Locale & icu_locale,icu::Collator * collator,Handle<JSObject> resolved)545 void SetResolvedCollatorSettings(Isolate* isolate,
546                                  const icu::Locale& icu_locale,
547                                  icu::Collator* collator,
548                                  Handle<JSObject> resolved) {
549   Factory* factory = isolate->factory();
550   UErrorCode status = U_ZERO_ERROR;
551 
552   JSObject::SetProperty(
553       resolved,
554       factory->NewStringFromStaticAscii("numeric"),
555       factory->ToBoolean(
556           collator->getAttribute(UCOL_NUMERIC_COLLATION, status) == UCOL_ON),
557       NONE,
558       SLOPPY).Assert();
559 
560   switch (collator->getAttribute(UCOL_CASE_FIRST, status)) {
561     case UCOL_LOWER_FIRST:
562       JSObject::SetProperty(
563           resolved,
564           factory->NewStringFromStaticAscii("caseFirst"),
565           factory->NewStringFromStaticAscii("lower"),
566           NONE,
567           SLOPPY).Assert();
568       break;
569     case UCOL_UPPER_FIRST:
570       JSObject::SetProperty(
571           resolved,
572           factory->NewStringFromStaticAscii("caseFirst"),
573           factory->NewStringFromStaticAscii("upper"),
574           NONE,
575           SLOPPY).Assert();
576       break;
577     default:
578       JSObject::SetProperty(
579           resolved,
580           factory->NewStringFromStaticAscii("caseFirst"),
581           factory->NewStringFromStaticAscii("false"),
582           NONE,
583           SLOPPY).Assert();
584   }
585 
586   switch (collator->getAttribute(UCOL_STRENGTH, status)) {
587     case UCOL_PRIMARY: {
588       JSObject::SetProperty(
589           resolved,
590           factory->NewStringFromStaticAscii("strength"),
591           factory->NewStringFromStaticAscii("primary"),
592           NONE,
593           SLOPPY).Assert();
594 
595       // case level: true + s1 -> case, s1 -> base.
596       if (UCOL_ON == collator->getAttribute(UCOL_CASE_LEVEL, status)) {
597         JSObject::SetProperty(
598             resolved,
599             factory->NewStringFromStaticAscii("sensitivity"),
600             factory->NewStringFromStaticAscii("case"),
601             NONE,
602             SLOPPY).Assert();
603       } else {
604         JSObject::SetProperty(
605             resolved,
606             factory->NewStringFromStaticAscii("sensitivity"),
607             factory->NewStringFromStaticAscii("base"),
608             NONE,
609             SLOPPY).Assert();
610       }
611       break;
612     }
613     case UCOL_SECONDARY:
614       JSObject::SetProperty(
615           resolved,
616           factory->NewStringFromStaticAscii("strength"),
617           factory->NewStringFromStaticAscii("secondary"),
618           NONE,
619           SLOPPY).Assert();
620       JSObject::SetProperty(
621           resolved,
622           factory->NewStringFromStaticAscii("sensitivity"),
623           factory->NewStringFromStaticAscii("accent"),
624           NONE,
625           SLOPPY).Assert();
626       break;
627     case UCOL_TERTIARY:
628       JSObject::SetProperty(
629           resolved,
630           factory->NewStringFromStaticAscii("strength"),
631           factory->NewStringFromStaticAscii("tertiary"),
632           NONE,
633           SLOPPY).Assert();
634       JSObject::SetProperty(
635           resolved,
636           factory->NewStringFromStaticAscii("sensitivity"),
637           factory->NewStringFromStaticAscii("variant"),
638           NONE,
639           SLOPPY).Assert();
640       break;
641     case UCOL_QUATERNARY:
642       // We shouldn't get quaternary and identical from ICU, but if we do
643       // put them into variant.
644       JSObject::SetProperty(
645           resolved,
646           factory->NewStringFromStaticAscii("strength"),
647           factory->NewStringFromStaticAscii("quaternary"),
648           NONE,
649           SLOPPY).Assert();
650       JSObject::SetProperty(
651           resolved,
652           factory->NewStringFromStaticAscii("sensitivity"),
653           factory->NewStringFromStaticAscii("variant"),
654           NONE,
655           SLOPPY).Assert();
656       break;
657     default:
658       JSObject::SetProperty(
659           resolved,
660           factory->NewStringFromStaticAscii("strength"),
661           factory->NewStringFromStaticAscii("identical"),
662           NONE,
663           SLOPPY).Assert();
664       JSObject::SetProperty(
665           resolved,
666           factory->NewStringFromStaticAscii("sensitivity"),
667           factory->NewStringFromStaticAscii("variant"),
668           NONE,
669           SLOPPY).Assert();
670   }
671 
672   JSObject::SetProperty(
673       resolved,
674       factory->NewStringFromStaticAscii("ignorePunctuation"),
675       factory->ToBoolean(collator->getAttribute(
676           UCOL_ALTERNATE_HANDLING, status) == UCOL_SHIFTED),
677       NONE,
678       SLOPPY).Assert();
679 
680   // Set the locale
681   char result[ULOC_FULLNAME_CAPACITY];
682   status = U_ZERO_ERROR;
683   uloc_toLanguageTag(
684       icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
685   if (U_SUCCESS(status)) {
686     JSObject::SetProperty(
687         resolved,
688         factory->NewStringFromStaticAscii("locale"),
689         factory->NewStringFromAsciiChecked(result),
690         NONE,
691         SLOPPY).Assert();
692   } else {
693     // This would never happen, since we got the locale from ICU.
694     JSObject::SetProperty(
695         resolved,
696         factory->NewStringFromStaticAscii("locale"),
697         factory->NewStringFromStaticAscii("und"),
698         NONE,
699         SLOPPY).Assert();
700   }
701 }
702 
703 
CreateICUBreakIterator(Isolate * isolate,const icu::Locale & icu_locale,Handle<JSObject> options)704 icu::BreakIterator* CreateICUBreakIterator(
705     Isolate* isolate,
706     const icu::Locale& icu_locale,
707     Handle<JSObject> options) {
708   UErrorCode status = U_ZERO_ERROR;
709   icu::BreakIterator* break_iterator = NULL;
710   icu::UnicodeString type;
711   if (!ExtractStringSetting(isolate, options, "type", &type)) return NULL;
712 
713   if (type == UNICODE_STRING_SIMPLE("character")) {
714     break_iterator =
715       icu::BreakIterator::createCharacterInstance(icu_locale, status);
716   } else if (type == UNICODE_STRING_SIMPLE("sentence")) {
717     break_iterator =
718       icu::BreakIterator::createSentenceInstance(icu_locale, status);
719   } else if (type == UNICODE_STRING_SIMPLE("line")) {
720     break_iterator =
721       icu::BreakIterator::createLineInstance(icu_locale, status);
722   } else {
723     // Defualt is word iterator.
724     break_iterator =
725       icu::BreakIterator::createWordInstance(icu_locale, status);
726   }
727 
728   if (U_FAILURE(status)) {
729     delete break_iterator;
730     return NULL;
731   }
732 
733   return break_iterator;
734 }
735 
736 
SetResolvedBreakIteratorSettings(Isolate * isolate,const icu::Locale & icu_locale,icu::BreakIterator * break_iterator,Handle<JSObject> resolved)737 void SetResolvedBreakIteratorSettings(Isolate* isolate,
738                                       const icu::Locale& icu_locale,
739                                       icu::BreakIterator* break_iterator,
740                                       Handle<JSObject> resolved) {
741   Factory* factory = isolate->factory();
742   UErrorCode status = U_ZERO_ERROR;
743 
744   // Set the locale
745   char result[ULOC_FULLNAME_CAPACITY];
746   status = U_ZERO_ERROR;
747   uloc_toLanguageTag(
748       icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
749   if (U_SUCCESS(status)) {
750     JSObject::SetProperty(
751         resolved,
752         factory->NewStringFromStaticAscii("locale"),
753         factory->NewStringFromAsciiChecked(result),
754         NONE,
755         SLOPPY).Assert();
756   } else {
757     // This would never happen, since we got the locale from ICU.
758     JSObject::SetProperty(
759         resolved,
760         factory->NewStringFromStaticAscii("locale"),
761         factory->NewStringFromStaticAscii("und"),
762         NONE,
763         SLOPPY).Assert();
764   }
765 }
766 
767 }  // namespace
768 
769 
770 // static
GetTemplate(Isolate * isolate)771 Handle<ObjectTemplateInfo> I18N::GetTemplate(Isolate* isolate) {
772   return GetEternal<1, i::EternalHandles::I18N_TEMPLATE_ONE>(isolate);
773 }
774 
775 
776 // static
GetTemplate2(Isolate * isolate)777 Handle<ObjectTemplateInfo> I18N::GetTemplate2(Isolate* isolate) {
778   return GetEternal<2, i::EternalHandles::I18N_TEMPLATE_TWO>(isolate);
779 }
780 
781 
782 // static
InitializeDateTimeFormat(Isolate * isolate,Handle<String> locale,Handle<JSObject> options,Handle<JSObject> resolved)783 icu::SimpleDateFormat* DateFormat::InitializeDateTimeFormat(
784     Isolate* isolate,
785     Handle<String> locale,
786     Handle<JSObject> options,
787     Handle<JSObject> resolved) {
788   // Convert BCP47 into ICU locale format.
789   UErrorCode status = U_ZERO_ERROR;
790   icu::Locale icu_locale;
791   char icu_result[ULOC_FULLNAME_CAPACITY];
792   int icu_length = 0;
793   v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
794   if (bcp47_locale.length() != 0) {
795     uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
796                         &icu_length, &status);
797     if (U_FAILURE(status) || icu_length == 0) {
798       return NULL;
799     }
800     icu_locale = icu::Locale(icu_result);
801   }
802 
803   icu::SimpleDateFormat* date_format = CreateICUDateFormat(
804       isolate, icu_locale, options);
805   if (!date_format) {
806     // Remove extensions and try again.
807     icu::Locale no_extension_locale(icu_locale.getBaseName());
808     date_format = CreateICUDateFormat(isolate, no_extension_locale, options);
809 
810     // Set resolved settings (pattern, numbering system, calendar).
811     SetResolvedDateSettings(
812         isolate, no_extension_locale, date_format, resolved);
813   } else {
814     SetResolvedDateSettings(isolate, icu_locale, date_format, resolved);
815   }
816 
817   return date_format;
818 }
819 
820 
UnpackDateFormat(Isolate * isolate,Handle<JSObject> obj)821 icu::SimpleDateFormat* DateFormat::UnpackDateFormat(
822     Isolate* isolate,
823     Handle<JSObject> obj) {
824   Handle<String> key =
825       isolate->factory()->NewStringFromStaticAscii("dateFormat");
826   if (JSReceiver::HasOwnProperty(obj, key)) {
827     return reinterpret_cast<icu::SimpleDateFormat*>(
828         obj->GetInternalField(0));
829   }
830 
831   return NULL;
832 }
833 
834 
835 template<class T>
DeleteNativeObjectAt(const v8::WeakCallbackData<v8::Value,void> & data,int index)836 void DeleteNativeObjectAt(const v8::WeakCallbackData<v8::Value, void>& data,
837                           int index) {
838   v8::Local<v8::Object> obj = v8::Handle<v8::Object>::Cast(data.GetValue());
839   delete reinterpret_cast<T*>(obj->GetAlignedPointerFromInternalField(index));
840 }
841 
842 
DestroyGlobalHandle(const v8::WeakCallbackData<v8::Value,void> & data)843 static void DestroyGlobalHandle(
844     const v8::WeakCallbackData<v8::Value, void>& data) {
845   GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
846 }
847 
848 
DeleteDateFormat(const v8::WeakCallbackData<v8::Value,void> & data)849 void DateFormat::DeleteDateFormat(
850     const v8::WeakCallbackData<v8::Value, void>& data) {
851   DeleteNativeObjectAt<icu::SimpleDateFormat>(data, 0);
852   DestroyGlobalHandle(data);
853 }
854 
855 
InitializeNumberFormat(Isolate * isolate,Handle<String> locale,Handle<JSObject> options,Handle<JSObject> resolved)856 icu::DecimalFormat* NumberFormat::InitializeNumberFormat(
857     Isolate* isolate,
858     Handle<String> locale,
859     Handle<JSObject> options,
860     Handle<JSObject> resolved) {
861   // Convert BCP47 into ICU locale format.
862   UErrorCode status = U_ZERO_ERROR;
863   icu::Locale icu_locale;
864   char icu_result[ULOC_FULLNAME_CAPACITY];
865   int icu_length = 0;
866   v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
867   if (bcp47_locale.length() != 0) {
868     uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
869                         &icu_length, &status);
870     if (U_FAILURE(status) || icu_length == 0) {
871       return NULL;
872     }
873     icu_locale = icu::Locale(icu_result);
874   }
875 
876   icu::DecimalFormat* number_format =
877       CreateICUNumberFormat(isolate, icu_locale, options);
878   if (!number_format) {
879     // Remove extensions and try again.
880     icu::Locale no_extension_locale(icu_locale.getBaseName());
881     number_format = CreateICUNumberFormat(
882         isolate, no_extension_locale, options);
883 
884     // Set resolved settings (pattern, numbering system).
885     SetResolvedNumberSettings(
886         isolate, no_extension_locale, number_format, resolved);
887   } else {
888     SetResolvedNumberSettings(isolate, icu_locale, number_format, resolved);
889   }
890 
891   return number_format;
892 }
893 
894 
UnpackNumberFormat(Isolate * isolate,Handle<JSObject> obj)895 icu::DecimalFormat* NumberFormat::UnpackNumberFormat(
896     Isolate* isolate,
897     Handle<JSObject> obj) {
898   Handle<String> key =
899       isolate->factory()->NewStringFromStaticAscii("numberFormat");
900   if (JSReceiver::HasOwnProperty(obj, key)) {
901     return reinterpret_cast<icu::DecimalFormat*>(obj->GetInternalField(0));
902   }
903 
904   return NULL;
905 }
906 
907 
DeleteNumberFormat(const v8::WeakCallbackData<v8::Value,void> & data)908 void NumberFormat::DeleteNumberFormat(
909     const v8::WeakCallbackData<v8::Value, void>& data) {
910   DeleteNativeObjectAt<icu::DecimalFormat>(data, 0);
911   DestroyGlobalHandle(data);
912 }
913 
914 
InitializeCollator(Isolate * isolate,Handle<String> locale,Handle<JSObject> options,Handle<JSObject> resolved)915 icu::Collator* Collator::InitializeCollator(
916     Isolate* isolate,
917     Handle<String> locale,
918     Handle<JSObject> options,
919     Handle<JSObject> resolved) {
920   // Convert BCP47 into ICU locale format.
921   UErrorCode status = U_ZERO_ERROR;
922   icu::Locale icu_locale;
923   char icu_result[ULOC_FULLNAME_CAPACITY];
924   int icu_length = 0;
925   v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
926   if (bcp47_locale.length() != 0) {
927     uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
928                         &icu_length, &status);
929     if (U_FAILURE(status) || icu_length == 0) {
930       return NULL;
931     }
932     icu_locale = icu::Locale(icu_result);
933   }
934 
935   icu::Collator* collator = CreateICUCollator(isolate, icu_locale, options);
936   if (!collator) {
937     // Remove extensions and try again.
938     icu::Locale no_extension_locale(icu_locale.getBaseName());
939     collator = CreateICUCollator(isolate, no_extension_locale, options);
940 
941     // Set resolved settings (pattern, numbering system).
942     SetResolvedCollatorSettings(
943         isolate, no_extension_locale, collator, resolved);
944   } else {
945     SetResolvedCollatorSettings(isolate, icu_locale, collator, resolved);
946   }
947 
948   return collator;
949 }
950 
951 
UnpackCollator(Isolate * isolate,Handle<JSObject> obj)952 icu::Collator* Collator::UnpackCollator(Isolate* isolate,
953                                         Handle<JSObject> obj) {
954   Handle<String> key = isolate->factory()->NewStringFromStaticAscii("collator");
955   if (JSReceiver::HasOwnProperty(obj, key)) {
956     return reinterpret_cast<icu::Collator*>(obj->GetInternalField(0));
957   }
958 
959   return NULL;
960 }
961 
962 
DeleteCollator(const v8::WeakCallbackData<v8::Value,void> & data)963 void Collator::DeleteCollator(
964     const v8::WeakCallbackData<v8::Value, void>& data) {
965   DeleteNativeObjectAt<icu::Collator>(data, 0);
966   DestroyGlobalHandle(data);
967 }
968 
969 
InitializeBreakIterator(Isolate * isolate,Handle<String> locale,Handle<JSObject> options,Handle<JSObject> resolved)970 icu::BreakIterator* BreakIterator::InitializeBreakIterator(
971     Isolate* isolate,
972     Handle<String> locale,
973     Handle<JSObject> options,
974     Handle<JSObject> resolved) {
975   // Convert BCP47 into ICU locale format.
976   UErrorCode status = U_ZERO_ERROR;
977   icu::Locale icu_locale;
978   char icu_result[ULOC_FULLNAME_CAPACITY];
979   int icu_length = 0;
980   v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
981   if (bcp47_locale.length() != 0) {
982     uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
983                         &icu_length, &status);
984     if (U_FAILURE(status) || icu_length == 0) {
985       return NULL;
986     }
987     icu_locale = icu::Locale(icu_result);
988   }
989 
990   icu::BreakIterator* break_iterator = CreateICUBreakIterator(
991       isolate, icu_locale, options);
992   if (!break_iterator) {
993     // Remove extensions and try again.
994     icu::Locale no_extension_locale(icu_locale.getBaseName());
995     break_iterator = CreateICUBreakIterator(
996         isolate, no_extension_locale, options);
997 
998     // Set resolved settings (locale).
999     SetResolvedBreakIteratorSettings(
1000         isolate, no_extension_locale, break_iterator, resolved);
1001   } else {
1002     SetResolvedBreakIteratorSettings(
1003         isolate, icu_locale, break_iterator, resolved);
1004   }
1005 
1006   return break_iterator;
1007 }
1008 
1009 
UnpackBreakIterator(Isolate * isolate,Handle<JSObject> obj)1010 icu::BreakIterator* BreakIterator::UnpackBreakIterator(Isolate* isolate,
1011                                                        Handle<JSObject> obj) {
1012   Handle<String> key =
1013       isolate->factory()->NewStringFromStaticAscii("breakIterator");
1014   if (JSReceiver::HasOwnProperty(obj, key)) {
1015     return reinterpret_cast<icu::BreakIterator*>(obj->GetInternalField(0));
1016   }
1017 
1018   return NULL;
1019 }
1020 
1021 
DeleteBreakIterator(const v8::WeakCallbackData<v8::Value,void> & data)1022 void BreakIterator::DeleteBreakIterator(
1023     const v8::WeakCallbackData<v8::Value, void>& data) {
1024   DeleteNativeObjectAt<icu::BreakIterator>(data, 0);
1025   DeleteNativeObjectAt<icu::UnicodeString>(data, 1);
1026   DestroyGlobalHandle(data);
1027 }
1028 
1029 } }  // namespace v8::internal
1030