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
28namespace v8 {
29namespace internal {
30
31namespace {
32
33bool 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
49bool 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
63bool 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
77icu::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
125void 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
229template<int internal_fields, EternalHandles::SingletonHandle field>
230Handle<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
246icu::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
353void 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
481icu::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
545void 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
704icu::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
737void 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
771Handle<ObjectTemplateInfo> I18N::GetTemplate(Isolate* isolate) {
772  return GetEternal<1, i::EternalHandles::I18N_TEMPLATE_ONE>(isolate);
773}
774
775
776// static
777Handle<ObjectTemplateInfo> I18N::GetTemplate2(Isolate* isolate) {
778  return GetEternal<2, i::EternalHandles::I18N_TEMPLATE_TWO>(isolate);
779}
780
781
782// static
783icu::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
821icu::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
835template<class T>
836void 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
843static void DestroyGlobalHandle(
844    const v8::WeakCallbackData<v8::Value, void>& data) {
845  GlobalHandles::Destroy(reinterpret_cast<Object**>(data.GetParameter()));
846}
847
848
849void DateFormat::DeleteDateFormat(
850    const v8::WeakCallbackData<v8::Value, void>& data) {
851  DeleteNativeObjectAt<icu::SimpleDateFormat>(data, 0);
852  DestroyGlobalHandle(data);
853}
854
855
856icu::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
895icu::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
908void NumberFormat::DeleteNumberFormat(
909    const v8::WeakCallbackData<v8::Value, void>& data) {
910  DeleteNativeObjectAt<icu::DecimalFormat>(data, 0);
911  DestroyGlobalHandle(data);
912}
913
914
915icu::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
952icu::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
963void Collator::DeleteCollator(
964    const v8::WeakCallbackData<v8::Value, void>& data) {
965  DeleteNativeObjectAt<icu::Collator>(data, 0);
966  DestroyGlobalHandle(data);
967}
968
969
970icu::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
1010icu::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
1022void 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