diff --git a/src/main/java/com/artemis/the/gr8/playerstats/api/enums/Unit.java b/src/main/java/com/artemis/the/gr8/playerstats/api/enums/Unit.java index 71cef78..7e29621 100644 --- a/src/main/java/com/artemis/the/gr8/playerstats/api/enums/Unit.java +++ b/src/main/java/com/artemis/the/gr8/playerstats/api/enums/Unit.java @@ -3,6 +3,8 @@ package com.artemis.the.gr8.playerstats.api.enums; import org.bukkit.Statistic; import org.jetbrains.annotations.NotNull; +import java.util.Locale; + /** * All the units PlayerStats can display statistics in, separated * by {@link Unit.Type}. @@ -146,7 +148,7 @@ public enum Unit { match exactly (it can be "day" or "days", for example), and is case-insensitive. @param unitName an approximation of the name belonging to the desired Unit, case-insensitive */ public static @NotNull Unit fromString(@NotNull String unitName) { - return switch (unitName.toLowerCase()) { + return switch (unitName.toLowerCase(Locale.ENGLISH)) { case "cm" -> Unit.CM; case "m", "block", "blocks" -> Unit.BLOCK; case "mile", "miles" -> Unit.MILE; @@ -170,7 +172,7 @@ public enum Unit { * @return the Type of this Unit */ public static @NotNull Type getTypeFromStatistic(Statistic statistic) { - String name = statistic.toString().toLowerCase(); + String name = statistic.toString().toLowerCase(Locale.ENGLISH); if (name.contains("one_cm")) { return Type.DISTANCE; } else if (name.contains("damage")) { diff --git a/src/main/java/com/artemis/the/gr8/playerstats/commands/TabCompleter.java b/src/main/java/com/artemis/the/gr8/playerstats/commands/TabCompleter.java new file mode 100644 index 0000000..deab6a8 --- /dev/null +++ b/src/main/java/com/artemis/the/gr8/playerstats/commands/TabCompleter.java @@ -0,0 +1,116 @@ +package com.artemis.the.gr8.playerstats.commands; + +import com.artemis.the.gr8.playerstats.utils.EnumHandler; +import com.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; +import com.artemis.the.gr8.playerstats.commands.cmdutils.TabCompleteHelper; +import org.bukkit.Statistic; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +public final class TabCompleter implements org.bukkit.command.TabCompleter { + + private final EnumHandler enumHandler; + private final OfflinePlayerHandler offlinePlayerHandler; + private final TabCompleteHelper tabCompleteHelper; + + private final List commandOptions; + + public TabCompleter(EnumHandler enumHandler, OfflinePlayerHandler offlinePlayerHandler) { + this.enumHandler = enumHandler; + this.offlinePlayerHandler = offlinePlayerHandler; + tabCompleteHelper = new TabCompleteHelper(enumHandler); + + commandOptions = new ArrayList<>(); + commandOptions.add("top"); + commandOptions.add("player"); + commandOptions.add("server"); + commandOptions.add("me"); + + } + + //args[0] = statistic (length = 1) + //args[1] = commandOption (top/player/me) OR substatistic (block/item/entitytype) (length = 2) + //args[2] = executorName OR commandOption (top/player/me) (length = 3) + //args[3] = executorName (length = 4) + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + List tabSuggestions = new ArrayList<>(); + + if (args.length >= 1) { + String currentArg = args[args.length -1]; + + if (args.length == 1) { //after typing "stat", suggest a list of viable statistics + tabSuggestions = getFirstArgSuggestions(args[0]); + } + + else { //after checking if args[0] is a viable statistic, suggest substatistic OR commandOptions + String previousArg = args[args.length -2]; + + if (enumHandler.isStatistic(previousArg)) { + Statistic stat = EnumHandler.getStatEnum(previousArg); + if (stat != null) { + tabSuggestions = getTabSuggestions(getRelevantList(stat), currentArg); + } + } + + //if previous arg = "player" + else if (previousArg.equalsIgnoreCase("player")) { + + if (args.length >= 3 && enumHandler.isEntityStatistic(args[args.length-3])) { + tabSuggestions = commandOptions; //if arg before "player" was entity-stat, suggest commandOptions + } + else { //otherwise "player" is target-flag: suggest playerNames + tabSuggestions = getTabSuggestions(offlinePlayerHandler.getOfflinePlayerNames(), currentArg); + } + } + + //after a substatistic, suggest commandOptions + else if (enumHandler.isSubStatEntry(previousArg)) { + tabSuggestions = commandOptions; + } + } + } + return tabSuggestions; + } + + private List getFirstArgSuggestions(String currentArg) { + List suggestions = enumHandler.getStatNames(); + suggestions.add("examples"); + suggestions.add("help"); + return getTabSuggestions(suggestions, currentArg); + } + + private List getTabSuggestions(List completeList, String currentArg) { + return completeList.stream() + .filter(item -> item.toLowerCase(Locale.ENGLISH).contains(currentArg.toLowerCase(Locale.ENGLISH))) + .collect(Collectors.toList()); + } + + private List getRelevantList(Statistic stat) { + switch (stat.getType()) { + case BLOCK -> { + return tabCompleteHelper.getAllBlockNames(); + } + case ITEM -> { + if (stat == Statistic.BREAK_ITEM) { + return tabCompleteHelper.getItemBrokenSuggestions(); + } else { + return tabCompleteHelper.getAllItemNames(); + } + } + case ENTITY -> { + return tabCompleteHelper.getEntitySuggestions(); + } + default -> { + return commandOptions; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/artemis/the/gr8/playerstats/commands/cmdutils/TabCompleteHelper.java b/src/main/java/com/artemis/the/gr8/playerstats/commands/cmdutils/TabCompleteHelper.java new file mode 100644 index 0000000..ab4f00a --- /dev/null +++ b/src/main/java/com/artemis/the/gr8/playerstats/commands/cmdutils/TabCompleteHelper.java @@ -0,0 +1,58 @@ +package com.artemis.the.gr8.playerstats.commands.cmdutils; + +import com.artemis.the.gr8.playerstats.utils.EnumHandler; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; + +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +public final class TabCompleteHelper { + + private final EnumHandler enumHandler; + private static List itemBrokenSuggestions; + private static List entitySuggestions; + + public TabCompleteHelper(EnumHandler enumHandler) { + this.enumHandler = enumHandler; + prepareLists(); + } + + public List getAllItemNames() { + return enumHandler.getItemNames(); + } + + public List getItemBrokenSuggestions() { + return itemBrokenSuggestions; + } + + public List getAllBlockNames() { + return enumHandler.getBlockNames(); + } + + public List getEntitySuggestions() { + return entitySuggestions; + } + + + private static void prepareLists() { + //breaking an item means running its durability negative + itemBrokenSuggestions = Arrays.stream(Material.values()) + .parallel() + .filter(Material::isItem) + .filter(item -> item.getMaxDurability() != 0) + .map(Material::toString) + .map(string -> string.toLowerCase(Locale.ENGLISH)) + .collect(Collectors.toList()); + + //the only statistics dealing with entities are killed_entity and entity_killed_by + entitySuggestions = Arrays.stream(EntityType.values()) + .parallel() + .filter(EntityType::isAlive) + .map(EntityType::toString) + .map(string -> string.toLowerCase(Locale.ENGLISH)) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/com/artemis/the/gr8/playerstats/core/msg/msgutils/LanguageKeyHandler.java b/src/main/java/com/artemis/the/gr8/playerstats/core/msg/msgutils/LanguageKeyHandler.java index a96a664..2f13bc4 100644 --- a/src/main/java/com/artemis/the/gr8/playerstats/core/msg/msgutils/LanguageKeyHandler.java +++ b/src/main/java/com/artemis/the/gr8/playerstats/core/msg/msgutils/LanguageKeyHandler.java @@ -14,6 +14,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.Locale; /** * @@ -44,6 +45,7 @@ public final class LanguageKeyHandler extends FileHandler { } return instance; } + languageKeys = YamlConfiguration.loadConfiguration(languageKeyFile); } @Contract(pure = true) @@ -235,9 +237,9 @@ public final class LanguageKeyHandler extends FileHandler { */ public @Nullable String getBlockKey(Material block) { if (block == null) return null; - else if (block.toString().toLowerCase().contains("wall_banner")) { //replace wall_banner with regular banner, since there is no key for wall banners - String blockName = block.toString().toLowerCase().replace("wall_", ""); - Material newBlock = EnumHandler.getInstance().getBlockEnum(blockName); + else if (block.toString().toLowerCase(Locale.ENGLISH).contains("wall_banner")) { //replace wall_banner with regular banner, since there is no key for wall banners + String blockName = block.toString().toLowerCase(Locale.ENGLISH).replace("wall_", ""); + Material newBlock = EnumHandler.getBlockEnum(blockName); return (newBlock != null) ? "block.minecraft." + newBlock.getKey().getKey() : null; } else { @@ -260,7 +262,7 @@ public final class LanguageKeyHandler extends FileHandler { private @NotNull HashMap generateStatisticKeys() { //get the enum names for all statistics first HashMap statNames = new HashMap<>(Statistic.values().length); - Arrays.stream(Statistic.values()).forEach(statistic -> statNames.put(statistic, statistic.toString().toLowerCase())); + Arrays.stream(Statistic.values()).forEach(statistic -> statNames.put(statistic, statistic.toString().toLowerCase(Locale.ENGLISH))); //replace the ones for which the language key is different from the enum name statNames.put(Statistic.ARMOR_CLEANED, "clean_armor"); diff --git a/src/main/java/com/artemis/the/gr8/playerstats/core/utils/EnumHandler.java b/src/main/java/com/artemis/the/gr8/playerstats/core/utils/EnumHandler.java index c58eb01..44172dc 100644 --- a/src/main/java/com/artemis/the/gr8/playerstats/core/utils/EnumHandler.java +++ b/src/main/java/com/artemis/the/gr8/playerstats/core/utils/EnumHandler.java @@ -9,6 +9,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Locale; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -95,7 +96,7 @@ public final class EnumHandler { */ public @Nullable EntityType getEntityEnum(String entityName) { try { - return EntityType.valueOf(entityName.toUpperCase()); + return EntityType.valueOf(entityName.toUpperCase(Locale.ENGLISH)); } catch (IllegalArgumentException | NullPointerException e) { return null; @@ -124,7 +125,7 @@ public final class EnumHandler { */ public @Nullable Statistic getStatEnum(@NotNull String statName) { try { - return Statistic.valueOf(statName.toUpperCase()); + return Statistic.valueOf(statName.toUpperCase(Locale.ENGLISH)); } catch (IllegalArgumentException e) { return null; @@ -138,7 +139,7 @@ public final class EnumHandler { * @return true if this String is a valid Statistic */ public boolean isStatistic(@NotNull String statName) { - return statNames.contains(statName.toLowerCase()); + return statNames.contains(statName.toLowerCase(Locale.ENGLISH)); } /** @@ -162,7 +163,7 @@ public final class EnumHandler { * of Type.Untyped */ public boolean isSubStatEntry(@NotNull String statName) { - return subStatNames.contains(statName.toLowerCase()); + return subStatNames.contains(statName.toLowerCase(Locale.ENGLISH)); } /** @@ -186,20 +187,20 @@ public final class EnumHandler { private void prepareLists() { List entityNames = Arrays.stream(EntityType.values()) .map(EntityType::toString) - .map(String::toLowerCase) + .map(string -> string.toLowerCase(Locale.ENGLISH)) .filter(entityName -> !entityName.equalsIgnoreCase("unknown")) .collect(Collectors.toList()); blockNames = Arrays.stream(Material.values()) .filter(Material::isBlock) .map(Material::toString) - .map(String::toLowerCase) + .map(string -> string.toLowerCase(Locale.ENGLISH)) .collect(Collectors.toList()); itemNames = Arrays.stream(Material.values()) .filter(Material::isItem) .map(Material::toString) - .map(String::toLowerCase) + .map(string -> string.toLowerCase(Locale.ENGLISH)) .collect(Collectors.toList()); subStatNames = Stream.of(blockNames, entityNames, itemNames) @@ -209,7 +210,7 @@ public final class EnumHandler { statNames = Arrays.stream(Statistic.values()) .map(Statistic::toString) - .map(String::toLowerCase) + .map(string -> string.toLowerCase(Locale.ENGLISH)) .collect(Collectors.toList()); } } \ No newline at end of file diff --git a/src/main/java/com/artemis/the/gr8/playerstats/msg/msgutils/StringUtils.java b/src/main/java/com/artemis/the/gr8/playerstats/msg/msgutils/StringUtils.java new file mode 100644 index 0000000..79cc7cf --- /dev/null +++ b/src/main/java/com/artemis/the/gr8/playerstats/msg/msgutils/StringUtils.java @@ -0,0 +1,34 @@ +package com.artemis.the.gr8.playerstats.msg.msgutils; + +import com.artemis.the.gr8.playerstats.utils.MyLogger; + +import java.util.Locale; + +/** + * A small utility class that helps make enum constant + * names prettier for output in stat-messages. + */ +public final class StringUtils { + + private StringUtils() { + } + + /** + * Replace "_" with " " and capitalize each first letter of the input. + * + * @param input String to prettify, case-insensitive + */ + public static String prettify(String input) { + if (input == null) return null; + StringBuilder capitals = new StringBuilder(input.toLowerCase(Locale.ENGLISH)); + capitals.setCharAt(0, Character.toUpperCase(capitals.charAt(0))); + while (capitals.indexOf("_") != -1) { + MyLogger.logHighLevelMsg("Replacing underscores and capitalizing names..."); + + int index = capitals.indexOf("_"); + capitals.setCharAt(index + 1, Character.toUpperCase(capitals.charAt(index + 1))); + capitals.setCharAt(index, ' '); + } + return capitals.toString(); + } +} \ No newline at end of file