package com.bergerkiller.bukkit.common.utils;
import java.util.Locale;
import org.bukkit.DyeColor;
import org.bukkit.GrassSpecies;
import org.bukkit.Material;
import org.bukkit.TreeSpecies;
import org.bukkit.material.Leaves;
import org.bukkit.material.LongGrass;
import org.bukkit.material.MaterialData;
import org.bukkit.material.TexturedMaterial;
import org.bukkit.material.Tree;
import org.bukkit.material.Wool;
import com.bergerkiller.bukkit.common.StringReplaceBundle;
import com.bergerkiller.bukkit.common.collections.StringMap;
import com.bergerkiller.bukkit.common.collections.StringMapCaseInsensitive;
import com.bergerkiller.bukkit.common.conversion.Conversion;
import com.bergerkiller.bukkit.common.conversion.type.NumberConverter;
public class ParseUtil {
private static final StringMapCaseInsensitive<Boolean> BOOL_NAME_MAP = new StringMapCaseInsensitive<Boolean>();
private static final StringMap<Material> MAT_NAME_MAP = new StringMap<Material>();
private static final StringReplaceBundle MAT_ALIASES = new StringReplaceBundle();
static {
// Boolean representing text values
for (String trueValue : new String[] {"yes", "allow", "allowed", "true", "ye", "y", "t", "on", "enabled", "enable"}) {
BOOL_NAME_MAP.put(trueValue, Boolean.TRUE);
}
for (String falseValue : new String[] {"no", "none", "deny", "denied", "false", "n", "f", "off", "disabled", "disable"}) {
BOOL_NAME_MAP.put(falseValue, Boolean.FALSE);
}
// Material by name mapping
for (Material material : Material.values()) {
MAT_NAME_MAP.putUpper(material.toString(), material);
}
MAT_NAME_MAP.put("CROP", Material.CROPS);
MAT_NAME_MAP.put("REDSTONETORCH", Material.REDSTONE_TORCH_ON);
MAT_NAME_MAP.put("BUTTON", Material.STONE_BUTTON);
MAT_NAME_MAP.put("PISTON", Material.PISTON_BASE);
MAT_NAME_MAP.put("STICKPISTON", Material.PISTON_STICKY_BASE);
MAT_NAME_MAP.put("MOSSSTONE", Material.MOSSY_COBBLESTONE);
MAT_NAME_MAP.put("STONESTAIR", Material.COBBLESTONE_STAIRS);
MAT_NAME_MAP.put("SANDSTAIR", Material.SANDSTONE_STAIRS);
MAT_NAME_MAP.put("GOLDAPPLE", Material.GOLDEN_APPLE);
MAT_NAME_MAP.put("APPLEGOLD", Material.GOLDEN_APPLE);
MAT_NAME_MAP.put("COBBLEFENCE", Material.COBBLE_WALL);
MAT_NAME_MAP.put("STONEFENCE", Material.COBBLE_WALL);
MAT_NAME_MAP.put("COBBLEFENCE", Material.COBBLE_WALL);
MAT_NAME_MAP.put("STONEFENCE", Material.COBBLE_WALL);
MAT_NAME_MAP.put("STONEWALL", Material.COBBLE_WALL);
// Material by name aliases
MAT_ALIASES.add(" ", "_").add("DIAM_", "DIAMOND").add("LEAT_", "LEATHER").add("_", "");
MAT_ALIASES.add("SHOVEL", "SPADE").add("SLAB", "STEP").add("GOLDEN", "GOLD").add("WOODEN", "WOOD");
MAT_ALIASES.add("PRESSUREPLATE", "PLATE").add("PANTS", "LEGGINGS");
MAT_ALIASES.add("REDSTONEDUST", "REDSTONE").add("REDSTONEREPEATER", "DIODE");
MAT_ALIASES.add("SULPHER", "SULPHUR").add("SULPHOR", "SULPHUR").add("DOORBLOCK", "DOOR").add("REPEATER", "DIODE");
MAT_ALIASES.add("LIGHTER", "FLINTANDSTEEL").add("LITPUMPKIN", "JACKOLANTERN").add("STONEBRICK", "SMOOTHBRICK");
}
/**
* Attempts to filter all non-numeric values from the text specified<br><br>
* - Commas are changed to dots<br>
* - Text after a space is excluded<br>
* - Non-digit information is erased<br>
* - Prefixed text is ignored<br>
* - A single dot maximum is enforced<br>
* - Null input returns an empty String instead
*
* @param text to filter
* @return filtered text
*/
public static String filterNumeric(String text) {
if (text == null) {
return "";
}
StringBuilder rval = new StringBuilder(text.length());
boolean hasComma = false;
boolean hasDigit = false;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (Character.isDigit(c)) {
rval.append(c);
hasDigit = true;
} else if (c == ' ') {
if (hasDigit) {
break;
}
} else if ((c == ',' || c == '.') && !hasComma) {
rval.append('.');
hasComma = true;
} else if (c == '-' && rval.length() == 0) {
rval.append(c);
}
}
return rval.toString();
}
/**
* Checks if the given value is a full valid number
*
* @param text to check
* @return True if it is a number, False if it isn't
*/
public static boolean isNumeric(String text) {
if (LogicUtil.nullOrEmpty(text)) {
return false;
}
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (!Character.isDigit(c) && c != '.' && c != ',') {
return false;
}
}
return true;
}
/**
* Checks if a given value is a full valid boolean
*
* @param text to check
* @return True if it is a boolean, False if it isn't
*/
public static boolean isBool(String text) {
return BOOL_NAME_MAP.containsKey(text);
}
/**
* Parses the text specified to a boolean
*
* @param text to parse
* @return Parsed value, false when not a known yes value
*/
public static boolean parseBool(String text) {
return parseBool(text, Boolean.FALSE).booleanValue();
}
/**
* Parses the text specified to a boolean
*
* @param text to parse
* @param def value to return if the text is not a boolean expression
* @return Parsed value, or the default
*/
public static Boolean parseBool(String text, Boolean def) {
return LogicUtil.fixNull(BOOL_NAME_MAP.get(text), def);
}
/**
* Tries to parse the text specified to a float
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static float parseFloat(String text, float def) {
return parseFloat(text, Float.valueOf(def)).floatValue();
}
/**
* Tries to parse the text specified to a float
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static Float parseFloat(String text, Float def) {
return NumberConverter.toFloat.convert(text, def);
}
/**
* Tries to parse the text specified to a double
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static double parseDouble(String text, double def) {
return parseDouble(text, Double.valueOf(def)).doubleValue();
}
/**
* Tries to parse the text specified to a double
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static Double parseDouble(String text, Double def) {
return NumberConverter.toDouble.convert(text, def);
}
/**
* Tries to parse the text specified to a long
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static long parseLong(String text, long def) {
return parseLong(text, Long.valueOf(def)).longValue();
}
/**
* Tries to parse the text specified to a long
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static Long parseLong(String text, Long def) {
return NumberConverter.toLong.convert(text, def);
}
/**
* Tries to parse the text specified to an int
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static int parseInt(String text, int def) {
return parseInt(text, Integer.valueOf(def)).intValue();
}
/**
* Tries to parse the text specified to an int
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static Integer parseInt(String text, Integer def) {
return NumberConverter.toInt.convert(text, def);
}
/**
* Tries to parse the text specified to a short
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static float parseShort(String text, short def) {
return parseShort(text, Short.valueOf(def)).shortValue();
}
/**
* Tries to parse the text specified to a short
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static Short parseShort(String text, Short def) {
return NumberConverter.toShort.convert(text, def);
}
/**
* Tries to parse the text specified to a byte
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static float parseByte(String text, byte def) {
return parseByte(text, Byte.valueOf(def)).byteValue();
}
/**
* Tries to parse the text specified to a byte
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static Byte parseByte(String text, Byte def) {
return NumberConverter.toByte.convert(text, def);
}
/**
* Parses a time value from a String. Supported formats:<br>
* - Seconds only (can be a double value)<br>
* - Minutes:Seconds (int values)<br>
* - Hours:Minutes:Seconds (int values)
*
* @param timestring to parse
* @return time in milliseconds
*/
public static long parseTime(String timestring) {
long rval = 0;
if (!LogicUtil.nullOrEmpty(timestring)) {
String[] parts = timestring.split(":");
if (parts.length == 1) {
//Seconds display only
rval = (long) (ParseUtil.parseDouble(parts[0], 0.0) * 1000);
} else if (parts.length == 2) {
//Min:Sec
rval = ParseUtil.parseLong(parts[0], 0) * 60000;
rval += ParseUtil.parseLong(parts[1], 0) * 1000;
} else if (parts.length == 3) {
//Hour:Min:Sec
rval = ParseUtil.parseLong(parts[0], 0) * 3600000;
rval += ParseUtil.parseLong(parts[1], 0) * 60000;
rval += ParseUtil.parseLong(parts[2], 0) * 1000;
}
}
return rval;
}
/**
* Tries to parse the text to one of the values in the array specified
*
* @param values array to look for a value
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static <T> T parseArray(T[] values, String text, T def) {
if (LogicUtil.nullOrEmpty(text)) {
return def;
}
text = text.toUpperCase(Locale.ENGLISH).replace("_", "").replace(" ", "");
String[] names = new String[values.length];
int i;
for (i = 0; i < names.length; i++) {
names[i] = values[i].toString().toUpperCase(Locale.ENGLISH).replace("_", "");
if (names[i].equals(text)) {
return values[i];
}
}
for (i = 0; i < names.length; i++) {
if (names[i].contains(text)) {
return values[i];
}
}
for (i = 0; i < names.length; i++) {
if (text.contains(names[i])) {
return values[i];
}
}
return def;
}
/**
* Tries to parse the text to one of the values in the Enum specified<br>
* <b>The default value is used to obtain the class to look in, it can not be null!</b>
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
@SuppressWarnings("unchecked")
public static <T> T parseEnum(String text, T def) {
return parseEnum((Class<T>) def.getClass(), text, def);
}
/**
* Tries to parse the text to one of the values in the Enum specified
*
* @param enumClass to look for a value
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static <T> T parseEnum(Class<T> enumClass, String text, T def) {
if (!enumClass.isEnum()) {
throw new IllegalArgumentException("Class '" + enumClass.getSimpleName() + "' is not an Enumeration!");
}
return parseArray(enumClass.getEnumConstants(), text, def);
}
/**
* Tries to parse the text to one of the values in the TreeSpecies class
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static TreeSpecies parseTreeSpecies(String text, TreeSpecies def) {
text = text.toLowerCase(Locale.ENGLISH);
if (text.contains("oak")) {
return TreeSpecies.GENERIC;
} else if (text.contains("pine") || text.contains("spruce")) {
return TreeSpecies.REDWOOD;
} else {
return parseEnum(TreeSpecies.class, text, def);
}
}
/**
* Tries to parse the text to one of the values in the Material class
*
* @param text to parse
* @param def to return on failure
* @return Parsed or default value
*/
public static Material parseMaterial(String text, Material def) {
if (LogicUtil.nullOrEmpty(text)) {
return def;
}
// From ID
try {
return LogicUtil.fixNull(MaterialUtil.getType(Integer.parseInt(text)), def);
} catch (Exception ex) {
}
// Replace aliases and find the corresponding Material
String matName = MAT_ALIASES.replace(text.trim().toUpperCase(Locale.ENGLISH));
Material mat;
while (true) {
// First consult the name mapping (faster)
mat = MAT_NAME_MAP.get(matName);
if (mat != null) {
return mat;
}
// Parse it (slower)
mat = parseEnum(Material.class, matName, null);
if (mat != null) {
return mat;
}
// Handle a 'multiple' in the name (sadly, no ES)
if (matName.endsWith("S")) {
matName = matName.substring(0, matName.length() - 1);
} else {
return def;
}
}
}
/**
* Old version of parseMaterialData - please use the int version, as data is more than a byte
*/
@Deprecated
public static Byte parseMaterialData(String text, Material material, Byte def) {
final int data = parseMaterialData(text, material, -1);
if (data < 0 || data > 255) {
return def;
} else {
return Byte.valueOf((byte) data);
}
}
/**
* Tries to parse the text to a data value for a Material
*
* @param text to parse
* @param material to parse the text against
* @param def to return on failure (hint: use -1)
* @return Parsed or default value
*/
public static int parseMaterialData(String text, Material material, int def) {
try {
return Integer.parseInt(text);
} catch (NumberFormatException ex) {
if (material == Material.WOOD) {
TreeSpecies ts = parseTreeSpecies(text, null);
if (ts != null) {
return MaterialUtil.getRawData(ts);
}
return def;
} else {
MaterialData dat = MaterialUtil.getData(material, 0);
if (dat instanceof TexturedMaterial) {
TexturedMaterial tdat = (TexturedMaterial) dat;
Material mat = parseMaterial(text, null);
if (mat == null)
return def;
tdat.setMaterial(mat);
} else if (dat instanceof Wool) {
Wool wdat = (Wool) dat;
DyeColor color = parseEnum(DyeColor.class, text, null);
if (color == null)
return def;
wdat.setColor(color);
} else if (dat instanceof Tree) {
Tree tdat = (Tree) dat;
TreeSpecies species = parseTreeSpecies(text, null);
if (species == null)
return def;
tdat.setSpecies(species);
} else if (dat instanceof Leaves) {
Leaves tdat = (Leaves) dat;
TreeSpecies species = parseTreeSpecies(text, null);
if (species == null)
return def;
tdat.setSpecies(species);
} else if (dat instanceof LongGrass) {
LongGrass ldat = (LongGrass) dat;
GrassSpecies species = parseEnum(GrassSpecies.class, text, null);
if (species == null)
return def;
ldat.setSpecies(species);
} else {
return def;
}
return MaterialUtil.getRawData(dat);
}
}
}
/**
* Tries to convert a given Object to the type specified
*
* @param object to convert
* @param type to convert to
* @return The convered object, or null if not possible
*/
public static <T> T convert(Object object, Class<T> type) {
return convert(object, type, null);
}
/**
* Tries to convert a given Object to the type specified<br>
* <b>The default value can not be null!</b>
*
* @param object to convert
* @param def to return on failure
* @return The convered object, or the default if not possible
*/
@SuppressWarnings("unchecked")
public static <T> T convert(Object object, T def) {
return convert(object, (Class<T>) def.getClass(), def);
}
/**
* Tries to convert a given Object to the type specified
*
* @param object to convert
* @param type to convert to
* @param def to return on failure
* @return The convered object, or the default if not possible
*/
public static <T> T convert(Object object, Class<T> type, T def) {
return Conversion.convert(object, type, def);
}
}