001    package org.bukkit;
002    
003    import java.util.Map;
004    import java.util.regex.Pattern;
005    
006    import org.apache.commons.lang.Validate;
007    
008    import com.google.common.collect.Maps;
009    
010    /**
011     * All supported color values for chat
012     */
013    public enum ChatColor {
014        /**
015         * Represents black
016         */
017        BLACK('0', 0x00),
018        /**
019         * Represents dark blue
020         */
021        DARK_BLUE('1', 0x1),
022        /**
023         * Represents dark green
024         */
025        DARK_GREEN('2', 0x2),
026        /**
027         * Represents dark blue (aqua)
028         */
029        DARK_AQUA('3', 0x3),
030        /**
031         * Represents dark red
032         */
033        DARK_RED('4', 0x4),
034        /**
035         * Represents dark purple
036         */
037        DARK_PURPLE('5', 0x5),
038        /**
039         * Represents gold
040         */
041        GOLD('6', 0x6),
042        /**
043         * Represents gray
044         */
045        GRAY('7', 0x7),
046        /**
047         * Represents dark gray
048         */
049        DARK_GRAY('8', 0x8),
050        /**
051         * Represents blue
052         */
053        BLUE('9', 0x9),
054        /**
055         * Represents green
056         */
057        GREEN('a', 0xA),
058        /**
059         * Represents aqua
060         */
061        AQUA('b', 0xB),
062        /**
063         * Represents red
064         */
065        RED('c', 0xC),
066        /**
067         * Represents light purple
068         */
069        LIGHT_PURPLE('d', 0xD),
070        /**
071         * Represents yellow
072         */
073        YELLOW('e', 0xE),
074        /**
075         * Represents white
076         */
077        WHITE('f', 0xF),
078        /**
079         * Represents magical characters that change around randomly
080         */
081        MAGIC('k', 0x10, true),
082        /**
083         * Makes the text bold.
084         */
085        BOLD('l', 0x11, true),
086        /**
087         * Makes a line appear through the text.
088         */
089        STRIKETHROUGH('m', 0x12, true),
090        /**
091         * Makes the text appear underlined.
092         */
093        UNDERLINE('n', 0x13, true),
094        /**
095         * Makes the text italic.
096         */
097        ITALIC('o', 0x14, true),
098        /**
099         * Resets all previous chat colors or formats.
100         */
101        RESET('r', 0x15);
102    
103        /**
104         * The special character which prefixes all chat colour codes. Use this if
105         * you need to dynamically convert colour codes from your custom format.
106         */
107        public static final char COLOR_CHAR = '\u00A7';
108        private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + String.valueOf(COLOR_CHAR) + "[0-9A-FK-OR]");
109    
110        private final int intCode;
111        private final char code;
112        private final boolean isFormat;
113        private final String toString;
114        private final static Map<Integer, ChatColor> BY_ID = Maps.newHashMap();
115        private final static Map<Character, ChatColor> BY_CHAR = Maps.newHashMap();
116    
117        private ChatColor(char code, int intCode) {
118            this(code, intCode, false);
119        }
120    
121        private ChatColor(char code, int intCode, boolean isFormat) {
122            this.code = code;
123            this.intCode = intCode;
124            this.isFormat = isFormat;
125            this.toString = new String(new char[] {COLOR_CHAR, code});
126        }
127    
128        /**
129         * Gets the char value associated with this color
130         *
131         * @return A char value of this color code
132         */
133        public char getChar() {
134            return code;
135        }
136    
137        @Override
138        public String toString() {
139            return toString;
140        }
141    
142        /**
143         * Checks if this code is a format code as opposed to a color code.
144         */
145        public boolean isFormat() {
146            return isFormat;
147        }
148    
149        /**
150         * Checks if this code is a color code as opposed to a format code.
151         */
152        public boolean isColor() {
153            return !isFormat && this != RESET;
154        }
155    
156        /**
157         * Gets the color represented by the specified color code
158         *
159         * @param code Code to check
160         * @return Associative {@link org.bukkit.ChatColor} with the given code,
161         *     or null if it doesn't exist
162         */
163        public static ChatColor getByChar(char code) {
164            return BY_CHAR.get(code);
165        }
166    
167        /**
168         * Gets the color represented by the specified color code
169         *
170         * @param code Code to check
171         * @return Associative {@link org.bukkit.ChatColor} with the given code,
172         *     or null if it doesn't exist
173         */
174        public static ChatColor getByChar(String code) {
175            Validate.notNull(code, "Code cannot be null");
176            Validate.isTrue(code.length() > 0, "Code must have at least one char");
177    
178            return BY_CHAR.get(code.charAt(0));
179        }
180    
181        /**
182         * Strips the given message of all color codes
183         *
184         * @param input String to strip of color
185         * @return A copy of the input string, without any coloring
186         */
187        public static String stripColor(final String input) {
188            if (input == null) {
189                return null;
190            }
191    
192            return STRIP_COLOR_PATTERN.matcher(input).replaceAll("");
193        }
194    
195        /**
196         * Translates a string using an alternate color code character into a
197         * string that uses the internal ChatColor.COLOR_CODE color code
198         * character. The alternate color code character will only be replaced if
199         * it is immediately followed by 0-9, A-F, a-f, K-O, k-o, R or r.
200         *
201         * @param altColorChar The alternate color code character to replace. Ex: &
202         * @param textToTranslate Text containing the alternate color code character.
203         * @return Text containing the ChatColor.COLOR_CODE color code character.
204         */
205        public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) {
206            char[] b = textToTranslate.toCharArray();
207            for (int i = 0; i < b.length - 1; i++) {
208                if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i+1]) > -1) {
209                    b[i] = ChatColor.COLOR_CHAR;
210                    b[i+1] = Character.toLowerCase(b[i+1]);
211                }
212            }
213            return new String(b);
214        }
215    
216        /**
217         * Gets the ChatColors used at the end of the given input string.
218         *
219         * @param input Input string to retrieve the colors from.
220         * @return Any remaining ChatColors to pass onto the next line.
221         */
222        public static String getLastColors(String input) {
223            String result = "";
224            int length = input.length();
225    
226            // Search backwards from the end as it is faster
227            for (int index = length - 1; index > -1; index--) {
228                char section = input.charAt(index);
229                if (section == COLOR_CHAR && index < length - 1) {
230                    char c = input.charAt(index + 1);
231                    ChatColor color = getByChar(c);
232    
233                    if (color != null) {
234                        result = color.toString() + result;
235    
236                        // Once we find a color or reset we can stop searching
237                        if (color.isColor() || color.equals(RESET)) {
238                            break;
239                        }
240                    }
241                }
242            }
243    
244            return result;
245        }
246    
247        static {
248            for (ChatColor color : values()) {
249                BY_ID.put(color.intCode, color);
250                BY_CHAR.put(color.code, color);
251            }
252        }
253    }