348
349 int splitIndex = splitRanges(priorityList);
350 List<LanguageRange> nonZeroRanges;
351 List<LanguageRange> zeroRanges;
352 if (splitIndex != -1) {
353 nonZeroRanges = priorityList.subList(0, splitIndex);
354 zeroRanges = priorityList.subList(splitIndex, priorityList.size());
355 } else {
356 nonZeroRanges = priorityList;
357 zeroRanges = List.of();
358 }
359
360 for (LanguageRange lr : nonZeroRanges) {
361 String range = lr.getRange();
362
363 // Special language range ("*") is ignored in lookup.
364 if (range.equals("*")) {
365 continue;
366 }
367
368 String rangeForRegex = range.replaceAll("\\x2A", "\\\\p{Alnum}*");
369 while (rangeForRegex.length() > 0) {
370 for (String tag : tags) {
371 tag = tag.toLowerCase(Locale.ROOT);
372 if (tag.matches(rangeForRegex)
373 && !shouldIgnoreLookupMatch(zeroRanges, tag)) {
374 return tag;
375 }
376 }
377
378 // Truncate from the end....
379 rangeForRegex = truncateRange(rangeForRegex);
380 }
381 }
382
383 return null;
384 }
385
386 /**
387 * The tag which is falling in the exclusion range(s) should not be
388 * considered as the matching tag. Ignores the tag matching with the
389 * non zero range(s), if the tag also matches with one of the exclusion
390 * range(s) i.e. range(s) having quality weight q=0.
391 */
392 private static boolean shouldIgnoreLookupMatch(List<LanguageRange> zeroRange,
393 String tag) {
394 for (LanguageRange lr : zeroRange) {
395 String range = lr.getRange();
396
397 // Special language range ("*") is ignored in lookup.
398 if (range.equals("*")) {
399 continue;
400 }
401
402 String rangeForRegex = range.replaceAll("\\x2A", "\\\\p{Alnum}*");
403 while (rangeForRegex.length() > 0) {
404 if (tag.matches(rangeForRegex)) {
405 return true;
406 }
407 // Truncate from the end....
408 rangeForRegex = truncateRange(rangeForRegex);
409 }
410 }
411
412 return false;
413 }
414
415 /* Truncate the range from end during the lookup match */
416 private static String truncateRange(String rangeForRegex) {
417 int index = rangeForRegex.lastIndexOf('-');
418 if (index >= 0) {
419 rangeForRegex = rangeForRegex.substring(0, index);
420
421 // if range ends with an extension key, truncate it.
422 index = rangeForRegex.lastIndexOf('-');
430
431 return rangeForRegex;
432 }
433
434 /* Returns the split index of the priority list, if it contains
435 * language range(s) with quality weight as 0 i.e. q=0, else -1
436 */
437 private static int splitRanges(List<LanguageRange> priorityList) {
438 int size = priorityList.size();
439 for (int index = 0; index < size; index++) {
440 LanguageRange range = priorityList.get(index);
441 if (range.getWeight() == 0) {
442 return index;
443 }
444 }
445
446 return -1; // no q=0 range exists
447 }
448
449 public static List<LanguageRange> parse(String ranges) {
450 ranges = ranges.replaceAll(" ", "").toLowerCase(Locale.ROOT);
451 if (ranges.startsWith("accept-language:")) {
452 ranges = ranges.substring(16); // delete unnecessary prefix
453 }
454
455 String[] langRanges = ranges.split(",");
456 List<LanguageRange> list = new ArrayList<>(langRanges.length);
457 List<String> tempList = new ArrayList<>();
458 int numOfRanges = 0;
459
460 for (String range : langRanges) {
461 int index;
462 String r;
463 double w;
464
465 if ((index = range.indexOf(";q=")) == -1) {
466 r = range;
467 w = MAX_WEIGHT;
468 } else {
469 r = range.substring(0, index);
470 index += 3;
519 numOfRanges++;
520 tempList.add(equiv);
521 }
522
523 // bb-XX -> bb-YY(, cc-YY)
524 equivalent = getEquivalentForRegionAndVariant(equiv);
525 if (equivalent != null
526 && !tempList.contains(equivalent)) {
527 list.add(index+1, new LanguageRange(equivalent, w));
528 numOfRanges++;
529 tempList.add(equivalent);
530 }
531 }
532 }
533 }
534 }
535
536 return list;
537 }
538
539 private static String[] getEquivalentsForLanguage(String range) {
540 String r = range;
541
542 while (r.length() > 0) {
543 if (LocaleEquivalentMaps.singleEquivMap.containsKey(r)) {
544 String equiv = LocaleEquivalentMaps.singleEquivMap.get(r);
545 // Return immediately for performance if the first matching
546 // subtag is found.
547 return new String[] {range.replaceFirst(r, equiv)};
548 } else if (LocaleEquivalentMaps.multiEquivsMap.containsKey(r)) {
549 String[] equivs = LocaleEquivalentMaps.multiEquivsMap.get(r);
550 for (int i = 0; i < equivs.length; i++) {
551 equivs[i] = range.replaceFirst(r, equivs[i]);
552 }
553 return equivs;
554 }
555
556 // Truncate the last subtag simply.
557 int index = r.lastIndexOf('-');
558 if (index == -1) {
559 break;
560 }
561 r = r.substring(0, index);
562 }
563
564 return null;
565 }
566
567 private static String getEquivalentForRegionAndVariant(String range) {
568 int extensionKeyIndex = getExtentionKeyIndex(range);
569
570 for (String subtag : LocaleEquivalentMaps.regionVariantEquivMap.keySet()) {
571 int index;
572 if ((index = range.indexOf(subtag)) != -1) {
573 // Check if the matching text is a valid region or variant.
574 if (extensionKeyIndex != Integer.MIN_VALUE
575 && index > extensionKeyIndex) {
576 continue;
577 }
578
579 int len = index + subtag.length();
580 if (range.length() == len || range.charAt(len) == '-') {
581 return range.replaceFirst(subtag, LocaleEquivalentMaps.regionVariantEquivMap.get(subtag));
582 }
583 }
584 }
585
586 return null;
587 }
588
589 private static int getExtentionKeyIndex(String s) {
590 char[] c = s.toCharArray();
591 int index = Integer.MIN_VALUE;
592 for (int i = 1; i < c.length; i++) {
593 if (c[i] == '-') {
594 if (i - index == 2) {
595 return index;
596 } else {
597 index = i;
598 }
599 }
600 }
601 return Integer.MIN_VALUE;
|
348
349 int splitIndex = splitRanges(priorityList);
350 List<LanguageRange> nonZeroRanges;
351 List<LanguageRange> zeroRanges;
352 if (splitIndex != -1) {
353 nonZeroRanges = priorityList.subList(0, splitIndex);
354 zeroRanges = priorityList.subList(splitIndex, priorityList.size());
355 } else {
356 nonZeroRanges = priorityList;
357 zeroRanges = List.of();
358 }
359
360 for (LanguageRange lr : nonZeroRanges) {
361 String range = lr.getRange();
362
363 // Special language range ("*") is ignored in lookup.
364 if (range.equals("*")) {
365 continue;
366 }
367
368 String rangeForRegex = range.replace("*", "\\p{Alnum}*");
369 while (rangeForRegex.length() > 0) {
370 for (String tag : tags) {
371 tag = tag.toLowerCase(Locale.ROOT);
372 if (tag.matches(rangeForRegex)
373 && !shouldIgnoreLookupMatch(zeroRanges, tag)) {
374 return tag;
375 }
376 }
377
378 // Truncate from the end....
379 rangeForRegex = truncateRange(rangeForRegex);
380 }
381 }
382
383 return null;
384 }
385
386 /**
387 * The tag which is falling in the exclusion range(s) should not be
388 * considered as the matching tag. Ignores the tag matching with the
389 * non zero range(s), if the tag also matches with one of the exclusion
390 * range(s) i.e. range(s) having quality weight q=0.
391 */
392 private static boolean shouldIgnoreLookupMatch(List<LanguageRange> zeroRange,
393 String tag) {
394 for (LanguageRange lr : zeroRange) {
395 String range = lr.getRange();
396
397 // Special language range ("*") is ignored in lookup.
398 if (range.equals("*")) {
399 continue;
400 }
401
402 String rangeForRegex = range.replace("*", "\\p{Alnum}*");
403 while (rangeForRegex.length() > 0) {
404 if (tag.matches(rangeForRegex)) {
405 return true;
406 }
407 // Truncate from the end....
408 rangeForRegex = truncateRange(rangeForRegex);
409 }
410 }
411
412 return false;
413 }
414
415 /* Truncate the range from end during the lookup match */
416 private static String truncateRange(String rangeForRegex) {
417 int index = rangeForRegex.lastIndexOf('-');
418 if (index >= 0) {
419 rangeForRegex = rangeForRegex.substring(0, index);
420
421 // if range ends with an extension key, truncate it.
422 index = rangeForRegex.lastIndexOf('-');
430
431 return rangeForRegex;
432 }
433
434 /* Returns the split index of the priority list, if it contains
435 * language range(s) with quality weight as 0 i.e. q=0, else -1
436 */
437 private static int splitRanges(List<LanguageRange> priorityList) {
438 int size = priorityList.size();
439 for (int index = 0; index < size; index++) {
440 LanguageRange range = priorityList.get(index);
441 if (range.getWeight() == 0) {
442 return index;
443 }
444 }
445
446 return -1; // no q=0 range exists
447 }
448
449 public static List<LanguageRange> parse(String ranges) {
450 ranges = ranges.replace(" ", "").toLowerCase(Locale.ROOT);
451 if (ranges.startsWith("accept-language:")) {
452 ranges = ranges.substring(16); // delete unnecessary prefix
453 }
454
455 String[] langRanges = ranges.split(",");
456 List<LanguageRange> list = new ArrayList<>(langRanges.length);
457 List<String> tempList = new ArrayList<>();
458 int numOfRanges = 0;
459
460 for (String range : langRanges) {
461 int index;
462 String r;
463 double w;
464
465 if ((index = range.indexOf(";q=")) == -1) {
466 r = range;
467 w = MAX_WEIGHT;
468 } else {
469 r = range.substring(0, index);
470 index += 3;
519 numOfRanges++;
520 tempList.add(equiv);
521 }
522
523 // bb-XX -> bb-YY(, cc-YY)
524 equivalent = getEquivalentForRegionAndVariant(equiv);
525 if (equivalent != null
526 && !tempList.contains(equivalent)) {
527 list.add(index+1, new LanguageRange(equivalent, w));
528 numOfRanges++;
529 tempList.add(equivalent);
530 }
531 }
532 }
533 }
534 }
535
536 return list;
537 }
538
539 /**
540 * A faster alternative approach to String.replaceFirst(), if the given
541 * string is a literal String, not a regex.
542 */
543 private static String replaceFirstSubStringMatch(String range,
544 String substr, String replacement) {
545 int pos = range.indexOf(substr);
546 if (pos == -1) {
547 return range;
548 } else {
549 return range.substring(0, pos) + replacement
550 + range.substring(pos + substr.length());
551 }
552 }
553
554 private static String[] getEquivalentsForLanguage(String range) {
555 String r = range;
556
557 while (r.length() > 0) {
558 if (LocaleEquivalentMaps.singleEquivMap.containsKey(r)) {
559 String equiv = LocaleEquivalentMaps.singleEquivMap.get(r);
560 // Return immediately for performance if the first matching
561 // subtag is found.
562 return new String[]{replaceFirstSubStringMatch(range,
563 r, equiv)};
564 } else if (LocaleEquivalentMaps.multiEquivsMap.containsKey(r)) {
565 String[] equivs = LocaleEquivalentMaps.multiEquivsMap.get(r);
566 String[] result = new String[equivs.length];
567 for (int i = 0; i < equivs.length; i++) {
568 result[i] = replaceFirstSubStringMatch(range,
569 r, equivs[i]);
570 }
571 return result;
572 }
573
574 // Truncate the last subtag simply.
575 int index = r.lastIndexOf('-');
576 if (index == -1) {
577 break;
578 }
579 r = r.substring(0, index);
580 }
581
582 return null;
583 }
584
585 private static String getEquivalentForRegionAndVariant(String range) {
586 int extensionKeyIndex = getExtentionKeyIndex(range);
587
588 for (String subtag : LocaleEquivalentMaps.regionVariantEquivMap.keySet()) {
589 int index;
590 if ((index = range.indexOf(subtag)) != -1) {
591 // Check if the matching text is a valid region or variant.
592 if (extensionKeyIndex != Integer.MIN_VALUE
593 && index > extensionKeyIndex) {
594 continue;
595 }
596
597 int len = index + subtag.length();
598 if (range.length() == len || range.charAt(len) == '-') {
599 return replaceFirstSubStringMatch(range, subtag,
600 LocaleEquivalentMaps.regionVariantEquivMap
601 .get(subtag));
602 }
603 }
604 }
605
606 return null;
607 }
608
609 private static int getExtentionKeyIndex(String s) {
610 char[] c = s.toCharArray();
611 int index = Integer.MIN_VALUE;
612 for (int i = 1; i < c.length; i++) {
613 if (c[i] == '-') {
614 if (i - index == 2) {
615 return index;
616 } else {
617 index = i;
618 }
619 }
620 }
621 return Integer.MIN_VALUE;
|