Coverage Report

Created: 2021-08-22 09:07

/src/skia/third_party/externals/icu/source/common/localebuilder.cpp
Line
Count
Source (jump to first uncovered line)
1
// © 2019 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
4
#include <utility>
5
6
#include "bytesinkutil.h"  // CharStringByteSink
7
#include "charstr.h"
8
#include "cstring.h"
9
#include "ulocimp.h"
10
#include "unicode/localebuilder.h"
11
#include "unicode/locid.h"
12
13
U_NAMESPACE_BEGIN
14
15
0
#define UPRV_ISDIGIT(c) (((c) >= '0') && ((c) <= '9'))
16
0
#define UPRV_ISALPHANUM(c) (uprv_isASCIILetter(c) || UPRV_ISDIGIT(c) )
17
18
const char* kAttributeKey = "attribute";
19
20
0
static bool _isExtensionSubtags(char key, const char* s, int32_t len) {
21
0
    switch (uprv_tolower(key)) {
22
0
        case 'u':
23
0
            return ultag_isUnicodeExtensionSubtags(s, len);
24
0
        case 't':
25
0
            return ultag_isTransformedExtensionSubtags(s, len);
26
0
        case 'x':
27
0
            return ultag_isPrivateuseValueSubtags(s, len);
28
0
        default:
29
0
            return ultag_isExtensionSubtags(s, len);
30
0
    }
31
0
}
32
33
LocaleBuilder::LocaleBuilder() : UObject(), status_(U_ZERO_ERROR), language_(),
34
    script_(), region_(), variant_(nullptr), extensions_(nullptr)
