33 |
33 |
34 import java.util.ArrayList; |
34 import java.util.ArrayList; |
35 import java.util.HashMap; |
35 import java.util.HashMap; |
36 import java.util.HashSet; |
36 import java.util.HashSet; |
37 import java.util.List; |
37 import java.util.List; |
|
38 import java.util.Map; |
38 import java.util.Set; |
39 import java.util.Set; |
39 |
40 |
40 public final class InternalLocaleBuilder { |
41 public final class InternalLocaleBuilder { |
41 |
42 |
42 private String _language = ""; |
43 private static final CaseInsensitiveChar PRIVATEUSE_KEY |
43 private String _script = ""; |
44 = new CaseInsensitiveChar(LanguageTag.PRIVATEUSE); |
44 private String _region = ""; |
45 |
45 private String _variant = ""; |
46 private String language = ""; |
46 |
47 private String script = ""; |
47 private static final CaseInsensitiveChar PRIVUSE_KEY = new CaseInsensitiveChar(LanguageTag.PRIVATEUSE.charAt(0)); |
48 private String region = ""; |
48 |
49 private String variant = ""; |
49 private HashMap<CaseInsensitiveChar, String> _extensions; |
50 |
50 private HashSet<CaseInsensitiveString> _uattributes; |
51 private Map<CaseInsensitiveChar, String> extensions; |
51 private HashMap<CaseInsensitiveString, String> _ukeywords; |
52 private Set<CaseInsensitiveString> uattributes; |
|
53 private Map<CaseInsensitiveString, String> ukeywords; |
52 |
54 |
53 |
55 |
54 public InternalLocaleBuilder() { |
56 public InternalLocaleBuilder() { |
55 } |
57 } |
56 |
58 |
57 public InternalLocaleBuilder setLanguage(String language) throws LocaleSyntaxException { |
59 public InternalLocaleBuilder setLanguage(String language) throws LocaleSyntaxException { |
58 if (language == null || language.length() == 0) { |
60 if (LocaleUtils.isEmpty(language)) { |
59 _language = ""; |
61 this.language = ""; |
60 } else { |
62 } else { |
61 if (!LanguageTag.isLanguage(language)) { |
63 if (!LanguageTag.isLanguage(language)) { |
62 throw new LocaleSyntaxException("Ill-formed language: " + language, 0); |
64 throw new LocaleSyntaxException("Ill-formed language: " + language, 0); |
63 } |
65 } |
64 _language = language; |
66 this.language = language; |
65 } |
67 } |
66 return this; |
68 return this; |
67 } |
69 } |
68 |
70 |
69 public InternalLocaleBuilder setScript(String script) throws LocaleSyntaxException { |
71 public InternalLocaleBuilder setScript(String script) throws LocaleSyntaxException { |
70 if (script == null || script.length() == 0) { |
72 if (LocaleUtils.isEmpty(script)) { |
71 _script = ""; |
73 this.script = ""; |
72 } else { |
74 } else { |
73 if (!LanguageTag.isScript(script)) { |
75 if (!LanguageTag.isScript(script)) { |
74 throw new LocaleSyntaxException("Ill-formed script: " + script, 0); |
76 throw new LocaleSyntaxException("Ill-formed script: " + script, 0); |
75 } |
77 } |
76 _script = script; |
78 this.script = script; |
77 } |
79 } |
78 return this; |
80 return this; |
79 } |
81 } |
80 |
82 |
81 public InternalLocaleBuilder setRegion(String region) throws LocaleSyntaxException { |
83 public InternalLocaleBuilder setRegion(String region) throws LocaleSyntaxException { |
82 if (region == null || region.length() == 0) { |
84 if (LocaleUtils.isEmpty(region)) { |
83 _region = ""; |
85 this.region = ""; |
84 } else { |
86 } else { |
85 if (!LanguageTag.isRegion(region)) { |
87 if (!LanguageTag.isRegion(region)) { |
86 throw new LocaleSyntaxException("Ill-formed region: " + region, 0); |
88 throw new LocaleSyntaxException("Ill-formed region: " + region, 0); |
87 } |
89 } |
88 _region = region; |
90 this.region = region; |
89 } |
91 } |
90 return this; |
92 return this; |
91 } |
93 } |
92 |
94 |
93 public InternalLocaleBuilder setVariant(String variant) throws LocaleSyntaxException { |
95 public InternalLocaleBuilder setVariant(String variant) throws LocaleSyntaxException { |
94 if (variant == null || variant.length() == 0) { |
96 if (LocaleUtils.isEmpty(variant)) { |
95 _variant = ""; |
97 this.variant = ""; |
96 } else { |
98 } else { |
97 // normalize separators to "_" |
99 // normalize separators to "_" |
98 String var = variant.replaceAll(LanguageTag.SEP, BaseLocale.SEP); |
100 String var = variant.replaceAll(LanguageTag.SEP, BaseLocale.SEP); |
99 int errIdx = checkVariants(var, BaseLocale.SEP); |
101 int errIdx = checkVariants(var, BaseLocale.SEP); |
100 if (errIdx != -1) { |
102 if (errIdx != -1) { |
101 throw new LocaleSyntaxException("Ill-formed variant: " + variant, errIdx); |
103 throw new LocaleSyntaxException("Ill-formed variant: " + variant, errIdx); |
102 } |
104 } |
103 _variant = var; |
105 this.variant = var; |
104 } |
106 } |
105 return this; |
107 return this; |
106 } |
108 } |
107 |
109 |
108 public InternalLocaleBuilder addUnicodeLocaleAttribute(String attribute) throws LocaleSyntaxException { |
110 public InternalLocaleBuilder addUnicodeLocaleAttribute(String attribute) throws LocaleSyntaxException { |
109 if (!UnicodeLocaleExtension.isAttribute(attribute)) { |
111 if (!UnicodeLocaleExtension.isAttribute(attribute)) { |
110 throw new LocaleSyntaxException("Ill-formed Unicode locale attribute: " + attribute); |
112 throw new LocaleSyntaxException("Ill-formed Unicode locale attribute: " + attribute); |
111 } |
113 } |
112 // Use case insensitive string to prevent duplication |
114 // Use case insensitive string to prevent duplication |
113 if (_uattributes == null) { |
115 if (uattributes == null) { |
114 _uattributes = new HashSet<CaseInsensitiveString>(4); |
116 uattributes = new HashSet<>(4); |
115 } |
117 } |
116 _uattributes.add(new CaseInsensitiveString(attribute)); |
118 uattributes.add(new CaseInsensitiveString(attribute)); |
117 return this; |
119 return this; |
118 } |
120 } |
119 |
121 |
120 public InternalLocaleBuilder removeUnicodeLocaleAttribute(String attribute) throws LocaleSyntaxException { |
122 public InternalLocaleBuilder removeUnicodeLocaleAttribute(String attribute) throws LocaleSyntaxException { |
121 if (attribute == null || !UnicodeLocaleExtension.isAttribute(attribute)) { |
123 if (attribute == null || !UnicodeLocaleExtension.isAttribute(attribute)) { |
122 throw new LocaleSyntaxException("Ill-formed Unicode locale attribute: " + attribute); |
124 throw new LocaleSyntaxException("Ill-formed Unicode locale attribute: " + attribute); |
123 } |
125 } |
124 if (_uattributes != null) { |
126 if (uattributes != null) { |
125 _uattributes.remove(new CaseInsensitiveString(attribute)); |
127 uattributes.remove(new CaseInsensitiveString(attribute)); |
126 } |
128 } |
127 return this; |
129 return this; |
128 } |
130 } |
129 |
131 |
130 public InternalLocaleBuilder setUnicodeLocaleKeyword(String key, String type) throws LocaleSyntaxException { |
132 public InternalLocaleBuilder setUnicodeLocaleKeyword(String key, String type) throws LocaleSyntaxException { |
132 throw new LocaleSyntaxException("Ill-formed Unicode locale keyword key: " + key); |
134 throw new LocaleSyntaxException("Ill-formed Unicode locale keyword key: " + key); |
133 } |
135 } |
134 |
136 |
135 CaseInsensitiveString cikey = new CaseInsensitiveString(key); |
137 CaseInsensitiveString cikey = new CaseInsensitiveString(key); |
136 if (type == null) { |
138 if (type == null) { |
137 if (_ukeywords != null) { |
139 if (ukeywords != null) { |
138 // null type is used for remove the key |
140 // null type is used for remove the key |
139 _ukeywords.remove(cikey); |
141 ukeywords.remove(cikey); |
140 } |
142 } |
141 } else { |
143 } else { |
142 if (type.length() != 0) { |
144 if (type.length() != 0) { |
143 // normalize separator to "-" |
145 // normalize separator to "-" |
144 String tp = type.replaceAll(BaseLocale.SEP, LanguageTag.SEP); |
146 String tp = type.replaceAll(BaseLocale.SEP, LanguageTag.SEP); |
145 // validate |
147 // validate |
146 StringTokenIterator itr = new StringTokenIterator(tp, LanguageTag.SEP); |
148 StringTokenIterator itr = new StringTokenIterator(tp, LanguageTag.SEP); |
147 while (!itr.isDone()) { |
149 while (!itr.isDone()) { |
148 String s = itr.current(); |
150 String s = itr.current(); |
149 if (!UnicodeLocaleExtension.isTypeSubtag(s)) { |
151 if (!UnicodeLocaleExtension.isTypeSubtag(s)) { |
150 throw new LocaleSyntaxException("Ill-formed Unicode locale keyword type: " + type, itr.currentStart()); |
152 throw new LocaleSyntaxException("Ill-formed Unicode locale keyword type: " |
|
153 + type, |
|
154 itr.currentStart()); |
151 } |
155 } |
152 itr.next(); |
156 itr.next(); |
153 } |
157 } |
154 } |
158 } |
155 if (_ukeywords == null) { |
159 if (ukeywords == null) { |
156 _ukeywords = new HashMap<CaseInsensitiveString, String>(4); |
160 ukeywords = new HashMap<>(4); |
157 } |
161 } |
158 _ukeywords.put(cikey, type); |
162 ukeywords.put(cikey, type); |
159 } |
163 } |
160 return this; |
164 return this; |
161 } |
165 } |
162 |
166 |
163 public InternalLocaleBuilder setExtension(char singleton, String value) throws LocaleSyntaxException { |
167 public InternalLocaleBuilder setExtension(char singleton, String value) throws LocaleSyntaxException { |
165 boolean isBcpPrivateuse = LanguageTag.isPrivateusePrefixChar(singleton); |
169 boolean isBcpPrivateuse = LanguageTag.isPrivateusePrefixChar(singleton); |
166 if (!isBcpPrivateuse && !LanguageTag.isExtensionSingletonChar(singleton)) { |
170 if (!isBcpPrivateuse && !LanguageTag.isExtensionSingletonChar(singleton)) { |
167 throw new LocaleSyntaxException("Ill-formed extension key: " + singleton); |
171 throw new LocaleSyntaxException("Ill-formed extension key: " + singleton); |
168 } |
172 } |
169 |
173 |
170 boolean remove = (value == null || value.length() == 0); |
174 boolean remove = LocaleUtils.isEmpty(value); |
171 CaseInsensitiveChar key = new CaseInsensitiveChar(singleton); |
175 CaseInsensitiveChar key = new CaseInsensitiveChar(singleton); |
172 |
176 |
173 if (remove) { |
177 if (remove) { |
174 if (UnicodeLocaleExtension.isSingletonChar(key.value())) { |
178 if (UnicodeLocaleExtension.isSingletonChar(key.value())) { |
175 // clear entire Unicode locale extension |
179 // clear entire Unicode locale extension |
176 if (_uattributes != null) { |
180 if (uattributes != null) { |
177 _uattributes.clear(); |
181 uattributes.clear(); |
178 } |
182 } |
179 if (_ukeywords != null) { |
183 if (ukeywords != null) { |
180 _ukeywords.clear(); |
184 ukeywords.clear(); |
181 } |
185 } |
182 } else { |
186 } else { |
183 if (_extensions != null && _extensions.containsKey(key)) { |
187 if (extensions != null && extensions.containsKey(key)) { |
184 _extensions.remove(key); |
188 extensions.remove(key); |
185 } |
189 } |
186 } |
190 } |
187 } else { |
191 } else { |
188 // validate value |
192 // validate value |
189 String val = value.replaceAll(BaseLocale.SEP, LanguageTag.SEP); |
193 String val = value.replaceAll(BaseLocale.SEP, LanguageTag.SEP); |
195 validSubtag = LanguageTag.isPrivateuseSubtag(s); |
199 validSubtag = LanguageTag.isPrivateuseSubtag(s); |
196 } else { |
200 } else { |
197 validSubtag = LanguageTag.isExtensionSubtag(s); |
201 validSubtag = LanguageTag.isExtensionSubtag(s); |
198 } |
202 } |
199 if (!validSubtag) { |
203 if (!validSubtag) { |
200 throw new LocaleSyntaxException("Ill-formed extension value: " + s, itr.currentStart()); |
204 throw new LocaleSyntaxException("Ill-formed extension value: " + s, |
|
205 itr.currentStart()); |
201 } |
206 } |
202 itr.next(); |
207 itr.next(); |
203 } |
208 } |
204 |
209 |
205 if (UnicodeLocaleExtension.isSingletonChar(key.value())) { |
210 if (UnicodeLocaleExtension.isSingletonChar(key.value())) { |
206 setUnicodeLocaleExtension(val); |
211 setUnicodeLocaleExtension(val); |
207 } else { |
212 } else { |
208 if (_extensions == null) { |
213 if (extensions == null) { |
209 _extensions = new HashMap<CaseInsensitiveChar, String>(4); |
214 extensions = new HashMap<>(4); |
210 } |
215 } |
211 _extensions.put(key, val); |
216 extensions.put(key, val); |
212 } |
217 } |
213 } |
218 } |
214 return this; |
219 return this; |
215 } |
220 } |
216 |
221 |
217 /* |
222 /* |
218 * Set extension/private subtags in a single string representation |
223 * Set extension/private subtags in a single string representation |
219 */ |
224 */ |
220 public InternalLocaleBuilder setExtensions(String subtags) throws LocaleSyntaxException { |
225 public InternalLocaleBuilder setExtensions(String subtags) throws LocaleSyntaxException { |
221 if (subtags == null || subtags.length() == 0) { |
226 if (LocaleUtils.isEmpty(subtags)) { |
222 clearExtensions(); |
227 clearExtensions(); |
223 return this; |
228 return this; |
224 } |
229 } |
225 subtags = subtags.replaceAll(BaseLocale.SEP, LanguageTag.SEP); |
230 subtags = subtags.replaceAll(BaseLocale.SEP, LanguageTag.SEP); |
226 StringTokenIterator itr = new StringTokenIterator(subtags, LanguageTag.SEP); |
231 StringTokenIterator itr = new StringTokenIterator(subtags, LanguageTag.SEP); |
300 * BCP47 extensions are already validated and well-formed, but may contain duplicates |
310 * BCP47 extensions are already validated and well-formed, but may contain duplicates |
301 */ |
311 */ |
302 private InternalLocaleBuilder setExtensions(List<String> bcpExtensions, String privateuse) { |
312 private InternalLocaleBuilder setExtensions(List<String> bcpExtensions, String privateuse) { |
303 clearExtensions(); |
313 clearExtensions(); |
304 |
314 |
305 if (bcpExtensions != null && bcpExtensions.size() > 0) { |
315 if (!LocaleUtils.isEmpty(bcpExtensions)) { |
306 HashSet<CaseInsensitiveChar> processedExntensions = new HashSet<CaseInsensitiveChar>(bcpExtensions.size()); |
316 Set<CaseInsensitiveChar> done = new HashSet<>(bcpExtensions.size()); |
307 for (String bcpExt : bcpExtensions) { |
317 for (String bcpExt : bcpExtensions) { |
308 CaseInsensitiveChar key = new CaseInsensitiveChar(bcpExt.charAt(0)); |
318 CaseInsensitiveChar key = new CaseInsensitiveChar(bcpExt); |
309 // ignore duplicates |
319 // ignore duplicates |
310 if (!processedExntensions.contains(key)) { |
320 if (!done.contains(key)) { |
311 // each extension string contains singleton, e.g. "a-abc-def" |
321 // each extension string contains singleton, e.g. "a-abc-def" |
312 if (UnicodeLocaleExtension.isSingletonChar(key.value())) { |
322 if (UnicodeLocaleExtension.isSingletonChar(key.value())) { |
313 setUnicodeLocaleExtension(bcpExt.substring(2)); |
323 setUnicodeLocaleExtension(bcpExt.substring(2)); |
314 } else { |
324 } else { |
315 if (_extensions == null) { |
325 if (extensions == null) { |
316 _extensions = new HashMap<CaseInsensitiveChar, String>(4); |
326 extensions = new HashMap<>(4); |
317 } |
327 } |
318 _extensions.put(key, bcpExt.substring(2)); |
328 extensions.put(key, bcpExt.substring(2)); |
319 } |
329 } |
320 } |
330 } |
|
331 done.add(key); |
321 } |
332 } |
322 } |
333 } |
323 if (privateuse != null && privateuse.length() > 0) { |
334 if (privateuse != null && privateuse.length() > 0) { |
324 // privateuse string contains prefix, e.g. "x-abc-def" |
335 // privateuse string contains prefix, e.g. "x-abc-def" |
325 if (_extensions == null) { |
336 if (extensions == null) { |
326 _extensions = new HashMap<CaseInsensitiveChar, String>(1); |
337 extensions = new HashMap<>(1); |
327 } |
338 } |
328 _extensions.put(new CaseInsensitiveChar(privateuse.charAt(0)), privateuse.substring(2)); |
339 extensions.put(new CaseInsensitiveChar(privateuse), privateuse.substring(2)); |
329 } |
340 } |
330 |
341 |
331 return this; |
342 return this; |
332 } |
343 } |
333 |
344 |
334 /* |
345 /* |
335 * Reset Builder's internal state with the given language tag |
346 * Reset Builder's internal state with the given language tag |
336 */ |
347 */ |
337 public InternalLocaleBuilder setLanguageTag(LanguageTag langtag) { |
348 public InternalLocaleBuilder setLanguageTag(LanguageTag langtag) { |
338 clear(); |
349 clear(); |
339 if (langtag.getExtlangs().size() > 0) { |
350 if (!langtag.getExtlangs().isEmpty()) { |
340 _language = langtag.getExtlangs().get(0); |
351 language = langtag.getExtlangs().get(0); |
341 } else { |
352 } else { |
342 String language = langtag.getLanguage(); |
353 String lang = langtag.getLanguage(); |
343 if (!language.equals(LanguageTag.UNDETERMINED)) { |
354 if (!lang.equals(LanguageTag.UNDETERMINED)) { |
344 _language = language; |
355 language = lang; |
345 } |
356 } |
346 } |
357 } |
347 _script = langtag.getScript(); |
358 script = langtag.getScript(); |
348 _region = langtag.getRegion(); |
359 region = langtag.getRegion(); |
349 |
360 |
350 List<String> bcpVariants = langtag.getVariants(); |
361 List<String> bcpVariants = langtag.getVariants(); |
351 if (bcpVariants.size() > 0) { |
362 if (!bcpVariants.isEmpty()) { |
352 StringBuilder var = new StringBuilder(bcpVariants.get(0)); |
363 StringBuilder var = new StringBuilder(bcpVariants.get(0)); |
353 for (int i = 1; i < bcpVariants.size(); i++) { |
364 int size = bcpVariants.size(); |
|
365 for (int i = 1; i < size; i++) { |
354 var.append(BaseLocale.SEP).append(bcpVariants.get(i)); |
366 var.append(BaseLocale.SEP).append(bcpVariants.get(i)); |
355 } |
367 } |
356 _variant = var.toString(); |
368 variant = var.toString(); |
357 } |
369 } |
358 |
370 |
359 setExtensions(langtag.getExtensions(), langtag.getPrivateuse()); |
371 setExtensions(langtag.getExtensions(), langtag.getPrivateuse()); |
360 |
372 |
361 return this; |
373 return this; |
362 } |
374 } |
363 |
375 |
364 public InternalLocaleBuilder setLocale(BaseLocale base, LocaleExtensions extensions) throws LocaleSyntaxException { |
376 public InternalLocaleBuilder setLocale(BaseLocale base, LocaleExtensions localeExtensions) throws LocaleSyntaxException { |
365 String language = base.getLanguage(); |
377 String language = base.getLanguage(); |
366 String script = base.getScript(); |
378 String script = base.getScript(); |
367 String region = base.getRegion(); |
379 String region = base.getRegion(); |
368 String variant = base.getVariant(); |
380 String variant = base.getVariant(); |
369 |
381 |
371 |
383 |
372 // Exception 1 - ja_JP_JP |
384 // Exception 1 - ja_JP_JP |
373 if (language.equals("ja") && region.equals("JP") && variant.equals("JP")) { |
385 if (language.equals("ja") && region.equals("JP") && variant.equals("JP")) { |
374 // When locale ja_JP_JP is created, ca-japanese is always there. |
386 // When locale ja_JP_JP is created, ca-japanese is always there. |
375 // The builder ignores the variant "JP" |
387 // The builder ignores the variant "JP" |
376 assert("japanese".equals(extensions.getUnicodeLocaleType("ca"))); |
388 assert("japanese".equals(localeExtensions.getUnicodeLocaleType("ca"))); |
377 variant = ""; |
389 variant = ""; |
378 } |
390 } |
379 // Exception 2 - th_TH_TH |
391 // Exception 2 - th_TH_TH |
380 else if (language.equals("th") && region.equals("TH") && variant.equals("TH")) { |
392 else if (language.equals("th") && region.equals("TH") && variant.equals("TH")) { |
381 // When locale th_TH_TH is created, nu-thai is always there. |
393 // When locale th_TH_TH is created, nu-thai is always there. |
382 // The builder ignores the variant "TH" |
394 // The builder ignores the variant "TH" |
383 assert("thai".equals(extensions.getUnicodeLocaleType("nu"))); |
395 assert("thai".equals(localeExtensions.getUnicodeLocaleType("nu"))); |
384 variant = ""; |
396 variant = ""; |
385 } |
397 } |
386 // Exception 3 - no_NO_NY |
398 // Exception 3 - no_NO_NY |
387 else if (language.equals("no") && region.equals("NO") && variant.equals("NY")) { |
399 else if (language.equals("no") && region.equals("NO") && variant.equals("NY")) { |
388 // no_NO_NY is a valid locale and used by Java 6 or older versions. |
400 // no_NO_NY is a valid locale and used by Java 6 or older versions. |
413 } |
425 } |
414 } |
426 } |
415 |
427 |
416 // The input locale is validated at this point. |
428 // The input locale is validated at this point. |
417 // Now, updating builder's internal fields. |
429 // Now, updating builder's internal fields. |
418 _language = language; |
430 this.language = language; |
419 _script = script; |
431 this.script = script; |
420 _region = region; |
432 this.region = region; |
421 _variant = variant; |
433 this.variant = variant; |
422 clearExtensions(); |
434 clearExtensions(); |
423 |
435 |
424 Set<Character> extKeys = (extensions == null) ? null : extensions.getKeys(); |
436 Set<Character> extKeys = (localeExtensions == null) ? null : localeExtensions.getKeys(); |
425 if (extKeys != null) { |
437 if (extKeys != null) { |
426 // map extensions back to builder's internal format |
438 // map localeExtensions back to builder's internal format |
427 for (Character key : extKeys) { |
439 for (Character key : extKeys) { |
428 Extension e = extensions.getExtension(key); |
440 Extension e = localeExtensions.getExtension(key); |
429 if (e instanceof UnicodeLocaleExtension) { |
441 if (e instanceof UnicodeLocaleExtension) { |
430 UnicodeLocaleExtension ue = (UnicodeLocaleExtension)e; |
442 UnicodeLocaleExtension ue = (UnicodeLocaleExtension)e; |
431 for (String uatr : ue.getUnicodeLocaleAttributes()) { |
443 for (String uatr : ue.getUnicodeLocaleAttributes()) { |
432 if (_uattributes == null) { |
444 if (uattributes == null) { |
433 _uattributes = new HashSet<CaseInsensitiveString>(4); |
445 uattributes = new HashSet<>(4); |
434 } |
446 } |
435 _uattributes.add(new CaseInsensitiveString(uatr)); |
447 uattributes.add(new CaseInsensitiveString(uatr)); |
436 } |
448 } |
437 for (String ukey : ue.getUnicodeLocaleKeys()) { |
449 for (String ukey : ue.getUnicodeLocaleKeys()) { |
438 if (_ukeywords == null) { |
450 if (ukeywords == null) { |
439 _ukeywords = new HashMap<CaseInsensitiveString, String>(4); |
451 ukeywords = new HashMap<>(4); |
440 } |
452 } |
441 _ukeywords.put(new CaseInsensitiveString(ukey), ue.getUnicodeLocaleType(ukey)); |
453 ukeywords.put(new CaseInsensitiveString(ukey), ue.getUnicodeLocaleType(ukey)); |
442 } |
454 } |
443 } else { |
455 } else { |
444 if (_extensions == null) { |
456 if (extensions == null) { |
445 _extensions = new HashMap<CaseInsensitiveChar, String>(4); |
457 extensions = new HashMap<>(4); |
446 } |
458 } |
447 _extensions.put(new CaseInsensitiveChar(key.charValue()), e.getValue()); |
459 extensions.put(new CaseInsensitiveChar(key), e.getValue()); |
448 } |
460 } |
449 } |
461 } |
450 } |
462 } |
451 return this; |
463 return this; |
452 } |
464 } |
453 |
465 |
454 public InternalLocaleBuilder clear() { |
466 public InternalLocaleBuilder clear() { |
455 _language = ""; |
467 language = ""; |
456 _script = ""; |
468 script = ""; |
457 _region = ""; |
469 region = ""; |
458 _variant = ""; |
470 variant = ""; |
459 clearExtensions(); |
471 clearExtensions(); |
460 return this; |
472 return this; |
461 } |
473 } |
462 |
474 |
463 public InternalLocaleBuilder clearExtensions() { |
475 public InternalLocaleBuilder clearExtensions() { |
464 if (_extensions != null) { |
476 if (extensions != null) { |
465 _extensions.clear(); |
477 extensions.clear(); |
466 } |
478 } |
467 if (_uattributes != null) { |
479 if (uattributes != null) { |
468 _uattributes.clear(); |
480 uattributes.clear(); |
469 } |
481 } |
470 if (_ukeywords != null) { |
482 if (ukeywords != null) { |
471 _ukeywords.clear(); |
483 ukeywords.clear(); |
472 } |
484 } |
473 return this; |
485 return this; |
474 } |
486 } |
475 |
487 |
476 public BaseLocale getBaseLocale() { |
488 public BaseLocale getBaseLocale() { |
477 String language = _language; |
489 String language = this.language; |
478 String script = _script; |
490 String script = this.script; |
479 String region = _region; |
491 String region = this.region; |
480 String variant = _variant; |
492 String variant = this.variant; |
481 |
493 |
482 // Special private use subtag sequence identified by "lvariant" will be |
494 // Special private use subtag sequence identified by "lvariant" will be |
483 // interpreted as Java variant. |
495 // interpreted as Java variant. |
484 if (_extensions != null) { |
496 if (extensions != null) { |
485 String privuse = _extensions.get(PRIVUSE_KEY); |
497 String privuse = extensions.get(PRIVATEUSE_KEY); |
486 if (privuse != null) { |
498 if (privuse != null) { |
487 StringTokenIterator itr = new StringTokenIterator(privuse, LanguageTag.SEP); |
499 StringTokenIterator itr = new StringTokenIterator(privuse, LanguageTag.SEP); |
488 boolean sawPrefix = false; |
500 boolean sawPrefix = false; |
489 int privVarStart = -1; |
501 int privVarStart = -1; |
490 while (!itr.isDone()) { |
502 while (!itr.isDone()) { |
491 if (sawPrefix) { |
503 if (sawPrefix) { |
492 privVarStart = itr.currentStart(); |
504 privVarStart = itr.currentStart(); |
493 break; |
505 break; |
494 } |
506 } |
495 if (AsciiUtil.caseIgnoreMatch(itr.current(), LanguageTag.PRIVUSE_VARIANT_PREFIX)) { |
507 if (LocaleUtils.caseIgnoreMatch(itr.current(), LanguageTag.PRIVUSE_VARIANT_PREFIX)) { |
496 sawPrefix = true; |
508 sawPrefix = true; |
497 } |
509 } |
498 itr.next(); |
510 itr.next(); |
499 } |
511 } |
500 if (privVarStart != -1) { |
512 if (privVarStart != -1) { |
501 StringBuilder sb = new StringBuilder(variant); |
513 StringBuilder sb = new StringBuilder(variant); |
502 if (sb.length() != 0) { |
514 if (sb.length() != 0) { |
503 sb.append(BaseLocale.SEP); |
515 sb.append(BaseLocale.SEP); |
504 } |
516 } |
505 sb.append(privuse.substring(privVarStart).replaceAll(LanguageTag.SEP, BaseLocale.SEP)); |
517 sb.append(privuse.substring(privVarStart).replaceAll(LanguageTag.SEP, |
|
518 BaseLocale.SEP)); |
506 variant = sb.toString(); |
519 variant = sb.toString(); |
507 } |
520 } |
508 } |
521 } |
509 } |
522 } |
510 |
523 |
511 return BaseLocale.getInstance(language, script, region, variant); |
524 return BaseLocale.getInstance(language, script, region, variant); |
512 } |
525 } |
513 |
526 |
514 public LocaleExtensions getLocaleExtensions() { |
527 public LocaleExtensions getLocaleExtensions() { |
515 if ((_extensions == null || _extensions.size() == 0) |
528 if (LocaleUtils.isEmpty(extensions) && LocaleUtils.isEmpty(uattributes) |
516 && (_uattributes == null || _uattributes.size() == 0) |
529 && LocaleUtils.isEmpty(ukeywords)) { |
517 && (_ukeywords == null || _ukeywords.size() == 0)) { |
530 return null; |
518 return LocaleExtensions.EMPTY_EXTENSIONS; |
531 } |
519 } |
532 |
520 |
533 LocaleExtensions lext = new LocaleExtensions(extensions, uattributes, ukeywords); |
521 return new LocaleExtensions(_extensions, _uattributes, _ukeywords); |
534 return lext.isEmpty() ? null : lext; |
522 } |
535 } |
523 |
536 |
524 /* |
537 /* |
525 * Remove special private use subtag sequence identified by "lvariant" |
538 * Remove special private use subtag sequence identified by "lvariant" |
526 * and return the rest. Only used by LocaleExtensions |
539 * and return the rest. Only used by LocaleExtensions |
574 * Duplicated attributes/keywords will be ignored. |
587 * Duplicated attributes/keywords will be ignored. |
575 * The input must be a valid extension subtags (excluding singleton). |
588 * The input must be a valid extension subtags (excluding singleton). |
576 */ |
589 */ |
577 private void setUnicodeLocaleExtension(String subtags) { |
590 private void setUnicodeLocaleExtension(String subtags) { |
578 // wipe out existing attributes/keywords |
591 // wipe out existing attributes/keywords |
579 if (_uattributes != null) { |
592 if (uattributes != null) { |
580 _uattributes.clear(); |
593 uattributes.clear(); |
581 } |
594 } |
582 if (_ukeywords != null) { |
595 if (ukeywords != null) { |
583 _ukeywords.clear(); |
596 ukeywords.clear(); |
584 } |
597 } |
585 |
598 |
586 StringTokenIterator itr = new StringTokenIterator(subtags, LanguageTag.SEP); |
599 StringTokenIterator itr = new StringTokenIterator(subtags, LanguageTag.SEP); |
587 |
600 |
588 // parse attributes |
601 // parse attributes |
589 while (!itr.isDone()) { |
602 while (!itr.isDone()) { |
590 if (!UnicodeLocaleExtension.isAttribute(itr.current())) { |
603 if (!UnicodeLocaleExtension.isAttribute(itr.current())) { |
591 break; |
604 break; |
592 } |
605 } |
593 if (_uattributes == null) { |
606 if (uattributes == null) { |
594 _uattributes = new HashSet<CaseInsensitiveString>(4); |
607 uattributes = new HashSet<>(4); |
595 } |
608 } |
596 _uattributes.add(new CaseInsensitiveString(itr.current())); |
609 uattributes.add(new CaseInsensitiveString(itr.current())); |
597 itr.next(); |
610 itr.next(); |
598 } |
611 } |
599 |
612 |
600 // parse keywords |
613 // parse keywords |
601 CaseInsensitiveString key = null; |
614 CaseInsensitiveString key = null; |
606 if (key != null) { |
619 if (key != null) { |
607 if (UnicodeLocaleExtension.isKey(itr.current())) { |
620 if (UnicodeLocaleExtension.isKey(itr.current())) { |
608 // next keyword - emit previous one |
621 // next keyword - emit previous one |
609 assert(typeStart == -1 || typeEnd != -1); |
622 assert(typeStart == -1 || typeEnd != -1); |
610 type = (typeStart == -1) ? "" : subtags.substring(typeStart, typeEnd); |
623 type = (typeStart == -1) ? "" : subtags.substring(typeStart, typeEnd); |
611 if (_ukeywords == null) { |
624 if (ukeywords == null) { |
612 _ukeywords = new HashMap<CaseInsensitiveString, String>(4); |
625 ukeywords = new HashMap<>(4); |
613 } |
626 } |
614 _ukeywords.put(key, type); |
627 ukeywords.put(key, type); |
615 |
628 |
616 // reset keyword info |
629 // reset keyword info |
617 CaseInsensitiveString tmpKey = new CaseInsensitiveString(itr.current()); |
630 CaseInsensitiveString tmpKey = new CaseInsensitiveString(itr.current()); |
618 key = _ukeywords.containsKey(tmpKey) ? null : tmpKey; |
631 key = ukeywords.containsKey(tmpKey) ? null : tmpKey; |
619 typeStart = typeEnd = -1; |
632 typeStart = typeEnd = -1; |
620 } else { |
633 } else { |
621 if (typeStart == -1) { |
634 if (typeStart == -1) { |
622 typeStart = itr.currentStart(); |
635 typeStart = itr.currentStart(); |
623 } |
636 } |
625 } |
638 } |
626 } else if (UnicodeLocaleExtension.isKey(itr.current())) { |
639 } else if (UnicodeLocaleExtension.isKey(itr.current())) { |
627 // 1. first keyword or |
640 // 1. first keyword or |
628 // 2. next keyword, but previous one was duplicate |
641 // 2. next keyword, but previous one was duplicate |
629 key = new CaseInsensitiveString(itr.current()); |
642 key = new CaseInsensitiveString(itr.current()); |
630 if (_ukeywords != null && _ukeywords.containsKey(key)) { |
643 if (ukeywords != null && ukeywords.containsKey(key)) { |
631 // duplicate |
644 // duplicate |
632 key = null; |
645 key = null; |
633 } |
646 } |
634 } |
647 } |
635 |
648 |
636 if (!itr.hasNext()) { |
649 if (!itr.hasNext()) { |
637 if (key != null) { |
650 if (key != null) { |
638 // last keyword |
651 // last keyword |
639 assert(typeStart == -1 || typeEnd != -1); |
652 assert(typeStart == -1 || typeEnd != -1); |
640 type = (typeStart == -1) ? "" : subtags.substring(typeStart, typeEnd); |
653 type = (typeStart == -1) ? "" : subtags.substring(typeStart, typeEnd); |
641 if (_ukeywords == null) { |
654 if (ukeywords == null) { |
642 _ukeywords = new HashMap<CaseInsensitiveString, String>(4); |
655 ukeywords = new HashMap<>(4); |
643 } |
656 } |
644 _ukeywords.put(key, type); |
657 ukeywords.put(key, type); |
645 } |
658 } |
646 break; |
659 break; |
647 } |
660 } |
648 |
661 |
649 itr.next(); |
662 itr.next(); |
650 } |
663 } |
651 } |
664 } |
652 |
665 |
653 static class CaseInsensitiveString { |
666 static final class CaseInsensitiveString { |
654 private String _s; |
667 private final String str, lowerStr; |
655 |
668 |
656 CaseInsensitiveString(String s) { |
669 CaseInsensitiveString(String s) { |
657 _s = s; |
670 str = s; |
|
671 lowerStr = LocaleUtils.toLowerString(s); |
658 } |
672 } |
659 |
673 |
660 public String value() { |
674 public String value() { |
661 return _s; |
675 return str; |
662 } |
676 } |
663 |
677 |
|
678 @Override |
664 public int hashCode() { |
679 public int hashCode() { |
665 return AsciiUtil.toLowerString(_s).hashCode(); |
680 return lowerStr.hashCode(); |
666 } |
681 } |
667 |
682 |
|
683 @Override |
668 public boolean equals(Object obj) { |
684 public boolean equals(Object obj) { |
669 if (this == obj) { |
685 if (this == obj) { |
670 return true; |
686 return true; |
671 } |
687 } |
672 if (!(obj instanceof CaseInsensitiveString)) { |
688 if (!(obj instanceof CaseInsensitiveString)) { |
673 return false; |
689 return false; |
674 } |
690 } |
675 return AsciiUtil.caseIgnoreMatch(_s, ((CaseInsensitiveString)obj).value()); |
691 return lowerStr.equals(((CaseInsensitiveString)obj).lowerStr); |
676 } |
692 } |
677 } |
693 } |
678 |
694 |
679 static class CaseInsensitiveChar { |
695 static final class CaseInsensitiveChar { |
680 private char _c; |
696 private final char ch, lowerCh; |
|
697 |
|
698 /** |
|
699 * Constructs a CaseInsensitiveChar with the first char of the |
|
700 * given s. |
|
701 */ |
|
702 private CaseInsensitiveChar(String s) { |
|
703 this(s.charAt(0)); |
|
704 } |
681 |
705 |
682 CaseInsensitiveChar(char c) { |
706 CaseInsensitiveChar(char c) { |
683 _c = c; |
707 ch = c; |
|
708 lowerCh = LocaleUtils.toLower(ch); |
684 } |
709 } |
685 |
710 |
686 public char value() { |
711 public char value() { |
687 return _c; |
712 return ch; |
688 } |
713 } |
689 |
714 |
|
715 @Override |
690 public int hashCode() { |
716 public int hashCode() { |
691 return AsciiUtil.toLower(_c); |
717 return lowerCh; |
692 } |
718 } |
693 |
719 |
|
720 @Override |
694 public boolean equals(Object obj) { |
721 public boolean equals(Object obj) { |
695 if (this == obj) { |
722 if (this == obj) { |
696 return true; |
723 return true; |
697 } |
724 } |
698 if (!(obj instanceof CaseInsensitiveChar)) { |
725 if (!(obj instanceof CaseInsensitiveChar)) { |
699 return false; |
726 return false; |
700 } |
727 } |
701 return _c == AsciiUtil.toLower(((CaseInsensitiveChar)obj).value()); |
728 return lowerCh == ((CaseInsensitiveChar)obj).lowerCh; |
702 } |
729 } |
703 |
|
704 } |
730 } |
705 } |
731 } |