1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache license, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the license for the specific language governing permissions and 15 * limitations under the license. 16 */ 17 package org.apache.logging.log4j.core.pattern; 18 19 import java.util.Arrays; 20 import java.util.HashMap; 21 import java.util.Locale; 22 import java.util.Map; 23 24 import org.apache.logging.log4j.core.util.Patterns; 25 import org.apache.logging.log4j.util.EnglishEnums; 26 27 /** 28 * Converts text into ANSI escape sequences. 29 * <p> 30 * The names for colors and attributes are standard, but the exact shade/hue/value of colors are not, and depend on the 31 * device used to display them. 32 * </p> 33 */ 34 public enum AnsiEscape { 35 36 /** 37 * The Control Sequence Introducer (or Control Sequence Initiator). 38 * <p> 39 * Most sequences are more than two characters and start with the characters ESC and [ (the left bracket). 40 * </p> 41 */ 42 CSI("\u001b["), 43 44 /** 45 * Escape suffix. 46 */ 47 SUFFIX("m"), 48 49 /** 50 * Escape separator. 51 */ 52 SEPARATOR(";"), 53 54 /** 55 * Normal general attribute. 56 */ 57 NORMAL("0"), 58 59 /** 60 * Bright general attribute. 61 */ 62 BRIGHT("1"), 63 64 /** 65 * Dim general attribute. 66 */ 67 DIM("2"), 68 69 /** 70 * Underline general attribute. 71 */ 72 UNDERLINE("3"), 73 74 /** 75 * Blink general attribute. 76 */ 77 BLINK("5"), 78 79 /** 80 * Reverse general attribute. 81 */ 82 REVERSE("7"), 83 84 /** 85 * Normal general attribute. 86 */ 87 HIDDEN("8"), 88 89 /** 90 * Black foreground color. 91 */ 92 BLACK("30"), 93 94 /** 95 * Black foreground color. 96 */ 97 FG_BLACK("30"), 98 99 /** 100 * Red foreground color. 101 */ 102 RED("31"), 103 104 /** 105 * Red foreground color. 106 */ 107 FG_RED("31"), 108 109 /** 110 * Green foreground color. 111 */ 112 GREEN("32"), 113 114 /** 115 * Green foreground color. 116 */ 117 FG_GREEN("32"), 118 119 /** 120 * Yellow foreground color. 121 */ 122 YELLOW("33"), 123 124 /** 125 * Yellow foreground color. 126 */ 127 FG_YELLOW("33"), 128 129 /** 130 * Blue foreground color. 131 */ 132 BLUE("34"), 133 134 /** 135 * Blue foreground color. 136 */ 137 FG_BLUE("34"), 138 139 /** 140 * Magenta foreground color. 141 */ 142 MAGENTA("35"), 143 144 /** 145 * Magenta foreground color. 146 */ 147 FG_MAGENTA("35"), 148 149 /** 150 * Cyan foreground color. 151 */ 152 CYAN("36"), 153 154 /** 155 * Cyan foreground color. 156 */ 157 FG_CYAN("36"), 158 159 /** 160 * White foreground color. 161 */ 162 WHITE("37"), 163 164 /** 165 * White foreground color. 166 */ 167 FG_WHITE("37"), 168 169 /** 170 * Default foreground color. 171 */ 172 DEFAULT("39"), 173 174 /** 175 * Default foreground color. 176 */ 177 FG_DEFAULT("39"), 178 179 /** 180 * Black background color. 181 */ 182 BG_BLACK("40"), 183 184 /** 185 * Red background color. 186 */ 187 BG_RED("41"), 188 189 /** 190 * Green background color. 191 */ 192 BG_GREEN("42"), 193 194 /** 195 * Yellow background color. 196 */ 197 BG_YELLOW("43"), 198 199 /** 200 * Blue background color. 201 */ 202 BG_BLUE("44"), 203 204 /** 205 * Magenta background color. 206 */ 207 BG_MAGENTA("45"), 208 209 /** 210 * Cyan background color. 211 */ 212 BG_CYAN("46"), 213 214 /** 215 * White background color. 216 */ 217 BG_WHITE("47"); 218 219 private static final String DEFAULT_STYLE = CSI.getCode() + SUFFIX.getCode(); 220 221 private final String code; 222 223 AnsiEscape(final String code) { 224 this.code = code; 225 } 226 227 /** 228 * Gets the default style. 229 * 230 * @return the default style 231 */ 232 public static String getDefaultStyle() { 233 return DEFAULT_STYLE; 234 } 235 236 /** 237 * Gets the escape code. 238 * 239 * @return the escape code. 240 */ 241 public String getCode() { 242 return code; 243 } 244 245 /** 246 * Creates a Map from a source array where values are ANSI escape sequences. The format is: 247 * 248 * <pre> 249 * Key1=Value, Key2=Value, ... 250 * </pre> 251 * 252 * For example: 253 * 254 * <pre> 255 * ERROR=red bold, WARN=yellow bold, INFO=green, ... 256 * </pre> 257 * 258 * You can use whitespace around the comma and equal sign. The names in values MUST come from the 259 * {@linkplain AnsiEscape} enum, case is normalized to upper-case internally. 260 * 261 * @param values the source string to parse. 262 * @param dontEscapeKeys do not escape these keys, leave the values as is in the map 263 * @return a new map 264 */ 265 public static Map<String, String> createMap(final String values, final String[] dontEscapeKeys) { 266 return createMap(values.split(Patterns.COMMA_SEPARATOR), dontEscapeKeys); 267 } 268 269 /** 270 * Creates a Map from a source array where values are ANSI escape sequences. Each array entry must be in the format: 271 * 272 * <pre> 273 * Key1 = Value 274 * </pre> 275 * 276 * For example: 277 * 278 * <pre> 279 * ERROR=red bold 280 * </pre> 281 * 282 * You can use whitespace around the equal sign and between the value elements. The names in values MUST come from 283 * the {@linkplain AnsiEscape} enum, case is normalized to upper-case internally. 284 * 285 * @param values 286 * the source array to parse. 287 * @param dontEscapeKeys 288 * do not escape these keys, leave the values as is in the map 289 * @return a new map 290 */ 291 public static Map<String, String> createMap(final String[] values, final String[] dontEscapeKeys) { 292 final String[] sortedIgnoreKeys = dontEscapeKeys != null ? dontEscapeKeys.clone() : new String[0]; 293 Arrays.sort(sortedIgnoreKeys); 294 final Map<String, String> map = new HashMap<>(); 295 for (final String string : values) { 296 final String[] keyValue = string.split(Patterns.toWhitespaceSeparator("=")); 297 if (keyValue.length > 1) { 298 final String key = keyValue[0].toUpperCase(Locale.ENGLISH); 299 final String value = keyValue[1]; 300 final boolean escape = Arrays.binarySearch(sortedIgnoreKeys, key) < 0; 301 map.put(key, escape ? createSequence(value.split("\\s")) : value); 302 } 303 } 304 return map; 305 } 306 307 /** 308 * Creates an ANSI escape sequence from the given {@linkplain AnsiEscape} names. 309 * 310 * @param names 311 * {@linkplain AnsiEscape} names. 312 * @return An ANSI escape sequence. 313 */ 314 public static String createSequence(final String... names) { 315 if (names == null) { 316 return getDefaultStyle(); 317 } 318 final StringBuilder sb = new StringBuilder(AnsiEscape.CSI.getCode()); 319 boolean first = true; 320 for (final String name : names) { 321 try { 322 final AnsiEscape escape = EnglishEnums.valueOf(AnsiEscape.class, name.trim()); 323 if (!first) { 324 sb.append(AnsiEscape.SEPARATOR.getCode()); 325 } 326 first = false; 327 sb.append(escape.getCode()); 328 } catch (final Exception ex) { 329 // Ignore the error. 330 } 331 } 332 sb.append(AnsiEscape.SUFFIX.getCode()); 333 return sb.toString(); 334 } 335 336 }