385 int splitIndex = splitRanges(priorityList);
386 List<LanguageRange> nonZeroRanges;
387 List<LanguageRange> zeroRanges;
388 if (splitIndex != -1) {
389 nonZeroRanges = priorityList.subList(0, splitIndex);
390 zeroRanges = priorityList.subList(splitIndex, priorityList.size());
391 } else {
392 nonZeroRanges = priorityList;
393 zeroRanges = List.of();
394 }
395
396 for (LanguageRange lr : nonZeroRanges) {
397 String range = lr.getRange();
398
399 // Special language range ("*") is ignored in lookup.
400 if (range.equals("*")) {
401 continue;
402 }
403
404 String rangeForRegex = range.replace("*", "\\p{Alnum}*");
405 while (rangeForRegex.length() > 0) {
406 for (String tag : tags) {
407 // change to lowercase for case-insensitive matching
408 String lowerCaseTag = tag.toLowerCase(Locale.ROOT);
409 if (lowerCaseTag.matches(rangeForRegex)
410 && !shouldIgnoreLookupMatch(zeroRanges, lowerCaseTag)) {
411 return tag; // preserve the case of the input tag
412 }
413 }
414
415 // Truncate from the end....
416 rangeForRegex = truncateRange(rangeForRegex);
417 }
418 }
419
420 return null;
421 }
422
423 /**
424 * The tag which is falling in the exclusion range(s) should not be
425 * considered as the matching tag. Ignores the tag matching with the
426 * non zero range(s), if the tag also matches with one of the exclusion
427 * range(s) i.e. range(s) having quality weight q=0.
428 */
429 private static boolean shouldIgnoreLookupMatch(List<LanguageRange> zeroRange,
430 String tag) {
431 for (LanguageRange lr : zeroRange) {
432 String range = lr.getRange();
433
434 // Special language range ("*") is ignored in lookup.
435 if (range.equals("*")) {
436 continue;
437 }
438
439 String rangeForRegex = range.replace("*", "\\p{Alnum}*");
440 while (rangeForRegex.length() > 0) {
441 if (tag.matches(rangeForRegex)) {
442 return true;
443 }
444 // Truncate from the end....
445 rangeForRegex = truncateRange(rangeForRegex);
446 }
447 }
448
449 return false;
450 }
451
452 /* Truncate the range from end during the lookup match */
453 private static String truncateRange(String rangeForRegex) {
454 int index = rangeForRegex.lastIndexOf('-');
455 if (index >= 0) {
456 rangeForRegex = rangeForRegex.substring(0, index);
457
458 // if range ends with an extension key, truncate it.
459 index = rangeForRegex.lastIndexOf('-');
460 if (index >= 0 && index == rangeForRegex.length() - 2) {
574 }
575
576 /**
577 * A faster alternative approach to String.replaceFirst(), if the given
578 * string is a literal String, not a regex.
579 */
580 private static String replaceFirstSubStringMatch(String range,
581 String substr, String replacement) {
582 int pos = range.indexOf(substr);
583 if (pos == -1) {
584 return range;
585 } else {
586 return range.substring(0, pos) + replacement
587 + range.substring(pos + substr.length());
588 }
589 }
590
591 private static String[] getEquivalentsForLanguage(String range) {
592 String r = range;
593
594 while (r.length() > 0) {
595 if (LocaleEquivalentMaps.singleEquivMap.containsKey(r)) {
596 String equiv = LocaleEquivalentMaps.singleEquivMap.get(r);
597 // Return immediately for performance if the first matching
598 // subtag is found.
599 return new String[]{replaceFirstSubStringMatch(range,
600 r, equiv)};
601 } else if (LocaleEquivalentMaps.multiEquivsMap.containsKey(r)) {
602 String[] equivs = LocaleEquivalentMaps.multiEquivsMap.get(r);
603 String[] result = new String[equivs.length];
604 for (int i = 0; i < equivs.length; i++) {
605 result[i] = replaceFirstSubStringMatch(range,
606 r, equivs[i]);
607 }
608 return result;
609 }
610
611 // Truncate the last subtag simply.
612 int index = r.lastIndexOf('-');
613 if (index == -1) {
614 break;
663 Map<String, List<String>> map) {
664 if (priorityList.isEmpty()) {
665 return new ArrayList<>(); // need to return a empty mutable List
666 }
667 if (map == null || map.isEmpty()) {
668 return new ArrayList<LanguageRange>(priorityList);
669 }
670
671 // Create a map, key=originalKey.toLowerCaes(), value=originalKey
672 Map<String, String> keyMap = new HashMap<>();
673 for (String key : map.keySet()) {
674 keyMap.put(key.toLowerCase(Locale.ROOT), key);
675 }
676
677 List<LanguageRange> list = new ArrayList<>();
678 for (LanguageRange lr : priorityList) {
679 String range = lr.getRange();
680 String r = range;
681 boolean hasEquivalent = false;
682
683 while (r.length() > 0) {
684 if (keyMap.containsKey(r)) {
685 hasEquivalent = true;
686 List<String> equivalents = map.get(keyMap.get(r));
687 if (equivalents != null) {
688 int len = r.length();
689 for (String equivalent : equivalents) {
690 list.add(new LanguageRange(equivalent.toLowerCase(Locale.ROOT)
691 + range.substring(len),
692 lr.getWeight()));
693 }
694 }
695 // Return immediately if the first matching subtag is found.
696 break;
697 }
698
699 // Truncate the last subtag simply.
700 int index = r.lastIndexOf('-');
701 if (index == -1) {
702 break;
703 }
|
385 int splitIndex = splitRanges(priorityList);
386 List<LanguageRange> nonZeroRanges;
387 List<LanguageRange> zeroRanges;
388 if (splitIndex != -1) {
389 nonZeroRanges = priorityList.subList(0, splitIndex);
390 zeroRanges = priorityList.subList(splitIndex, priorityList.size());
391 } else {
392 nonZeroRanges = priorityList;
393 zeroRanges = List.of();
394 }
395
396 for (LanguageRange lr : nonZeroRanges) {
397 String range = lr.getRange();
398
399 // Special language range ("*") is ignored in lookup.
400 if (range.equals("*")) {
401 continue;
402 }
403
404 String rangeForRegex = range.replace("*", "\\p{Alnum}*");
405 while (!rangeForRegex.isEmpty()) {
406 for (String tag : tags) {
407 // change to lowercase for case-insensitive matching
408 String lowerCaseTag = tag.toLowerCase(Locale.ROOT);
409 if (lowerCaseTag.matches(rangeForRegex)
410 && !shouldIgnoreLookupMatch(zeroRanges, lowerCaseTag)) {
411 return tag; // preserve the case of the input tag
412 }
413 }
414
415 // Truncate from the end....
416 rangeForRegex = truncateRange(rangeForRegex);
417 }
418 }
419
420 return null;
421 }
422
423 /**
424 * The tag which is falling in the exclusion range(s) should not be
425 * considered as the matching tag. Ignores the tag matching with the
426 * non zero range(s), if the tag also matches with one of the exclusion
427 * range(s) i.e. range(s) having quality weight q=0.
428 */
429 private static boolean shouldIgnoreLookupMatch(List<LanguageRange> zeroRange,
430 String tag) {
431 for (LanguageRange lr : zeroRange) {
432 String range = lr.getRange();
433
434 // Special language range ("*") is ignored in lookup.
435 if (range.equals("*")) {
436 continue;
437 }
438
439 String rangeForRegex = range.replace("*", "\\p{Alnum}*");
440 while (!rangeForRegex.isEmpty()) {
441 if (tag.matches(rangeForRegex)) {
442 return true;
443 }
444 // Truncate from the end....
445 rangeForRegex = truncateRange(rangeForRegex);
446 }
447 }
448
449 return false;
450 }
451
452 /* Truncate the range from end during the lookup match */
453 private static String truncateRange(String rangeForRegex) {
454 int index = rangeForRegex.lastIndexOf('-');
455 if (index >= 0) {
456 rangeForRegex = rangeForRegex.substring(0, index);
457
458 // if range ends with an extension key, truncate it.
459 index = rangeForRegex.lastIndexOf('-');
460 if (index >= 0 && index == rangeForRegex.length() - 2) {
574 }
575
576 /**
577 * A faster alternative approach to String.replaceFirst(), if the given
578 * string is a literal String, not a regex.
579 */
580 private static String replaceFirstSubStringMatch(String range,
581 String substr, String replacement) {
582 int pos = range.indexOf(substr);
583 if (pos == -1) {
584 return range;
585 } else {
586 return range.substring(0, pos) + replacement
587 + range.substring(pos + substr.length());
588 }
589 }
590
591 private static String[] getEquivalentsForLanguage(String range) {
592 String r = range;
593
594 while (!r.isEmpty()) {
595 if (LocaleEquivalentMaps.singleEquivMap.containsKey(r)) {
596 String equiv = LocaleEquivalentMaps.singleEquivMap.get(r);
597 // Return immediately for performance if the first matching
598 // subtag is found.
599 return new String[]{replaceFirstSubStringMatch(range,
600 r, equiv)};
601 } else if (LocaleEquivalentMaps.multiEquivsMap.containsKey(r)) {
602 String[] equivs = LocaleEquivalentMaps.multiEquivsMap.get(r);
603 String[] result = new String[equivs.length];
604 for (int i = 0; i < equivs.length; i++) {
605 result[i] = replaceFirstSubStringMatch(range,
606 r, equivs[i]);
607 }
608 return result;
609 }
610
611 // Truncate the last subtag simply.
612 int index = r.lastIndexOf('-');
613 if (index == -1) {
614 break;
663 Map<String, List<String>> map) {
664 if (priorityList.isEmpty()) {
665 return new ArrayList<>(); // need to return a empty mutable List
666 }
667 if (map == null || map.isEmpty()) {
668 return new ArrayList<LanguageRange>(priorityList);
669 }
670
671 // Create a map, key=originalKey.toLowerCaes(), value=originalKey
672 Map<String, String> keyMap = new HashMap<>();
673 for (String key : map.keySet()) {
674 keyMap.put(key.toLowerCase(Locale.ROOT), key);
675 }
676
677 List<LanguageRange> list = new ArrayList<>();
678 for (LanguageRange lr : priorityList) {
679 String range = lr.getRange();
680 String r = range;
681 boolean hasEquivalent = false;
682
683 while (!r.isEmpty()) {
684 if (keyMap.containsKey(r)) {
685 hasEquivalent = true;
686 List<String> equivalents = map.get(keyMap.get(r));
687 if (equivalents != null) {
688 int len = r.length();
689 for (String equivalent : equivalents) {
690 list.add(new LanguageRange(equivalent.toLowerCase(Locale.ROOT)
691 + range.substring(len),
692 lr.getWeight()));
693 }
694 }
695 // Return immediately if the first matching subtag is found.
696 break;
697 }
698
699 // Truncate the last subtag simply.
700 int index = r.lastIndexOf('-');
701 if (index == -1) {
702 break;
703 }
|