diff --git a/Essentials/src/com/earth2me/essentials/textreader/KeywordReplacer.java b/Essentials/src/com/earth2me/essentials/textreader/KeywordReplacer.java index b77b5c45e..448ceb5ba 100644 --- a/Essentials/src/com/earth2me/essentials/textreader/KeywordReplacer.java +++ b/Essentials/src/com/earth2me/essentials/textreader/KeywordReplacer.java @@ -20,8 +20,13 @@ import org.bukkit.plugin.Plugin; import static com.earth2me.essentials.I18n._; import com.earth2me.essentials.PlayerList; -import static com.earth2me.essentials.PlayerList.getMergedList; +import static com.earth2me.essentials.textreader.KeywordType.DISPLAYNAME; +import static com.earth2me.essentials.textreader.KeywordType.PLAYER; +import java.util.EnumMap; +import java.util.HashMap; import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class KeywordReplacer implements IText @@ -31,6 +36,9 @@ public class KeywordReplacer implements IText private final transient IEssentials ess; private final transient boolean extended; private transient ExecuteTimer execTimer; + private final static Pattern KEYWORD = Pattern.compile("\\{([^\\{\\}]+)\\}"); + private final static Pattern KEYWORDSPLIT = Pattern.compile("\\:"); + private final EnumMap keywordCache = new EnumMap(KeywordType.class); public KeywordReplacer(final IText input, final CommandSender sender, final IEssentials ess) { @@ -52,161 +60,269 @@ public class KeywordReplacer implements IText private void replaceKeywords(final CommandSender sender) { - String displayName, ipAddress, balance, mails, world; - String worlds, online, unique, onlineList, date, time; - String worldTime12, worldTime24, worldDate, plugins; - String userName, version, address, tps, uptime; - String coords; execTimer = new ExecuteTimer(); execTimer.start(); + User user = null; if (sender instanceof Player) { - final User user = ess.getUser(sender); + user = ess.getUser(sender); + //This is just so any displayname lookups below show the correct nickname user.setDisplayNick(); - displayName = user.getDisplayName(); - ipAddress = user.getAddress() == null || user.getAddress().getAddress() == null ? "" : user.getAddress().getAddress().toString(); - address = user.getAddress() == null ? "" : user.getAddress().toString(); - execTimer.mark("User Grab"); - balance = NumberUtil.displayCurrency(user.getMoney(), ess); - execTimer.mark("Economy"); - mails = Integer.toString(user.getMails().size()); - final Location location = user.getLocation(); - world = location == null || location.getWorld() == null ? "" : location.getWorld().getName(); - worldTime12 = DescParseTickFormat.format12(user.getWorld() == null ? 0 : user.getWorld().getTime()); - worldTime24 = DescParseTickFormat.format24(user.getWorld() == null ? 0 : user.getWorld().getTime()); - worldDate = DateFormat.getDateInstance(DateFormat.MEDIUM, ess.getI18n().getCurrentLocale()).format(DescParseTickFormat.ticksToDate(user.getWorld() == null ? 0 : user.getWorld().getFullTime())); - coords = _("coordsKeyword", location.getBlockX(), location.getBlockY(), location.getBlockZ()); } - else - { - displayName = address = ipAddress = balance = mails = world = worldTime12 = worldTime24 = worldDate = coords = ""; - } - execTimer.mark("Player variables"); - Map> playerList = PlayerList.getPlayerLists(ess, extended); - - userName = sender.getName(); - int playerHidden = 0; - for (Player p : ess.getServer().getOnlinePlayers()) - { - if (ess.getUser(p).isHidden()) - { - playerHidden++; - } - } - online = Integer.toString(ess.getServer().getOnlinePlayers().length - playerHidden); - unique = Integer.toString(ess.getUserMap().getUniqueUsers()); - execTimer.mark("Player list"); - - final StringBuilder worldsBuilder = new StringBuilder(); - for (World w : ess.getServer().getWorlds()) - { - if (worldsBuilder.length() > 0) - { - worldsBuilder.append(", "); - } - worldsBuilder.append(w.getName()); - } - worlds = worldsBuilder.toString(); - - final StringBuilder playerlistBuilder = new StringBuilder(); - for (Player p : ess.getServer().getOnlinePlayers()) - { - if (ess.getUser(p).isHidden()) - { - continue; - } - if (playerlistBuilder.length() > 0) - { - playerlistBuilder.append(", "); - } - playerlistBuilder.append(p.getDisplayName()); - } - onlineList = playerlistBuilder.toString(); - - final StringBuilder pluginlistBuilder = new StringBuilder(); - for (Plugin p : ess.getServer().getPluginManager().getPlugins()) - { - if (pluginlistBuilder.length() > 0) - { - pluginlistBuilder.append(", "); - } - pluginlistBuilder.append(p.getDescription().getName()); - } - plugins = pluginlistBuilder.toString(); - - execTimer.mark("List builders"); - - date = DateFormat.getDateInstance(DateFormat.MEDIUM, ess.getI18n().getCurrentLocale()).format(new Date()); - time = DateFormat.getTimeInstance(DateFormat.MEDIUM, ess.getI18n().getCurrentLocale()).format(new Date()); - - version = ess.getServer().getVersion(); - - tps = Double.toString(ess.getTimer().getAverageTPS()); - uptime = DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime()); - - execTimer.mark("Server Dates"); + execTimer.mark("User Grab"); for (int i = 0; i < input.getLines().size(); i++) { String line = input.getLines().get(i); + final Matcher matcher = KEYWORD.matcher(line); - line = line.replace("{PLAYER}", displayName); - line = line.replace("{DISPLAYNAME}", displayName); - line = line.replace("{USERNAME}", userName); - line = line.replace("{BALANCE}", balance); - line = line.replace("{MAILS}", mails); - line = line.replace("{WORLD}", world); - line = line.replace("{ONLINE}", online); - line = line.replace("{UNIQUE}", unique); - line = line.replace("{WORLDS}", worlds); - line = line.replace("{PLAYERLIST}", onlineList); - line = line.replace("{TIME}", time); - line = line.replace("{DATE}", date); - line = line.replace("{WORLDTIME12}", worldTime12); - line = line.replace("{WORLDTIME24}", worldTime24); - line = line.replace("{WORLDDATE}", worldDate); - line = line.replace("{COORDS}", coords); - line = line.replace("{TPS}", tps); - line = line.replace("{UPTIME}", uptime); - - if (extended) + while (matcher.find()) { - line = line.replace("{IP}", ipAddress); - line = line.replace("{ADDRESS}", address); - line = line.replace("{PLUGINS}", plugins); - line = line.replace("{VERSION}", version); - - for (String groupName : playerList.keySet()) - { - final List groupUsers = playerList.get(groupName); - if (groupUsers != null && !groupUsers.isEmpty()) - { - line = line.replaceAll("\\{PLAYERLIST\\:" + groupName.toUpperCase() + "(?:\\:([^\\{\\}]*))?\\}", - PlayerList.listUsers(ess, groupUsers, " ")); - } - } - - boolean doReplace = true; - while (doReplace) - { - final String newLine = line.replaceAll("\\{PLAYERLIST\\:\\w*(?:\\:([^\\{\\}]*))?\\}", "$1"); - if (newLine.equals(line)) - { - doReplace = false; - } - line = newLine; - } + final String fullMatch = matcher.group(0); + final String keywordMatch = matcher.group(1); + final String[] matchTokens = KEYWORDSPLIT.split(keywordMatch); + line = replaceLine(line, fullMatch, matchTokens, user); } - replaced.add(line); } - execTimer.mark("String replace"); + + execTimer.mark("Text Replace"); final String timeroutput = execTimer.end(); if (ess.getSettings().isDebug()) { ess.getLogger().log(Level.INFO, "Keyword Replacer " + timeroutput); } + } + private String replaceLine(String line, final String fullMatch, final String[] matchTokens, final User user) + { + final String keyword = matchTokens[0]; + try + { + String replacer = null; + KeywordType validKeyword = KeywordType.valueOf(keyword); + if (validKeyword.getType().equals(KeywordCachable.CACHEABLE) && keywordCache.containsKey(validKeyword)) + { + replacer = keywordCache.get(validKeyword).toString(); + } + else if (validKeyword.getType().equals(KeywordCachable.SUBVALUE)) + { + String subKeyword = ""; + if (matchTokens.length > 1) + { + subKeyword = matchTokens[1]; + } + + if (keywordCache.containsKey(validKeyword)) + { + Map values = (Map)keywordCache.get(validKeyword); + if (values.containsKey(subKeyword)) + { + replacer = values.get(subKeyword); + } + } + } + + if (replacer == null) + { + replacer = ""; + switch (validKeyword) + { + case PLAYER: + case DISPLAYNAME: + if (user != null) + { + replacer = user.getDisplayName(); + } + break; + case USERNAME: + if (user != null) + { + replacer = user.getName(); + } + break; + case BALANCE: + if (user != null) + { + replacer = NumberUtil.displayCurrency(user.getMoney(), ess); + } + break; + case MAILS: + if (user != null) + { + replacer = Integer.toString(user.getMails().size()); + } + break; + case WORLD: + if (user != null) + { + final Location location = user.getLocation(); + replacer = location == null || location.getWorld() == null ? "" : location.getWorld().getName(); + } + break; + case ONLINE: + int playerHidden = 0; + for (Player p : ess.getServer().getOnlinePlayers()) + { + if (ess.getUser(p).isHidden()) + { + playerHidden++; + } + } + replacer = Integer.toString(ess.getServer().getOnlinePlayers().length - playerHidden); + break; + case UNIQUE: + replacer = Integer.toString(ess.getUserMap().getUniqueUsers()); + break; + case WORLDS: + final StringBuilder worldsBuilder = new StringBuilder(); + for (World w : ess.getServer().getWorlds()) + { + if (worldsBuilder.length() > 0) + { + worldsBuilder.append(", "); + } + worldsBuilder.append(w.getName()); + } + replacer = worldsBuilder.toString(); + break; + case PLAYERLIST: + final Map outputList; + if (keywordCache.containsKey(validKeyword)) + { + outputList = (Map)keywordCache.get(validKeyword); + } + else + { + //First lets build the per group playerlist + final Map> playerList = PlayerList.getPlayerLists(ess, extended); + outputList = new HashMap(); + for (String groupName : playerList.keySet()) + { + final List groupUsers = playerList.get(groupName); + if (groupUsers != null && !groupUsers.isEmpty()) + { + outputList.put(groupName, PlayerList.listUsers(ess, groupUsers, " ")); + } + } + + //Now lets build the all user playerlist + final StringBuilder playerlistBuilder = new StringBuilder(); + for (Player p : ess.getServer().getOnlinePlayers()) + { + if (ess.getUser(p).isHidden()) + { + continue; + } + if (playerlistBuilder.length() > 0) + { + playerlistBuilder.append(", "); + } + playerlistBuilder.append(p.getDisplayName()); + } + outputList.put("", playerlistBuilder.toString()); + keywordCache.put(validKeyword, outputList); + } + + //Now thats all done, output the one we want and cache the rest. + if (matchTokens.length == 1) + { + replacer = outputList.get(""); + } + else if (outputList.containsKey(matchTokens[1])) + { + replacer = outputList.get(matchTokens[1]); + } + else if (matchTokens.length > 2) + { + replacer = matchTokens[2]; + } + + keywordCache.put(validKeyword, outputList); + break; + case TIME: + replacer = DateFormat.getTimeInstance(DateFormat.MEDIUM, ess.getI18n().getCurrentLocale()).format(new Date()); + break; + case DATE: + replacer = DateFormat.getDateInstance(DateFormat.MEDIUM, ess.getI18n().getCurrentLocale()).format(new Date()); + break; + case WORLDTIME12: + if (user != null) + { + replacer = DescParseTickFormat.format12(user.getWorld() == null ? 0 : user.getWorld().getTime()); + } + break; + case WORLDTIME24: + if (user != null) + { + replacer = DescParseTickFormat.format24(user.getWorld() == null ? 0 : user.getWorld().getTime()); + } + break; + case WORLDDATE: + if (user != null) + { + replacer = DateFormat.getDateInstance(DateFormat.MEDIUM, ess.getI18n().getCurrentLocale()).format(DescParseTickFormat.ticksToDate(user.getWorld() == null ? 0 : user.getWorld().getFullTime())); + } + break; + case COORDS: + if (user != null) + { + final Location location = user.getLocation(); + replacer = _("coordsKeyword", location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + break; + case TPS: + replacer = NumberUtil.formatDouble(ess.getTimer().getAverageTPS()); + break; + case UPTIME: + replacer = DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime()); + break; + case IP: + if (user != null) + { + replacer = user.getAddress() == null || user.getAddress().getAddress() == null ? "" : user.getAddress().getAddress().toString(); + } + break; + case ADDRESS: + if (user != null) + { + replacer = user.getAddress() == null ? "" : user.getAddress().toString(); + } + break; + case PLUGINS: + final StringBuilder pluginlistBuilder = new StringBuilder(); + for (Plugin p : ess.getServer().getPluginManager().getPlugins()) + { + if (pluginlistBuilder.length() > 0) + { + pluginlistBuilder.append(", "); + } + pluginlistBuilder.append(p.getDescription().getName()); + } + replacer = pluginlistBuilder.toString(); + break; + case VERSION: + replacer = ess.getServer().getVersion(); + break; + default: + replacer = "N/A"; + break; + } + + //If this is just a regular keyword, lets throw it into the cache + if (validKeyword.getType().equals(KeywordCachable.CACHEABLE)) + { + keywordCache.put(validKeyword, replacer); + } + } + + line = line.replace(fullMatch, replacer); + } + catch (IllegalArgumentException ex) + { + } + + return line; } @Override @@ -227,3 +343,48 @@ public class KeywordReplacer implements IText return input.getBookmarks(); } } + +//When adding a keyword here, you also need to add the implementation above +enum KeywordType +{ + PLAYER(KeywordCachable.CACHEABLE), + DISPLAYNAME(KeywordCachable.CACHEABLE), + USERNAME(KeywordCachable.NOTCACHEABLE), + BALANCE(KeywordCachable.CACHEABLE), + MAILS(KeywordCachable.CACHEABLE), + WORLD(KeywordCachable.CACHEABLE), + ONLINE(KeywordCachable.CACHEABLE), + UNIQUE(KeywordCachable.CACHEABLE), + WORLDS(KeywordCachable.CACHEABLE), + PLAYERLIST(KeywordCachable.SUBVALUE), + TIME(KeywordCachable.CACHEABLE), + DATE(KeywordCachable.CACHEABLE), + WORLDTIME12(KeywordCachable.CACHEABLE), + WORLDTIME24(KeywordCachable.CACHEABLE), + WORLDDATE(KeywordCachable.CACHEABLE), + COORDS(KeywordCachable.CACHEABLE), + TPS(KeywordCachable.CACHEABLE), + UPTIME(KeywordCachable.CACHEABLE), + IP(KeywordCachable.CACHEABLE), + ADDRESS(KeywordCachable.CACHEABLE), + PLUGINS(KeywordCachable.CACHEABLE), + VERSION(KeywordCachable.CACHEABLE); + private final KeywordCachable type; + + KeywordType(KeywordCachable type) + { + this.type = type; + } + + public KeywordCachable getType() + { + return type; + } +} + +enum KeywordCachable +{ + CACHEABLE, // This keyword can be cached as a string + SUBVALUE, // This keyword can be cached as a map + NOTCACHEABLE; // This keyword should never be cached +} \ No newline at end of file