35
0
{
36
0
    language_[0] = 0;
37
0
    script_[0] = 0;
38
0
    region_[0] = 0;
39
0
}
40
41
LocaleBuilder::~LocaleBuilder()
42
0
{
43
0
    delete variant_;
44
0
    delete extensions_;
45
0
}
46
47
LocaleBuilder& LocaleBuilder::setLocale(const Locale& locale)
48
0
{
49
0
    clear();
50
0
    setLanguage(locale.getLanguage());
51
0
    setScript(locale.getScript());
52
0
    setRegion(locale.getCountry());
53
0
    setVariant(locale.getVariant());
54
0
    extensions_ = locale.clone();
55
0
    if (extensions_ == nullptr) {
56
0
        status_ = U_MEMORY_ALLOCATION_ERROR;
57
0
    }
58
0
    return *this;
59
0
}
60
61
LocaleBuilder& LocaleBuilder::setLanguageTag(StringPiece tag)
62
0
{
63
0
    Locale l = Locale::forLanguageTag(tag, status_);
64
0
    if (U_FAILURE(status_)) { return *this; }
65
    // Because setLocale will reset status_ we need to return
66
    // first if we have error in forLanguageTag.
67
0
    setLocale(l);
68
0
    return *this;
69
0
}
70
71
static void setField(StringPiece input, char* dest, UErrorCode& errorCode,
72
0
                     UBool (*test)(const char*, int32_t)) {
73
0
    if (U_FAILURE(errorCode)) { return; }
74
0
    if (input.empty()) {
75
0
        dest[0] = '\0';
76
0
    } else if (test(input.data(), input.length())) {
77
0
        uprv_memcpy(dest, input.data(), input.length());
78
0
        dest[input.length()] = '\0';
79
0
    } else {
80
0
        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
81
0
    }
82
0
}
83
84
LocaleBuilder& LocaleBuilder::setLanguage(StringPiece language)
85
0
{
86
0
    setField(language, language_, status_, &ultag_isLanguageSubtag);
87
0
    return *this;
88
0
}
89
90
LocaleBuilder& LocaleBuilder::setScript(StringPiece script)
91
0
{
92
0
    setField(script, script_, status_, &ultag_isScriptSubtag);
93
0
    return *this;
94
0
}
95
96
LocaleBuilder& LocaleBuilder::setRegion(StringPiece region)
97
0
{
98
0
    setField(region, region_, status_, &ultag_isRegionSubtag);
99
0
    return *this;
100
0
}
101
102
0
static void transform(char* data, int32_t len) {
103
0
    for (int32_t i = 0; i < len; i++, data++) {
104
0
        if (*data == '_') {
105
0
            *data = '-';
106
0
        } else {
107
0
            *data = uprv_tolower(*data);
108
0
        }
109
0
    }
110
0
}
111
112
LocaleBuilder& LocaleBuilder::setVariant(StringPiece variant)
113
0
{
114
0
    if (U_FAILURE(status_)) { return *this; }
115
0
    if (variant.empty()) {
116
0
        delete variant_;
117
0
        variant_ = nullptr;
118
0
        return *this;
119
0
    }
120
0
    CharString* new_variant = new CharString(variant, status_);
121
0
    if (U_FAILURE(status_)) { return *this; }
122
0
    if (new_variant == nullptr) {
123
0
        status_ = U_MEMORY_ALLOCATION_ERROR;
124
0
        return *this;
125
0
    }
126
0
    transform(new_variant->data(), new_variant->length());
127
0
    if (!ultag_isVariantSubtags(new_variant->data(), new_variant->length())) {
128
0
        delete new_variant;
129
0
        status_ = U_ILLEGAL_ARGUMENT_ERROR;
130
0
        return *this;
131
0
    }
132
0
    delete variant_;
133
0
    variant_ = new_variant;
134
0
    return *this;
135
0
}
136
137
static bool
138
_isKeywordValue(const char* key, const char* value, int32_t value_len)
139
0
{
140
0
    if (key[1] == '\0') {
141
        // one char key
142
0
        return (UPRV_ISALPHANUM(uprv_tolower(key[0])) &&
143
0
                _isExtensionSubtags(key[0], value, value_len));
144
0
    } else if (uprv_strcmp(key, kAttributeKey) == 0) {
145
        // unicode attributes
146
0
        return ultag_isUnicodeLocaleAttributes(value, value_len);
147
0
    }
148
    // otherwise: unicode extension value
149
    // We need to convert from legacy key/value to unicode
150
    // key/value
151
0
    const char* unicode_locale_key = uloc_toUnicodeLocaleKey(key);
152
0
    const char* unicode_locale_type = uloc_toUnicodeLocaleType(key, value);
153
154
0
    return unicode_locale_key && unicode_locale_type &&
155
0
           ultag_isUnicodeLocaleKey(unicode_locale_key, -1) &&
156
0
           ultag_isUnicodeLocaleType(unicode_locale_type, -1);
157
0
}
158
159
static void
160
_copyExtensions(const Locale& from, icu::StringEnumeration *keywords,
161
                Locale& to, bool validate, UErrorCode& errorCode)
