11d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/* 21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2010 The Guava Authors 31d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 41d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License"); 51d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * you may not use this file except in compliance with the License. 61d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * You may obtain a copy of the License at 71d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 81d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0 91d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Unless required by applicable law or agreed to in writing, software 111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS, 121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * See the License for the specific language governing permissions and 141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * limitations under the License. 151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpackage com.google.common.base; 181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static com.google.common.base.Preconditions.checkArgument; 201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static com.google.common.base.Preconditions.checkNotNull; 211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.annotations.Beta; 231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.annotations.GwtCompatible; 241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.annotations.VisibleForTesting; 251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.Formatter; 271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport javax.annotation.Nullable; 291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/** 311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Static utility methods pertaining to {@code String} or {@code CharSequence} 321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * instances. 331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @author Kevin Bourrillion 351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @since 3.0 361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert@GwtCompatible 381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpublic final class Strings { 391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private Strings() {} 401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Returns the given string if it is non-null; the empty string otherwise. 431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param string the string to test and possibly return 451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @return {@code string} itself if it is non-null; {@code ""} if it is null 461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public static String nullToEmpty(@Nullable String string) { 481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return (string == null) ? "" : string; 491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Returns the given string if it is nonempty; {@code null} otherwise. 531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param string the string to test and possibly return 551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @return {@code string} itself if it is nonempty; {@code null} if it is 561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * empty or null 571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public static @Nullable String emptyToNull(@Nullable String string) { 591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return isNullOrEmpty(string) ? null : string; 601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Returns {@code true} if the given string is null or is the empty string. 641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <p>Consider normalizing your string references with {@link #nullToEmpty}. 661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * If you do, you can use {@link String#isEmpty()} instead of this 671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * method, and you won't need special null-safe forms of methods like {@link 681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * String#toUpperCase} either. Or, if you'd like to normalize "in the other 691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * direction," converting empty strings to {@code null}, you can use {@link 701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * #emptyToNull}. 711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param string a string reference to check 731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @return {@code true} if the string is null or is the empty string 741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public static boolean isNullOrEmpty(@Nullable String string) { 761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return string == null || string.length() == 0; // string.isEmpty() in Java 6 771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Returns a string, of length at least {@code minLength}, consisting of 811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * {@code string} prepended with as many copies of {@code padChar} as are 821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * necessary to reach that length. For example, 831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <ul> 851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <li>{@code padStart("7", 3, '0')} returns {@code "007"} 861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <li>{@code padStart("2010", 3, '0')} returns {@code "2010"} 871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * </ul> 881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <p>See {@link Formatter} for a richer set of formatting capabilities. 901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param string the string which should appear at the end of the result 921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param minLength the minimum length the resulting string must have. Can be 931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * zero or negative, in which case the input string is always returned. 941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param padChar the character to insert at the beginning of the result until 951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * the minimum length is reached 961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @return the padded string 971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public static String padStart(String string, int minLength, char padChar) { 991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkNotNull(string); // eager for GWT. 1001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (string.length() >= minLength) { 1011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return string; 1021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert StringBuilder sb = new StringBuilder(minLength); 1041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert for (int i = string.length(); i < minLength; i++) { 1051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert sb.append(padChar); 1061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert sb.append(string); 1081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return sb.toString(); 1091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 1121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Returns a string, of length at least {@code minLength}, consisting of 1131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * {@code string} appended with as many copies of {@code padChar} as are 1141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * necessary to reach that length. For example, 1151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 1161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <ul> 1171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <li>{@code padEnd("4.", 5, '0')} returns {@code "4.000"} 1181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <li>{@code padEnd("2010", 3, '!')} returns {@code "2010"} 1191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * </ul> 1201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 1211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * <p>See {@link Formatter} for a richer set of formatting capabilities. 1221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 1231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param string the string which should appear at the beginning of the result 1241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param minLength the minimum length the resulting string must have. Can be 1251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * zero or negative, in which case the input string is always returned. 1261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param padChar the character to append to the end of the result until the 1271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * minimum length is reached 1281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @return the padded string 1291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 1301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public static String padEnd(String string, int minLength, char padChar) { 1311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkNotNull(string); // eager for GWT. 1321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (string.length() >= minLength) { 1331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return string; 1341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert StringBuilder sb = new StringBuilder(minLength); 1361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert sb.append(string); 1371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert for (int i = string.length(); i < minLength; i++) { 1381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert sb.append(padChar); 1391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return sb.toString(); 1411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 1441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Returns a string consisting of a specific number of concatenated copies of 1451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * an input string. For example, {@code repeat("hey", 3)} returns the string 1461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * {@code "heyheyhey"}. 1471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 1481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param string any non-null string 1491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @param count the number of times to repeat it; a nonnegative integer 1501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @return a string containing {@code string} repeated {@code count} times 1511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * (the empty string if {@code count} is zero) 1521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @throws IllegalArgumentException if {@code count} is negative 1531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 1541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public static String repeat(String string, int count) { 1551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkNotNull(string); // eager for GWT. 1561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (count <= 1) { 1581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkArgument(count >= 0, "invalid count: %s", count); 1591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return (count == 0) ? "" : string; 1601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // IF YOU MODIFY THE CODE HERE, you must update StringsRepeatBenchmark 1631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert final int len = string.length(); 1641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert final long longSize = (long) len * (long) count; 1651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert final int size = (int) longSize; 1661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (size != longSize) { 1671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw new ArrayIndexOutOfBoundsException("Required array size too large: " 1681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert + String.valueOf(longSize)); 1691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert final char[] array = new char[size]; 1721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert string.getChars(0, len, array, 0); 1731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert int n; 1741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert for (n = len; n < size - n; n <<= 1) { 1751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert System.arraycopy(array, 0, array, n, n); 1761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert System.arraycopy(array, 0, array, n, size - n); 1781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return new String(array); 1791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 1821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Returns the longest string {@code prefix} such that 1831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * {@code a.toString().startsWith(prefix) && b.toString().startsWith(prefix)}, 1841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * taking care not to split surrogate pairs. If {@code a} and {@code b} have 1851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * no common prefix, returns the empty string. 1861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 1871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @since 11.0 1881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 1891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Beta 1901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public static String commonPrefix(CharSequence a, CharSequence b) { 1911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkNotNull(a); 1921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkNotNull(b); 1931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert int maxPrefixLength = Math.min(a.length(), b.length()); 1951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert int p = 0; 1961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert while (p < maxPrefixLength && a.charAt(p) == b.charAt(p)) { 1971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert p++; 1981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (validSurrogatePairAt(a, p - 1) || validSurrogatePairAt(b, p - 1)) { 2001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert p--; 2011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return a.subSequence(0, p).toString(); 2031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 2061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Returns the longest string {@code suffix} such that 2071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * {@code a.toString().endsWith(suffix) && b.toString().endsWith(suffix)}, 2081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * taking care not to split surrogate pairs. If {@code a} and {@code b} have 2091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * no common suffix, returns the empty string. 2101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 2111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @since 11.0 2121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 2131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Beta 2141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public static String commonSuffix(CharSequence a, CharSequence b) { 2151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkNotNull(a); 2161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkNotNull(b); 2171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert int maxSuffixLength = Math.min(a.length(), b.length()); 2191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert int s = 0; 2201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert while (s < maxSuffixLength 2211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert && a.charAt(a.length() - s - 1) == b.charAt(b.length() - s - 1)) { 2221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert s++; 2231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (validSurrogatePairAt(a, a.length() - s - 1) 2251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert || validSurrogatePairAt(b, b.length() - s - 1)) { 2261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert s--; 2271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return a.subSequence(a.length() - s, a.length()).toString(); 2291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /** 2321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * True when a valid surrogate pair starts at the given {@code index} in the 2331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * given {@code string}. Out-of-range indexes return false. 2341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 2351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @VisibleForTesting 2361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert static boolean validSurrogatePairAt(CharSequence string, int index) { 2371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return index >= 0 && index <= (string.length() - 2) 2381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert && Character.isHighSurrogate(string.charAt(index)) 2391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert && Character.isLowSurrogate(string.charAt(index + 1)); 2401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert} 242