blob: 98945e51f4c51d61b42c3d2adab09d4dc55d612e [file] [log] [blame]
/*=============================================================================#
# 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 static final String STOP= new String("STOP");
public static String toSimpleSingleLine(final String text) {
final int length= text.length();
StringBuilder escaped= null;
int idxWritten= 0;
ITERATE_CHARS : for (int idx= 0; idx < length; ) {
final char c= text.charAt(idx);
final String replacement;
switch (c) {
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
// case 0x09: HT
// case 0x0A: LF
case 0x0B:
// case 0x0C: FF
case 0x0E:
case 0x0F:
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x1B:
case 0x1C:
case 0x1E:
case 0x1F:
case 0x7F:
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
case 0x8E:
case 0x8F:
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0x98:
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
case 0x9E:
case 0x9F:
replacement= null;
break;
case '\t': // HT
replacement= " "; //$NON-NLS-1$
break;
case '\n': // LF
replacement= (idx < length - 1) ? " \u2014 " : STOP; //$NON-NLS-1$
break;
case '\f': // FF
replacement= STOP;
break;
default:
idx++;
continue ITERATE_CHARS;
}
if (replacement == STOP) {
if (escaped == null) {
return text.substring(0, idx);
}
if (idx > idxWritten) {
escaped.append(text, idxWritten, idx);
}
return escaped.toString();
}
if (escaped == null) {
escaped= new StringBuilder(length + 16);
}
if (idx > idxWritten) {
escaped.append(text, idxWritten, idx);
}
if (replacement != null) {
escaped.append(replacement);
}
idxWritten= ++idx;
continue ITERATE_CHARS;
}
if (escaped == null) {
return text;
}
if (length > idxWritten) {
escaped.append(text, idxWritten, length);
}
return escaped.toString();
}
private StringUtils() {
}
}