162
0
{
163
0
    if (U_FAILURE(errorCode)) { return; }
164
0
    LocalPointer<icu::StringEnumeration> ownedKeywords;
165
0
    if (keywords == nullptr) {
166
0
        ownedKeywords.adoptInstead(from.createKeywords(errorCode));
167
0
        if (U_FAILURE(errorCode) || ownedKeywords.isNull()) { return; }
168
0
        keywords = ownedKeywords.getAlias();
169
0
    }
170
0
    const char* key;
171
0
    while ((key = keywords->next(nullptr, errorCode)) != nullptr) {
172
0
        CharString value;
173
0
        CharStringByteSink sink(&value);
174
0
        from.getKeywordValue(key, sink, errorCode);
175
0
        if (U_FAILURE(errorCode)) { return; }
176
0
        if (uprv_strcmp(key, kAttributeKey) == 0) {
177
0
            transform(value.data(), value.length());
178
0
        }
179
0
        if (validate &&
180
0
            !_isKeywordValue(key, value.data(), value.length())) {
181
0
            errorCode = U_ILLEGAL_ARGUMENT_ERROR;
182
0
            return;
183
0
        }
184
0
        to.setKeywordValue(key, value.data(), errorCode);
185
0
        if (U_FAILURE(errorCode)) { return; }
186
0
    }
187
0
}
188
189
void static
190
_clearUAttributesAndKeyType(Locale& locale, UErrorCode& errorCode)
191
0
{
192
    // Clear Unicode attributes
193
0
    locale.setKeywordValue(kAttributeKey, "", errorCode);
194
195
    // Clear all Unicode keyword values
196
0
    LocalPointer<icu::StringEnumeration> iter(locale.createUnicodeKeywords(errorCode));
197
0
    if (U_FAILURE(errorCode) || iter.isNull()) { return; }
198
0
    const char* key;
199
0
    while ((key = iter->next(nullptr, errorCode)) != nullptr) {
200
0
        locale.setUnicodeKeywordValue(key, nullptr, errorCode);
201
0
    }
202
0
}
203
204
static void
205
_setUnicodeExtensions(Locale& locale, const CharString& value, UErrorCode& errorCode)
206
0
{
207
    // Add the unicode extensions to extensions_
208
0
    CharString locale_str("und-u-", errorCode);
209
0
    locale_str.append(value, errorCode);
210
0
    _copyExtensions(
211
0
        Locale::forLanguageTag(locale_str.data(), errorCode), nullptr,
212
0
        locale, false, errorCode);
213
0
}
214
215
LocaleBuilder& LocaleBuilder::setExtension(char key, StringPiece value)
216
0
{
217
0
    if (U_FAILURE(status_)) { return *this; }
218
0
    if (!UPRV_ISALPHANUM(key)) {
219
0
        status_ = U_ILLEGAL_ARGUMENT_ERROR;
220
0
        return *this;
221
0
    }
222
0
    CharString value_str(value, status_);
223
0
    if (U_FAILURE(status_)) { return *this; }
224
0
    transform(value_str.data(), value_str.length());
225
0
    if (!value_str.isEmpty() &&
226
0
            !_isExtensionSubtags(key, value_str.data(), value_str.length())) {
227
0
        status_ = U_ILLEGAL_ARGUMENT_ERROR;
228
0
        return *this;
229
0
    }
230
0
    if (extensions_ == nullptr) {
231
0
        extensions_ = new Locale();
232
0
        if (extensions_ == nullptr) {
233
0
            status_ = U_MEMORY_ALLOCATION_ERROR;
234
0
            return *this;
235
0
        }
236
0
    }
237
0
    if (uprv_tolower(key) != 'u') {
238
        // for t, x and others extension.
239
0
        extensions_->setKeywordValue(StringPiece(&key, 1), value_str.data(),
240
0
                                     status_);
241
0
        return *this;
242
0
    }
243
0
    _clearUAttributesAndKeyType(*extensions_, status_);
244
0
    if (U_FAILURE(status_)) { return *this; }
245
0
    if (!value.empty()) {
246
0
        _setUnicodeExtensions(*extensions_, value_str, status_);
247
0
    }
248
0
    return *this;
249
0
}
250
251
LocaleBuilder& LocaleBuilder::setUnicodeLocaleKeyword(
252
      StringPiece key, StringPiece type)
253
0
{
254
0
    if (U_FAILURE(status_)) { return *this; }
255
0
    if (!ultag_isUnicodeLocaleKey(key.data(), key.length()) ||
256
0
            (!type.empty() &&
257
0
                 !ultag_isUnicodeLocaleType(type.data(), type.length()))) {
258
0
      status_ = U_ILLEGAL_ARGUMENT_ERROR;
259
0
      return *this;
260
0
    }
261
0
    if (extensions_ == nullptr) {
262
0
        extensions_ = new Locale();
263
0
    }
264
0
    if (extensions_ == nullptr) {
265
0
        status_ = U_MEMORY_ALLOCATION_ERROR;
266
0
        return *this;
267
0
    }
268
0
    extensions_->setUnicodeKeywordValue(key, type, status_);
269
0
    return *this;
270
0
}
271
272
LocaleBuilder& LocaleBuilder::addUnicodeLocaleAttribute(
273
    StringPiece value)
