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 }