package com.bergerkiller.bukkit.common.utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.map.MinecraftFont;
import org.bukkit.map.MapFont.CharacterSprite;
public class StringUtil {
public static final char CHAT_STYLE_CHAR = '\u00A7';
public static final int SPACE_WIDTH = getWidth(' ');
public static final String[] EMPTY_ARRAY = new String[0];
private static final char[] CHAT_CODES;
static {
ChatColor[] styles = ChatColor.values();
LinkedHashSet<Character> chars = new LinkedHashSet<Character>(styles.length * 2);
for (int i = 0; i < styles.length; i++) {
chars.add(Character.toLowerCase(styles[i].getChar()));
chars.add(Character.toUpperCase(styles[i].getChar()));
}
CHAT_CODES = new char[chars.size()];
int i = 0;
for (Character c : chars) {
CHAT_CODES[i] = c.charValue();
i++;
}
}
/**
* Converts a Block to a {@link com.bergerkiller.bukkit.common.BlockLocation} formatted text
*
* @param block to convert
* @return A string representing the Block Location
*/
public static String blockToString(Block block) {
return block.getWorld().getName() + "_" + block.getX() + "_" + block.getY() + "_" + block.getZ();
}
/**
* Converts {@link com.bergerkiller.bukkit.common.BlockLocation} formatted text to a Block.
* If the World or Block is inaccessible, null is returned.
*
* @param str The String to convert
* @return Block denoted by the String
*/
public static Block stringToBlock(String str) {
try {
String s[] = str.split("_");
// Saved data needs at least 4 elements
if (s.length < 4) {
return null;
}
// Parse xyz from last three parts
int x = Integer.parseInt(s[s.length - 3]);
int y = Integer.parseInt(s[s.length - 2]);
int z = Integer.parseInt(s[s.length - 1]);
// Parse the world name from first parts
StringBuilder worldName = new StringBuilder(12);
for (int i = 0; i < s.length - 3; i++) {
if (i != 0) {
worldName.append('_');
}
worldName.append(s[i]);
}
// World exists? If not, can't get a block there
World world = Bukkit.getServer().getWorld(worldName.toString());
if (world == null) {
return null;
}
return world.getBlockAt(x, y, z);
} catch (Exception e) {
return null;
}
}
/**
* Gets the full width of one or more Strings appended
*
* @param text to get the total width of (can be one or more parts)
* @return The width of all the text combined
*/
public static int getWidth(String... text) {
int width = 0;
for (String part : text) {
char character;
CharacterSprite charsprite;
for (int i = 0; i < part.length(); i++) {
character = part.charAt(i);
if (character == '\n')
continue;
if (character == StringUtil.CHAT_STYLE_CHAR) {
i++;
continue;
} else if (character == ' ') {
width += SPACE_WIDTH;
} else {
charsprite = MinecraftFont.Font.getChar(character);
if (charsprite != null) {
width += charsprite.getWidth();
}
}
}
}
return width;
}
/**
* Gets the Width of a certain character in Minecraft Font
*
* @param character to get the width of
* @return Character width in pixels
*/
public static int getWidth(char character) {
return MinecraftFont.Font.getChar(character).getWidth();
}
public static int firstIndexOf(String text, char... values) {
for (int i = 0; i < text.length(); i++) {
if (LogicUtil.containsChar(text.charAt(i), values)) {
return i;
}
}
return -1;
}
public static int firstIndexOf(String text, String... values) {
return firstIndexOf(text, 0, values);
}
public static int firstIndexOf(String text, int startindex, String... values) {
int i = -1;
int index;
for (String value : values) {
if ((index = text.indexOf(value, startindex)) != -1 && (i == -1 || index < i)) {
i = index;
}
}
return i;
}
/**
* Gets a String containing a given text appended n times
*
* @param text to fill inside the String
* @param n times to put in the String
* @return new String with a length of [n * text.length()]
*/
public static String getFilledString(String text, int n) {
StringBuffer outputBuffer = new StringBuffer(text.length() * n);
for (int i = 0; i < n; i++){
outputBuffer.append(text);
}
return outputBuffer.toString();
}
/**
* Gets the text before the first occurrence of a given separator in a text.
*
* @param text to use
* @param delimiter to find
* @return the text before the first occurrence of the delimiter, or an empty String if not found
*/
public static String getBefore(String text, String delimiter) {
final int index = text.indexOf(delimiter);
return index >= 0 ? text.substring(0, index) : "";
}
/**
* Gets the text after the first occurrence of a given separator in a text
*
* @param text to use
* @param delimiter to find
* @return the text after the first occurrence of the delimiter, or an empty String if not found
*/
public static String getAfter(String text, String delimiter) {
final int index = text.indexOf(delimiter);
return index >= 0 ? text.substring(index + delimiter.length()) : "";
}
/**
* Gets the text before the last occurrence of a given separator in a text
*
* @param text to use
* @param delimiter to find
* @return the text before the delimiter, or an empty String if not found
*/
public static String getLastBefore(String text, String delimiter) {
final int index = text.lastIndexOf(delimiter);
return index >= 0 ? text.substring(0, index) : "";
}
/**
* Gets the text after the last occurrence of a given separator in a text
*
* @param text to use
* @param delimiter to find
* @return the text after the last occurrence of the delimiter, or an empty String if not found
*/
public static String getLastAfter(String text, String delimiter) {
final int index = text.lastIndexOf(delimiter);
return index >= 0 ? text.substring(index + delimiter.length()) : "";
}
/**
* Replaces a part of the text with the replacement
*
* @param text to replace a part in
* @param startIndex of the part
* @param endIndex of the part
* @param replacement for this part
*/
public static String replace(String text, int startIndex, int endIndex, String replacement) {
StringBuilder builder = new StringBuilder(text);
builder.replace(startIndex, endIndex, replacement);
return builder.toString();
}
/**
* Trims away a piece of text from the end of the input text
*
* @param text to trim the end of
* @param textToTrim from the ending
* @return text trimmed at the end
*/
public static String trimEnd(String text, String... textToTrim) {
for (String trim : textToTrim) {
if (text.endsWith(trim)) {
return text.substring(0, text.length() - trim.length());
}
}
return text;
}
/**
* Trims away a piece of text from the beginning of the input text
*
* @param text to trim the start of
* @param textToTrim from the beginning
* @return text trimmed at the start
*/
public static String trimStart(String text, String... textToTrim) {
for (String trim : textToTrim) {
if (text.startsWith(trim)) {
return text.substring(trim.length());
}
}
return text;
}
/**
* Equivalent of {@link String#trim()}, but only trims away the whitespace at the beginning.
*
* @param text to trim the start of
* @return text trimmed at the start
*/
public static String trimStart(String text) {
for (int i = 0; i < text.length(); i++) {
if (text.charAt(i) != ' ') {
return text.substring(i);
}
}
return "";
}
/**
* Equivalent of {@link String#trim()}, but only trims away the whitespace at the end.
*
* @param text to trim the end of
* @return text trimmed at the end
*/
public static String trimEnd(String text) {
for (int i = text.length() - 1; i >= 0; i--) {
if (text.charAt(i) != ' ') {
return text.substring(0, i + 1);
}
}
return "";
}
/**
* Removes a single elements from an array
*
* @param input array
* @param index in the array to remove
* @return modified array
*/
public static String[] remove(String[] input, int index) {
if (index < 0 || index >= input.length) {
return input;
}
String[] rval = new String[input.length - 1];
System.arraycopy(input, 0, rval, 0, index);
System.arraycopy(input, index + 1, rval, index, input.length - index - 1);
return rval;
}
/**
* Combines all the items in a set using the 'and' and ',' separators
*
* @param items to combine
* @return Combined items text
*/
@SuppressWarnings("rawtypes")
public static String combineNames(Set items) {
return combineNames((Collection) items);
}
/**
* Combines all the items in a collection using the 'and' and ',' separators
*
* @param items to combine
* @return Combined items text
*/
@SuppressWarnings("rawtypes")
public static String combineNames(Collection items) {
// If null or empty collection, return empty String
if (items == null || items.isEmpty()) {
return "";
}
// If only one item, return just this one item
if (items.size() == 1) {
Object item = items.iterator().next();
return item == null ? "" : item.toString();
}
// Build the items into one return value
StringBuilder rval = new StringBuilder();
int i = 0;
for (Object item : items) {
if (i == items.size() - 1) {
// Last item: Combine using 'and'
rval.append(" and ");
} else if (i > 0) {
// Middle item: Combine using a comma
rval.append(", ");
}
if (item != null) {
rval.append(item);
}
i++;
}
return rval.toString();
}
/**
* Combines all the items in an array using the 'and' and ',' separators
*
* @param items to combine
* @return Combined items text
*/
public static String combineNames(String... items) {
return combineNames(Arrays.asList(items));
}
/**
* Use {@link #join(String, String...)} instead (is more properly named).
* Method is not likely to be removed, however.
*/
@Deprecated
public static String combine(String separator, String... parts) {
return join(separator, parts);
}
/**
* Use {@link #join(String, Collection)} instead (is more properly named)
* Method is not likely to be removed, however.
*/
@Deprecated
public static String combine(String separator, Collection<String> parts) {
return join(separator, parts);
}
/**
* Combines all the separate parts together with a separator in between
*
* @param separator to put in between the parts
* @param parts to combine
* @return combined parts separated using the separator
*/
public static String join(String separator, String... parts) {
return join(separator, Arrays.asList(parts));
}
/**
* Combines all the separate parts together with a separator in between
*
* @param separator to put in between the parts
* @param parts to combine
* @return combined parts separated using the separator
*/
public static String join(String separator, Collection<String> parts) {
StringBuilder builder = new StringBuilder(parts.size() * 16);
boolean first = true;
for (String line : parts) {
if (!first) {
builder.append(separator);
}
if (line != null) {
builder.append(line);
}
first = false;
}
return builder.toString();
}
/**
* Converts the arguments to turn "-surrounded parts into a single element
*/
public static String[] convertArgs(String[] args) {
ArrayList<String> tmpargs = new ArrayList<String>(args.length);
boolean isCommenting = false;
for (String arg : args) {
if (!isCommenting && (arg.startsWith("\"") || arg.startsWith("'"))) {
if (arg.endsWith("\"") && arg.length() > 1) {
tmpargs.add(arg.substring(1, arg.length() - 1));
} else {
isCommenting = true;
tmpargs.add(arg.substring(1));
}
} else if (isCommenting && (arg.endsWith("\"") || arg.endsWith("'"))) {
arg = arg.substring(0, arg.length() - 1);
arg = tmpargs.get(tmpargs.size() - 1) + " " + arg;
tmpargs.set(tmpargs.size() - 1, arg);
isCommenting = false;
} else if (isCommenting) {
arg = tmpargs.get(tmpargs.size() - 1) + " " + arg;
tmpargs.set(tmpargs.size() - 1, arg);
} else {
tmpargs.add(arg);
}
}
return tmpargs.toArray(new String[0]);
}
/**
* Checks if a given Character is a valid chat formatting code
*
* @param character to check
* @return True if it is a formatting code, False if not
*/
public static boolean isChatCode(char character) {
return LogicUtil.containsChar(character, CHAT_CODES);
}
public static int getSuccessiveCharCount(String value, char character) {
return getSuccessiveCharCount(value, character, 0, value.length() - 1);
}
public static int getSuccessiveCharCount(String value, char character, int startindex) {
return getSuccessiveCharCount(value, character, startindex, value.length() - startindex - 1);
}
public static int getSuccessiveCharCount(String value, char character, int startindex, int endindex) {
int count = 0;
for (int i = startindex; i <= endindex; i++) {
if (value.charAt(i) == character) {
count++;
} else {
break;
}
}
return count;
}
public static void replaceAll(StringBuilder builder, String from, String to) {
int index = builder.indexOf(from);
while (index != -1) {
builder.replace(index, index + from.length(), to);
// Move to the end of the replacement
index += to.length();
index = builder.indexOf(from, index);
}
}
/**
* Obtains a chat color constant from a given color code<br>
* If the code is not part of a constant, the default value is returned
*
* @param code of the chat color
* @param def to return if not found
* @return Chat Color of the code
*/
public static ChatColor getColor(char code, ChatColor def) {
for (ChatColor color : ChatColor.values()) {
if (code == color.toString().charAt(1)) {
return color;
}
}
return def;
}
/**
* Converts color codes such as &5 to the Color code representation
*
* @param line to work on
* @return converted line
*/
public static String ampToColor(String line) {
return swapColorCodes(line, '&', CHAT_STYLE_CHAR);
}
/**
* Converts color codes to the ampersand representation, such as &5
*
* @param line to work on
* @return converted line
*/
public static String colorToAmp(String line) {
return swapColorCodes(line, CHAT_STYLE_CHAR, '&');
}
/**
* Swaps the color coded character
*
* @param line to operate on
* @param fromCode to replace
* @param toCode to replace fromCode with
* @return converted String
*/
public static String swapColorCodes(String line, char fromCode, char toCode) {
StringBuilder builder = new StringBuilder(line);
for (int i = 0; i < builder.length() - 1; i++) {
// Next char is a valid color code?
if (builder.charAt(i) == fromCode && isChatCode(builder.charAt(i + 1))) {
builder.setCharAt(i, toCode);
i++;
}
}
return builder.toString();
}
}