274
0
{
275
0
    CharString value_str(value, status_);
276
0
    if (U_FAILURE(status_)) { return *this; }
277
0
    transform(value_str.data(), value_str.length());
278
0
    if (!ultag_isUnicodeLocaleAttribute(value_str.data(), value_str.length())) {
279
0
        status_ = U_ILLEGAL_ARGUMENT_ERROR;
280
0
        return *this;
281
0
    }
282
0
    if (extensions_ == nullptr) {
283
0
        extensions_ = new Locale();
284
0
        if (extensions_ == nullptr) {
285
0
            status_ = U_MEMORY_ALLOCATION_ERROR;
286
0
            return *this;
287
0
        }
288
0
        extensions_->setKeywordValue(kAttributeKey, value_str.data(), status_);
289
0
        return *this;
290
0
    }
291
292
0
    CharString attributes;
293
0
    CharStringByteSink sink(&attributes);
294
0
    UErrorCode localErrorCode = U_ZERO_ERROR;
295
0
    extensions_->getKeywordValue(kAttributeKey, sink, localErrorCode);
296
0
    if (U_FAILURE(localErrorCode)) {
297
0
        CharString new_attributes(value_str.data(), status_);
298
        // No attributes, set the attribute.
299
0
        extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_);
300
0
        return *this;
301
0
    }
302
303
0
    transform(attributes.data(),attributes.length());
304
0
    const char* start = attributes.data();
305
0
    const char* limit = attributes.data() + attributes.length();
306
0
    CharString new_attributes;
307
0
    bool inserted = false;
308
0
    while (start < limit) {
309
0
        if (!inserted) {
310
0
            int cmp = uprv_strcmp(start, value_str.data());
311
0
            if (cmp == 0) { return *this; }  // Found it in attributes: Just return
312
0
            if (cmp > 0) {
313
0
                if (!new_attributes.isEmpty()) new_attributes.append('_', status_);
314
0
                new_attributes.append(value_str.data(), status_);
315
0
                inserted = true;
316
0
            }
317
0
        }
318
0
        if (!new_attributes.isEmpty()) {
319
0
            new_attributes.append('_', status_);
320
0
        }
321
0
        new_attributes.append(start, status_);
322
0
        start += uprv_strlen(start) + 1;
323
0
    }
324
0
    if (!inserted) {
325
0
        if (!new_attributes.isEmpty()) {
326
0
            new_attributes.append('_', status_);
327
0
        }
328
0
        new_attributes.append(value_str.data(), status_);
329
0
    }
330
    // Not yet in the attributes, set the attribute.
331
0
    extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_);
332
0
    return *this;
333
0
}
334
335
LocaleBuilder& LocaleBuilder::removeUnicodeLocaleAttribute(
336
    StringPiece value)
