001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 package org.apache.logging.log4j.core.pattern; 018 019 import java.util.Arrays; 020 import java.util.HashMap; 021 import java.util.Locale; 022 import java.util.Map; 023 024 /** 025 * Converts text into ANSI escape sequences. 026 * <p> 027 * The names for colors and attributes are standard, but the exact shade/hue/value of colors are not, and depend on the 028 * device used to display them. 029 * </p> 030 */ 031 public enum AnsiEscape { 032 033 /** 034 * Escape prefix. 035 */ 036 PREFIX("\u001b["), 037 /** 038 * Escape suffix. 039 */ 040 SUFFIX("m"), 041 042 /** 043 * Escape separator. 044 */ 045 SEPARATOR(";"), 046 047 /** 048 * Normal general attribute. 049 */ 050 NORMAL("0"), 051 052 /** 053 * Bright general attribute. 054 */ 055 BRIGHT("1"), 056 057 /** 058 * Dim general attribute. 059 */ 060 DIM("2"), 061 062 /** 063 * Underline general attribute. 064 */ 065 UNDERLINE("3"), 066 067 /** 068 * Blink general attribute. 069 */ 070 BLINK("5"), 071 072 /** 073 * Reverse general attribute. 074 */ 075 REVERSE("7"), 076 077 /** 078 * Normal general attribute. 079 */ 080 HIDDEN("8"), 081 082 /** 083 * Black foreground color. 084 */ 085 BLACK("30"), 086 087 /** 088 * Black foreground color. 089 */ 090 FG_BLACK("30"), 091 092 /** 093 * Red foreground color. 094 */ 095 RED("31"), 096 097 /** 098 * Red foreground color. 099 */ 100 FG_RED("31"), 101 102 /** 103 * Green foreground color. 104 */ 105 GREEN("32"), 106 107 /** 108 * Green foreground color. 109 */ 110 FG_GREEN("32"), 111 112 /** 113 * Yellow foreground color. 114 */ 115 YELLOW("33"), 116 117 /** 118 * Yellow foreground color. 119 */ 120 FG_YELLOW("33"), 121 122 /** 123 * Blue foreground color. 124 */ 125 BLUE("34"), 126 127 /** 128 * Blue foreground color. 129 */ 130 FG_BLUE("34"), 131 132 /** 133 * Magenta foreground color. 134 */ 135 MAGENTA("35"), 136 137 /** 138 * Magenta foreground color. 139 */ 140 FG_MAGENTA("35"), 141 142 /** 143 * Cyan foreground color. 144 */ 145 CYAN("36"), 146 147 /** 148 * Cyan foreground color. 149 */ 150 FG_CYAN("36"), 151 152 /** 153 * White foreground color. 154 */ 155 WHITE("37"), 156 157 /** 158 * White foreground color. 159 */ 160 FG_WHITE("37"), 161 162 /** 163 * Default foreground color. 164 */ 165 DEFAULT("39"), 166 167 /** 168 * Default foreground color. 169 */ 170 FG_DEFAULT("39"), 171 172 /** 173 * Black background color. 174 */ 175 BG_BLACK("40"), 176 177 /** 178 * Red background color. 179 */ 180 BG_RED("41"), 181 182 /** 183 * Green background color. 184 */ 185 BG_GREEN("42"), 186 187 /** 188 * Yellow background color. 189 */ 190 BG_YELLOW("43"), 191 192 /** 193 * Blue background color. 194 */ 195 BG_BLUE("44"), 196 197 /** 198 * Magenta background color. 199 */ 200 BG_MAGENTA("45"), 201 202 /** 203 * Cyan background color. 204 */ 205 BG_CYAN("46"), 206 207 /** 208 * White background color. 209 */ 210 BG_WHITE("47"); 211 212 private static final String WHITESPACE_REGEX = "\\s*"; 213 214 private final String code; 215 216 private AnsiEscape(final String code) { 217 this.code = code; 218 } 219 220 /** 221 * Gets the default style. 222 * 223 * @return the default style 224 */ 225 public static String getDefaultStyle() { 226 return PREFIX.getCode() + SUFFIX.getCode(); 227 } 228 229 private static String toRegexSeparator(final String separator) { 230 return WHITESPACE_REGEX + separator + WHITESPACE_REGEX; 231 } 232 233 /** 234 * Gets the escape code. 235 * 236 * @return the escape code. 237 */ 238 public String getCode() { 239 return code; 240 } 241 242 /** 243 * Creates a Map from a source array where values are ANSI escape sequences. The format is: 244 * 245 * <pre> 246 * Key1=Value, Key2=Value, ... 247 * </pre> 248 * 249 * For example: 250 * 251 * <pre> 252 * ERROR=red bold, WARN=yellow bold, INFO=green, ... 253 * </pre> 254 * 255 * You can use whitespace around the comma and equal sign. The names in values MUST come from the 256 * {@linkplain AnsiEscape} enum, case is normalized to upper-case internally. 257 * 258 * @param values the source string to parse. 259 * @param dontEscapeKeys do not escape these keys, leave the values as is in the map 260 * @return a new map 261 */ 262 public static Map<String, String> createMap(final String values, final String[] dontEscapeKeys) { 263 return createMap(values.split(toRegexSeparator(",")), dontEscapeKeys); 264 } 265 266 /** 267 * Creates a Map from a source array where values are ANSI escape sequences. Each array entry must be in the format: 268 * 269 * <pre> 270 * Key1 = Value 271 * </pre> 272 * 273 * For example: 274 * 275 * <pre> 276 * ERROR=red bold 277 * </pre> 278 * 279 * You can use whitespace around the equal sign and between the value elements. The names in values MUST come from 280 * the {@linkplain AnsiEscape} enum, case is normalized to upper-case internally. 281 * 282 * @param values 283 * the source array to parse. 284 * @param dontEscapeKeys 285 * do not escape these keys, leave the values as is in the map 286 * @return a new map 287 */ 288 public static Map<String, String> createMap(final String[] values, final String[] dontEscapeKeys) { 289 final String[] sortedIgnoreKeys = dontEscapeKeys != null ? dontEscapeKeys.clone() : new String[0]; 290 Arrays.sort(sortedIgnoreKeys); 291 final Map<String, String> map = new HashMap<String, String>(); 292 for (final String string : values) { 293 final String[] keyValue = string.split(toRegexSeparator("=")); 294 if (keyValue.length > 1) { 295 final String key = keyValue[0].toUpperCase(Locale.ENGLISH); 296 final String value = keyValue[1]; 297 final boolean escape = Arrays.binarySearch(sortedIgnoreKeys, key) < 0; 298 map.put(key, escape ? createSequence(value.split("\\s")) : value); 299 } 300 } 301 return map; 302 } 303 304 /** 305 * Creates an ANSI escape sequence from the given {@linkplain AnsiEscape} names. 306 * 307 * @param names 308 * {@linkplain AnsiEscape} names. 309 * @return An ANSI escape sequence. 310 */ 311 public static String createSequence(final String... names) { 312 if (names == null) { 313 return getDefaultStyle(); 314 } 315 final StringBuilder sb = new StringBuilder(AnsiEscape.PREFIX.getCode()); 316 boolean first = true; 317 for (final String name : names) { 318 try { 319 final AnsiEscape escape = AnsiEscape.valueOf(name.trim().toUpperCase(Locale.ENGLISH)); 320 if (!first) { 321 sb.append(AnsiEscape.SEPARATOR.getCode()); 322 } 323 first = false; 324 sb.append(escape.getCode()); 325 } catch (final Exception ex) { 326 // Ignore the error. 327 } 328 } 329 sb.append(AnsiEscape.SUFFIX.getCode()); 330 return sb.toString(); 331 } 332 333 }