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