337
0
{
338
0
    CharString value_str(value, status_);
339
0
    if (U_FAILURE(status_)) { return *this; }
340
0
    transform(value_str.data(), value_str.length());
341
0
    if (!ultag_isUnicodeLocaleAttribute(value_str.data(), value_str.length())) {
342
0
        status_ = U_ILLEGAL_ARGUMENT_ERROR;
343
0
        return *this;
344
0
    }
345
0
    if (extensions_ == nullptr) { return *this; }
346
0
    UErrorCode localErrorCode = U_ZERO_ERROR;
347
0
    CharString attributes;
348
0
    CharStringByteSink sink(&attributes);
349
0
    extensions_->getKeywordValue(kAttributeKey, sink, localErrorCode);
350
    // get failure, just return
351
0
    if (U_FAILURE(localErrorCode)) { return *this; }
352
    // Do not have any attributes, just return.
353
0
    if (attributes.isEmpty()) { return *this; }
354
355
0
    char* p = attributes.data();
356
    // Replace null terminiator in place for _ and - so later
357
    // we can use uprv_strcmp to compare.
358
0
    for (int32_t i = 0; i < attributes.length(); i++, p++) {
359
0
        *p = (*p == '_' || *p == '-') ? '\0' : uprv_tolower(*p);
360
0
    }
361
362
0
    const char* start = attributes.data();
363
0
    const char* limit = attributes.data() + attributes.length();
364
0
    CharString new_attributes;
365
0
    bool found = false;
366
0
    while (start < limit) {
367
0
        if (uprv_strcmp(start, value_str.data()) == 0) {
368
0
            found = true;
369
0
        } else {
370
0
            if (!new_attributes.isEmpty()) {
371
0
                new_attributes.append('_', status_);
372
0
            }
373
0
            new_attributes.append(start, status_);
374
0
        }
375
0
        start += uprv_strlen(start) + 1;
376
0
    }
377
    // Found the value in attributes, set the attribute.
378
0
    if (found) {
379
0
        extensions_->setKeywordValue(kAttributeKey, new_attributes.data(), status_);
380
0
    }
381
0
    return *this;
382
0
}
383
384
LocaleBuilder& LocaleBuilder::clear()
385
0
{
386
0
    status_ = U_ZERO_ERROR;
387
0
    language_[0] = 0;
388
0
    script_[0] = 0;
389
0
    region_[0] = 0;
390
0
    delete variant_;
391
0
    variant_ = nullptr;
392
0
    clearExtensions();
393
0
    return *this;
394
0
}
395
396
LocaleBuilder& LocaleBuilder::clearExtensions()
397
0
{
398
0
    delete extensions_;
399
0
    extensions_ = nullptr;
400
0
    return *this;
401
0
}
402
403
0
Locale makeBogusLocale() {
404
0
  Locale bogus;
405
0
  bogus.setToBogus();
406
0
  return bogus;
407
0
}
408
409
void LocaleBuilder::copyExtensionsFrom(const Locale& src, UErrorCode& errorCode)
410
0
{
411
0
    if (U_FAILURE(errorCode)) { return; }
412
0
    LocalPointer<icu::StringEnumeration> keywords(src.createKeywords(errorCode));
413
0
    if (U_FAILURE(errorCode) || keywords.isNull() || keywords->count(errorCode) == 0) {
414
        // Error, or no extensions to copy.
415
0
        return;
416
0
    }
417
0
    if (extensions_ == nullptr) {
418
0
        extensions_ = new Locale();
419
0
        if (extensions_ == nullptr) {
420
0
            status_ = U_MEMORY_ALLOCATION_ERROR;
421
0
            return;
422
0
        }
423
0
    }
424
0
    _copyExtensions(src, keywords.getAlias(), *extensions_, false, errorCode);
425
0
}
426
427
Locale LocaleBuilder::build(UErrorCode& errorCode)
428
0
{
429
0
    if (U_FAILURE(errorCode)) {
430
0
        return makeBogusLocale();
431
0
    }
432
0
    if (U_FAILURE(status_)) {
433
0
        errorCode = status_;
434
0
        return makeBogusLocale();
435
0
    }
436
0
    CharString locale_str(language_, errorCode);
437
0
    if (uprv_strlen(script_) > 0) {
438
0
        locale_str.append('-', errorCode).append(StringPiece(script_), errorCode);
439
0
    }
440
0
    if (uprv_strlen(region_) > 0) {
441
0
        locale_str.append('-', errorCode).append(StringPiece(region_), errorCode);
442
0
    }
443
0
    if (variant_ != nullptr) {
444
0
        locale_str.append('-', errorCode).append(StringPiece(variant_->data()), errorCode);
445
0
    }
446
0
    if (U_FAILURE(errorCode)) {
447
0
        return makeBogusLocale();
448
0
    }
449
0
    Locale product(locale_str.data());
450
0
    if (extensions_ != nullptr) {
451
0
        _copyExtensions(*extensions_, nullptr, product, true, errorCode);
452
0
    }
453
0
    if (U_FAILURE(errorCode)) {
454
0
        return makeBogusLocale();
455
0
    }
456
0
    return product;
457
0
}
458
459
0
UBool LocaleBuilder::copyErrorTo(UErrorCode &outErrorCode) const {
460
0
    if (U_FAILURE(outErrorCode)) {
461
        // Do not overwrite the older error code
462
0
        return TRUE;
463
0
    }
464
0
    outErrorCode = status_;
465
0
    return U_FAILURE(outErrorCode);
466
0
}
467
468
U_NAMESPACE_END