| /*=============================================================================# |
| # Copyright (c) 2021 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.jcommons.util; |
| |
| import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollection; |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| |
| |
| @NonNullByDefault |
| public final class StringUtils { |
| |
| |
| private static final byte[] U_DIGITS= { |
| '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', |
| 'A', 'B', 'C', 'D', 'E', 'F', |
| }; |
| |
| |
| /** |
| * Formats the Unicode code points in the standard U+XXXX notation. |
| * |
| * @param cp the code point |
| * @return a string with the formatted code point |
| */ |
| @SuppressWarnings("deprecation") |
| public static String formatCodePoint(int cp) { |
| final int nDigits= Math.max(((Integer.SIZE + 7 - Integer.numberOfLeadingZeros(cp)) / 8) * 2, 4); |
| final byte[] latin1= new byte[2 + nDigits]; |
| latin1[0]= 'U'; |
| latin1[1]= '+'; |
| int i= latin1.length; |
| do { |
| latin1[--i]= U_DIGITS[cp & 0xF]; |
| cp >>>= 4; |
| } while (i > 2); |
| return new String(latin1, 0, 0, latin1.length); |
| } |
| |
| |
| public static int firstIndexOfNonTrim(final String s, int start, final int end) { |
| if (start < 0 || end > s.length()) { |
| throw new StringIndexOutOfBoundsException(); |
| } |
| while (start < end && s.charAt(start) <= ' ') { |
| start++; |
| } |
| return start; |
| } |
| |
| public static int firstIndexOfNonTrim(final String s) { |
| int start= 0; |
| final int end= s.length(); |
| while (start < end && s.charAt(start) <= ' ') { |
| start++; |
| } |
| return start; |
| } |
| |
| public static int lastIndexOfNonTrim(final String s, final int start, int end) { |
| if (start < 0 || end > s.length()) { |
| throw new StringIndexOutOfBoundsException(); |
| } |
| while (start < end && s.charAt(end - 1) <= ' ') { |
| end--; |
| } |
| return end; |
| } |
| |
| public static int lastIndexOfNonTrim(final String s) { |
| int end= s.length(); |
| while (0 < end && s.charAt(end - 1) <= ' ') { |
| end--; |
| } |
| return end; |
| } |
| |
| public static String trim(final String s, int start, int end) { |
| if (start < 0 || end > s.length()) { |
| throw new StringIndexOutOfBoundsException(); |
| } |
| if (start == 0 && end == s.length()) { |
| return s.trim(); |
| } |
| while (start < end && s.charAt(start) <= ' ') { |
| start++; |
| } |
| while (start < end && s.charAt(end - 1) <= ' ') { |
| end--; |
| } |
| return s.substring(start, end); |
| } |
| |
| public static boolean isTrimEmpty(final String s) { |
| int start= 0; |
| final int end= s.length(); |
| while (start < end && s.charAt(start) <= ' ') { |
| start++; |
| } |
| return (start == end); |
| } |
| |
| public static boolean isTrimEmpty(final String s, int start, final int end) { |
| if (start < 0 || end > s.length()) { |
| throw new StringIndexOutOfBoundsException(); |
| } |
| while (start < end && s.charAt(start) <= ' ') { |
| start++; |
| } |
| return (start == end); |
| } |
| |
| |
| public static boolean containsAny(final String s, final ImCollection<String> searchStrings) { |
| String prevString= null; |
| for (final String searchString : searchStrings) { |
| if (prevString != null && searchString.startsWith(prevString)) { |
| continue; |
| } |
| prevString= searchString; |
| if (s.indexOf(searchString) >= 0) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public static boolean containsAny(final StringBuilder s, final ImCollection<String> searchStrings) { |
| String prevString= null; |
| for (final String searchString : searchStrings) { |
| if (prevString != null && searchString.startsWith(prevString)) { |
| continue; |
| } |
| prevString= searchString; |
| if (s.indexOf(searchString) >= 0) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| public static int firstIndexOfAny(final String s, final ImCollection<String> searchStrings, |
| final int fromIndex) { |
| if (fromIndex < 0 || fromIndex > s.length()) { |
| throw new StringIndexOutOfBoundsException(fromIndex); |
| } |
| int matchIndex= Integer.MAX_VALUE; |
| String prevString= null; |
| for (final String searchString : searchStrings) { |
| if (prevString != null && searchString.startsWith(prevString)) { |
| continue; |
| } |
| prevString= searchString; |
| final int index= s.indexOf(searchString, fromIndex); |
| if (index >= 0 && index < matchIndex) { |
| matchIndex= index; |
| } |
| } |
| return (matchIndex != Integer.MAX_VALUE) ? matchIndex : -1; |
| } |
| |
| public static int firstIndexOfAny(final String s, final ImCollection<String> searchStrings) { |
| int matchIndex= Integer.MAX_VALUE; |
| String prevString= null; |
| for (final String searchString : searchStrings) { |
| if (prevString != null && searchString.startsWith(prevString)) { |
| continue; |
| } |
| prevString= searchString; |
| final int index= s.indexOf(searchString); |
| if (index >= 0 && index < matchIndex) { |
| matchIndex= index; |
| } |
| } |
| return (matchIndex != Integer.MAX_VALUE) ? matchIndex : -1; |
| } |
| |
| public static int firstIndexOfAny(final StringBuilder s, final ImCollection<String> searchStrings, |
| final int fromIndex) { |
| if (fromIndex < 0 || fromIndex > s.length()) { |
| throw new StringIndexOutOfBoundsException(fromIndex); |
| } |
| int matchIndex= Integer.MAX_VALUE; |
| String prevString= null; |
| for (final String searchString : searchStrings) { |
| if (prevString != null && searchString.startsWith(prevString)) { |
| continue; |
| } |
| prevString= searchString; |
| final int index= s.indexOf(searchString, fromIndex); |
| if (index >= 0 && index < matchIndex) { |
| matchIndex= index; |
| } |
| } |
| return (matchIndex != Integer.MAX_VALUE) ? matchIndex : -1; |
| } |
| |
| public static int firstIndexOfAny(final StringBuilder s, final ImCollection<String> searchStrings) { |
| int matchIndex= Integer.MAX_VALUE; |
| String prevString= null; |
| for (final String searchString : searchStrings) { |
| if (prevString != null && searchString.startsWith(prevString)) { |
| continue; |
| } |
| prevString= searchString; |
| final int index= s.indexOf(searchString); |
| if (index >= 0 && index < matchIndex) { |
| matchIndex= index; |
| } |
| } |
| return (matchIndex != Integer.MAX_VALUE) ? matchIndex : -1; |
| } |
| |
| |
| public static final class Match { |
| |
| |
| private final String string; |
| |
| private final int startIndex; |
| |
| |
| public Match(final String string, final int startIndex) { |
| this.string= nonNullAssert(string); |
| this.startIndex= startIndex; |
| } |
| |
| |
| public String getString() { |
| return this.string; |
| } |
| |
| public int getStartIndex() { |
| return this.startIndex; |
| } |
| |
| public int getEndIndex() { |
| return this.startIndex + this.string.length(); |
| } |
| |
| public int getLength() { |
| return this.string.length(); |
| } |
| |
| } |
| |
| @SuppressWarnings("null") |
| public static @Nullable Match firstMatchOfAny(final String s, final ImCollection<String> searchStrings, |
| final int fromIndex) { |
| if (fromIndex < 0 || fromIndex > s.length()) { |
| throw new StringIndexOutOfBoundsException(fromIndex); |
| } |
| String matchString= null; |
| int matchIndex= Integer.MAX_VALUE; |
| String prevString= null; |
| boolean prevMatch= false; |
| for (final String searchString : searchStrings) { |
| if (prevString != null && searchString.startsWith(prevString)) { |
| if (prevMatch && s.startsWith(searchString, matchIndex)) { |
| matchString= prevString= searchString; |
| } |
| continue; |
| } |
| final int index= s.indexOf(searchString, fromIndex); |
| if (prevMatch= (index >= 0 |
| && (index < matchIndex |
| || index == matchIndex && searchString.length() > matchString.length() ))) { |
| matchString= searchString; |
| matchIndex= index; |
| } |
| prevString= searchString; |
| } |
| return (matchString != null) ? new Match(matchString, matchIndex) : null; |
| } |
| |
| @SuppressWarnings("null") |
| public static @Nullable Match firstMatchOfAny(final String s, final ImCollection<String> searchStrings) { |
| String matchString= null; |
| int matchIndex= Integer.MAX_VALUE; |
| String prevString= null; |
| boolean prevMatch= false; |
| for (final String searchString : searchStrings) { |
| if (prevString != null && searchString.startsWith(prevString)) { |
| if (prevMatch && s.startsWith(searchString, matchIndex)) { |
| matchString= searchString; |
| } |
| continue; |
| } |
| final int index= s.indexOf(searchString); |
| if (prevMatch= (index >= 0 |
| && (index < matchIndex |
| || index == matchIndex && searchString.length() > matchString.length() ))) { |
| matchString= searchString; |
| matchIndex= index; |
| } |
| prevString= searchString; |
| } |
| return (matchString != null) ? new Match(matchString, matchIndex) : null; |
| } |
| |
| @SuppressWarnings("null") |
| public static @Nullable Match firstMatchOfAny(final StringBuilder s, final ImCollection<String> searchStrings, |
| final int fromIndex) { |
| if (fromIndex < 0 || fromIndex > s.length()) { |
| throw new StringIndexOutOfBoundsException(fromIndex); |
| } |
| String matchString= null; |
| int matchIndex= Integer.MAX_VALUE; |
| String prevString= null; |
| boolean prevMatch= false; |
| for (final String searchString : searchStrings) { |
| if (prevString != null && searchString.startsWith(prevString)) { |
| if (prevMatch && matchIndex + searchString.length() <= s.length() |
| && s.substring(matchIndex, matchIndex + searchString.length()).equals(searchString) ) { |
| matchString= prevString= searchString; |
| } |
| continue; |
| } |
| final int index= s.indexOf(searchString, fromIndex); |
| if (prevMatch= (index >= 0 |
| && (index < matchIndex |
| || index == matchIndex && searchString.length() > matchString.length() ))) { |
| matchString= searchString; |
| matchIndex= index; |
| } |
| prevString= searchString; |
| } |
| return (matchString != null) ? new Match(matchString, matchIndex) : null; |
| } |
| |
| @SuppressWarnings("null") |
| public static @Nullable Match firstMatchOfAny(final StringBuilder s, final ImCollection<String> searchStrings) { |
| String matchString= null; |
| int matchIndex= Integer.MAX_VALUE; |
| String prevString= null; |
| boolean prevMatch= false; |
| for (final String searchString : searchStrings) { |
| if (prevString != null && searchString.startsWith(prevString)) { |
| if (prevMatch && matchIndex + searchString.length() <= s.length() |
| && s.substring(matchIndex, matchIndex + searchString.length()).equals(searchString) ) { |
| matchString= prevString= searchString; |
| } |
| continue; |
| } |
| final int index= s.indexOf(searchString); |
| if (prevMatch= (index >= 0 |
| && (index < matchIndex |
| || index == matchIndex && searchString.length() > matchString.length() ))) { |
| matchString= searchString; |
| matchIndex= index; |
| } |
| prevString= searchString; |
| } |
| return (matchString != null) ? new Match(matchString, matchIndex) : null; |
| } |
| |
| |
| private StringUtils() { |
| } |
| |
| } |