Java tutorial
/* * Copyright 2011 ZerothAngel <zerothangel@tyrannyofheaven.org> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.tyrannyofheaven.bukkit.util; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.conversations.Conversable; import org.bukkit.conversations.Conversation; import org.bukkit.conversations.ConversationFactory; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.util.ChatPaginator; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; /** * Convenience methods mainly for displaying info via {@link CommandSender#sendMessage(String)}. * * @author zerothangel */ public class ToHMessageUtils { // Number of lines per page private static final int LINES_PER_PAGE = ChatPaginator.CLOSED_CHAT_PAGE_HEIGHT; // Cache for colorize(). Feeling kinda iffy, but Strings are immutable... private static final ConcurrentMap<String, String> colorizeCache = new ConcurrentHashMap<>(); private ToHMessageUtils() { throw new AssertionError("Don't instantiate me!"); } /** * Convenience method for sending messages. Supports {@link String#format(String, Object...)} * formatting and multiple lines. * * @param sender the receiver of the message * @param format the message format string * @param args format arguments */ public static void sendMessage(CommandSender sender, String format, Object... args) { String message = String.format(format, args); for (String line : message.split("\n")) { sender.sendMessage(line); } } /** * Sends a plugin message to BungeeCord, signing it with zPermissions as * the channel and any information to write to it. * * @param plugin plugin to send with * @param channel sub channel to send to * @param objects objects to write */ public static void sendPluginMessage(Plugin plugin, String channel, String... objects) { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeUTF("zPermissions"); out.writeUTF(channel); try (ByteArrayOutputStream msgbytes = new ByteArrayOutputStream(); DataOutputStream msgout = new DataOutputStream(msgbytes)) { for (String s : objects) { msgout.writeUTF(s); } byte[] byteArray = msgbytes.toByteArray(); out.writeShort(byteArray.length); out.write(byteArray); for (Player player : Bukkit.getOnlinePlayers()) { player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray()); return; } } catch (IOException e) { throw new RuntimeException(e); } } // For colorize() private static enum ColorizeState { TEXT, COLOR_OPEN, COLOR_NAME, COLOR_CLOSE, COLOR_ESCAPE; } /** * Scans text for color tokens and replaces with the appropriate color * code. Might be more convenient than string concatenation? Tokens * consist of the color name enclosed in curly-braces. Braces may be * escaped by doubling. * * @param text the text to colorize * @return the colorized result */ public static String colorize(String text) { if (text == null) return null; // Works best with interned strings String cacheResult = colorizeCache.get(text); if (cacheResult != null) return cacheResult; StringBuilder out = new StringBuilder(); ColorizeState state = ColorizeState.TEXT; StringBuilder color = null; for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); if (state == ColorizeState.TEXT) { if (c == '{') { state = ColorizeState.COLOR_OPEN; } else if (c == '}') { state = ColorizeState.COLOR_CLOSE; } else if (c == '`') { state = ColorizeState.COLOR_ESCAPE; } else { out.append(c); } } else if (state == ColorizeState.COLOR_OPEN) { if (c == '{') { // Escaped bracket out.append('{'); state = ColorizeState.TEXT; } else if (Character.isUpperCase(c)) { // First character of color name color = new StringBuilder(); color.append(c); state = ColorizeState.COLOR_NAME; } else { // Invalid throw new IllegalArgumentException("Invalid color name"); } } else if (state == ColorizeState.COLOR_NAME) { if (Character.isUpperCase(c) || c == '_') { color.append(c); } else if (c == '}') { ChatColor chatColor = ChatColor.valueOf(color.toString()); out.append(chatColor); state = ColorizeState.TEXT; } else { // Invalid throw new IllegalArgumentException("Invalid color name"); } } else if (state == ColorizeState.COLOR_CLOSE) { // Optional, but for sanity's sake, to keep brackets matched if (c == '}') { out.append('}'); // Collapse to single bracket } else { out.append('}'); out.append(c); } state = ColorizeState.TEXT; } else if (state == ColorizeState.COLOR_ESCAPE) { out.append(decodeColor(c)); state = ColorizeState.TEXT; } else throw new AssertionError("Unknown ColorizeState"); } // End of string if (state == ColorizeState.COLOR_CLOSE) { out.append('}'); } else if (state != ColorizeState.TEXT) { // Was in the middle of color name throw new IllegalArgumentException("Invalid color name"); } cacheResult = out.toString(); colorizeCache.putIfAbsent(text, cacheResult); return cacheResult; } // Decode a color escape code. Same mapping as sk89q's plugins. private static String decodeColor(char c) { switch (c) { case '`': return "`"; case '0': return ChatColor.BLACK.toString(); case '1': return ChatColor.GRAY.toString(); case '2': return ChatColor.DARK_GRAY.toString(); case 'b': return ChatColor.BLUE.toString(); case 'B': return ChatColor.DARK_BLUE.toString(); case 'c': return ChatColor.AQUA.toString(); case 'C': return ChatColor.DARK_AQUA.toString(); case 'g': return ChatColor.GREEN.toString(); case 'G': return ChatColor.DARK_GREEN.toString(); case 'p': return ChatColor.LIGHT_PURPLE.toString(); case 'P': return ChatColor.DARK_PURPLE.toString(); case 'r': return ChatColor.RED.toString(); case 'R': return ChatColor.DARK_RED.toString(); case 'w': return ChatColor.WHITE.toString(); case 'y': return ChatColor.YELLOW.toString(); case 'Y': return ChatColor.GOLD.toString(); default: throw new IllegalArgumentException("Invalid color code"); } } /** * Broadcasts the message to every user with the given permission. Supports * {@link String#format(String, Object...)} formatting and multiple lines. * * @param server the server * @param permission the permission required to receive the broadcast * @param format the message format * @param args format args * @return number of users who received the message. In the case of a multi-line * message, this is the number of users who received the last line. */ public static int broadcast(Plugin plugin, String permission, String format, Object... args) { String message = String.format(format, args); int count = 0; for (String line : message.split("\n")) { count = plugin.getServer().broadcast(line, permission); } return count; } /** * Broadcasts the message to all players. Supports {@link String#format(String, Object...)} * formatting and multiple lines. * * @param server the server * @param format the message format * @param args format args * @return number of users who received the message. In the case of a multi-line * message, this is the number of users who received the last line. */ public static int broadcastMessage(Plugin plugin, String format, Object... args) { return broadcast(plugin, Server.BROADCAST_CHANNEL_USERS, format, args); } /** * Broadcasts the message to all admins. Supports {@link String#format(String, Object...)} * formatting and multiple lines. * * @param server the server * @param format the message format * @param args format args * @return number of users who received the message. In the case of a multi-line * message, this is the number of users who received the last line. */ public static int broadcastAdmin(Plugin plugin, String format, Object... args) { return broadcast(plugin, Server.BROADCAST_CHANNEL_ADMINISTRATIVE, format, args); } /** * Display a bunch of lines, automatically paginating if necessary. Only bothers * paginating if sender is a Player and the number of lines is greater than * the size of a page. * * @param plugin the plugin * @param sender the CommandSender to display the lines to * @param lines the lines to display */ public static void displayLines(Plugin plugin, CommandSender sender, List<String> lines) { if (lines.isEmpty()) return; if (sender instanceof Player) { // Word wrap long lines List<String> outputLines = new ArrayList<>(lines.size()); for (String line : lines) { String[] wrapped = ChatPaginator.wordWrap(line, ChatPaginator.GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH); for (String wrap : wrapped) outputLines.add(wrap); } lines = outputLines; if (lines.size() > LINES_PER_PAGE) { Conversation convo = new ConversationFactory(plugin) .withFirstPrompt(new PagerPrompt(lines, LINES_PER_PAGE)).withLocalEcho(false) .buildConversation((Conversable) sender); convo.begin(); return; } // Fall through... } // Don't bother with pager for (String line : lines) { sender.sendMessage(line); } } }