Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/intl/locale/MozLocale.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "mozilla/intl/MozLocale.h"
7
8
#include "nsReadableUtils.h"
9
#include "nsUnicharUtils.h"
10
11
#include "unicode/uloc.h"
12
13
using namespace mozilla::intl;
14
15
/**
16
 * Note: The file name is `MozLocale` to avoid compilation problems on case-insensitive
17
 * Windows. The class name is `Locale`.
18
 */
19
Locale::Locale(const nsACString& aLocale)
20
12
{
21
12
  MOZ_ASSERT(!aLocale.IsEmpty(), "Locale string cannot be empty");
22
12
23
12
  int32_t position = 0;
24
12
25
12
  if (!IsASCII(aLocale)) {
26
0
    mIsWellFormed = false;
27
0
    return;
28
0
  }
29
12
30
12
  nsAutoCString normLocale(aLocale);
31
12
  normLocale.ReplaceChar('_', '-');
32
12
33
12
  /**
34
12
   * BCP47 language tag:
35
12
   *
36
12
   * langtag = language            2*3ALPHA
37
12
   *           ["-" extlang]       3ALPHA *2("-" 3ALPHA)
38
12
   *           ["-" script]        4ALPHA
39
12
   *           ["-" region]        2ALPHA / 3DIGIT
40
12
   *           *("-" variant)      5*8alphanum / (DIGIT 3alphanum)
41
12
   *           *("-" extension)    [0-9a-wy-z] 1*("-" (1*8alphanum))
42
12
   *           ["-" privateuse]    x 1*("-" (1*8alphanum))
43
12
   *
44
12
   * This class currently supports a subset of the full BCP47 language tag
45
12
   * with a single extension of allowing variants to be 3ALPHA to support
46
12
   * `ja-JP-mac` code:
47
12
   *
48
12
   * langtag = language            2*3ALPHA
49
12
   *           ["-" script]        4ALPHA
50
12
   *           ["-" region]        2ALPHA
51
12
   *           *("-" variant)      3*8alphanum
52
12
   *           ["-"] privateuse]   "x" 1*("-" (1*8alphanum))
53
12
   *
54
12
   * The `position` variable represents the currently expected section of the tag
55
12
   * and intentionally skips positions (like `extlang`) which may be added later.
56
12
   *
57
12
   *  language-extlangs-script-region-variant-extension-privateuse
58
12
   *  --- 0 -- --- 1 -- -- 2 - -- 3 - -- 4 -- --- x --- ---- 6 ---
59
12
   */
60
30
  for (const nsACString& subTag : normLocale.Split('-')) {
61
30
    auto slen = subTag.Length();
62
30
    if (slen > 8) {
63
0
      mIsWellFormed = false;
64
0
      return;
65
30
    } else if (position == 6) {
66
0
      ToLowerCase(*mPrivateUse.AppendElement(subTag));
67
30
    } else if (subTag.LowerCaseEqualsLiteral("x")) {
68
0
      position = 6;
69
30
    } else if (position == 0) {
70
12
      if (slen < 2 || slen > 3) {
71
0
        mIsWellFormed = false;
72
0
        return;
73
0
      }
74
12
      mLanguage = subTag;
75
12
      ToLowerCase(mLanguage);
76
12
      position = 2;
77
18
    } else if (position <= 2 && slen == 4) {
78
6
      mScript = subTag;
79
6
      ToLowerCase(mScript);
80
6
      mScript.Replace(0, 1, ToUpperCase(mScript[0]));
81
6
      position = 3;
82
12
    } else if (position <= 3 && slen == 2) {
83
12
      mRegion = subTag;
84
12
      ToUpperCase(mRegion);
85
12
      position = 4;
86
12
    } else if (position <= 4 && slen >= 5 && slen <= 8) {
87
0
      nsAutoCString lcSubTag(subTag);
88
0
      ToLowerCase(lcSubTag);
89
0
      mVariants.InsertElementSorted(lcSubTag);
90
0
      position = 4;
91
0
    }
92
30
  }
93
12
}
94
95
const nsCString
96
Locale::AsString() const
97
9
{
98
9
  nsCString tag;
99
9
100
9
  if (!mIsWellFormed) {
101
0
    tag.AppendLiteral("und");
102
0
    return tag;
103
0
  }
104
9
105
9
  tag.Append(mLanguage);
106
9
107
9
  if (!mScript.IsEmpty()) {
108
3
    tag.AppendLiteral("-");
109
3
    tag.Append(mScript);
110
3
  }
111
9
112
9
  if (!mRegion.IsEmpty()) {
113
6
    tag.AppendLiteral("-");
114
6
    tag.Append(mRegion);
115
6
  }
116
9
117
9
  for (const auto& variant : mVariants) {
118
0
    tag.AppendLiteral("-");
119
0
    tag.Append(variant);
120
0
  }
121
9
122
9
  if (!mPrivateUse.IsEmpty()) {
123
0
    if (tag.IsEmpty()) {
124
0
      tag.AppendLiteral("x");
125
0
    } else {
126
0
      tag.AppendLiteral("-x");
127
0
    }
128
0
129
0
    for (const auto& subTag : mPrivateUse) {
130
0
      tag.AppendLiteral("-");
131
0
      tag.Append(subTag);
132
0
    }
133
0
  }
134
9
  return tag;
135
9
}
136
137
const nsACString&
138
Locale::GetLanguage() const
139
0
{
140
0
  return mLanguage;
141
0
}
142
143
const nsACString&
144
Locale::GetScript() const
145
0
{
146
0
  return mScript;
147
0
}
148
149
const nsACString&
150
Locale::GetRegion() const
151
0
{
152
0
  return mRegion;
153
0
}
154
155
const nsTArray<nsCString>&
156
Locale::GetVariants() const
157
0
{
158
0
  return mVariants;
159
0
}
160
161
bool
162
Locale::Matches(const Locale& aOther, bool aThisRange, bool aOtherRange) const
163
15
{
164
15
  if (!IsWellFormed() || !aOther.IsWellFormed()) {
165
15
    return false;
166
15
  }
167
0
168
0
  if ((!aThisRange || !mLanguage.IsEmpty()) &&
169
0
      (!aOtherRange || !aOther.mLanguage.IsEmpty()) &&
170
0
      !mLanguage.Equals(aOther.mLanguage)) {
171
0
    return false;
172
0
  }
173
0
174
0
  if ((!aThisRange || !mScript.IsEmpty()) &&
175
0
      (!aOtherRange || !aOther.mScript.IsEmpty()) &&
176
0
      !mScript.Equals(aOther.mScript)) {
177
0
    return false;
178
0
  }
179
0
  if ((!aThisRange || !mRegion.IsEmpty()) &&
180
0
      (!aOtherRange || !aOther.mRegion.IsEmpty()) &&
181
0
      !mRegion.Equals(aOther.mRegion)) {
182
0
    return false;
183
0
  }
184
0
  if ((!aThisRange || !mVariants.IsEmpty()) &&
185
0
      (!aOtherRange || !aOther.mVariants.IsEmpty()) &&
186
0
      mVariants != aOther.mVariants) {
187
0
    return false;
188
0
  }
189
0
  return true;
190
0
}
191
192
bool
193
Locale::AddLikelySubtags()
194
6
{
195
6
  const int32_t kLocaleMax = 160;
196
6
  char maxLocale[kLocaleMax];
197
6
198
6
  UErrorCode status = U_ZERO_ERROR;
199
6
  uloc_addLikelySubtags(AsString().get(), maxLocale, kLocaleMax, &status);
200
6
201
6
  if (U_FAILURE(status)) {
202
0
    return false;
203
0
  }
204
6
205
6
  nsDependentCString maxLocStr(maxLocale);
206
6
  Locale loc = Locale(maxLocStr);
207
6
208
6
  if (loc == *this) {
209
0
    return false;
210
0
  }
211
6
212
6
  mLanguage = loc.mLanguage;
213
6
  mScript = loc.mScript;
214
6
  mRegion = loc.mRegion;
215
6
216
6
  // We don't update variant from likelySubtag since it's not going to
217
6
  // provide it and we want to preserve the range
218
6
219
6
  return true;
220
6
}
221
222
void
223
Locale::ClearVariants()
224
3
{
225
3
  mVariants.Clear();
226
3
}
227
228
void
229
Locale::ClearRegion()
230
6
{
231
6
  mRegion.Truncate();
232
6
}