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 final String code;
220
221 private AnsiEscape(final String code) {
222 this.code = code;
223 }
224
225 /**
226 * Gets the default style.
227 *
228 * @return the default style
229 */
230 public static String getDefaultStyle() {
231 return CSI.getCode() + SUFFIX.getCode();
232 }
233
234 /**
235 * Gets the escape code.
236 *
237 * @return the escape code.
238 */
239 public String getCode() {
240 return code;
241 }
242
243 /**
244 * Creates a Map from a source array where values are ANSI escape sequences. The format is:
245 *
246 * <pre>
247 * Key1=Value, Key2=Value, ...
248 * </pre>
249 *
250 * For example:
251 *
252 * <pre>
253 * ERROR=red bold, WARN=yellow bold, INFO=green, ...
254 * </pre>
255 *
256 * You can use whitespace around the comma and equal sign. The names in values MUST come from the
257 * {@linkplain AnsiEscape} enum, case is normalized to upper-case internally.
258 *
259 * @param values the source string to parse.
260 * @param dontEscapeKeys do not escape these keys, leave the values as is in the map
261 * @return a new map
262 */
263 public static Map<String, String> createMap(final String values, final String[] dontEscapeKeys) {
264 return createMap(values.split(Patterns.COMMA_SEPARATOR), dontEscapeKeys);
265 }
266
267 /**
268 * Creates a Map from a source array where values are ANSI escape sequences. Each array entry must be in the format:
269 *
270 * <pre>
271 * Key1 = Value
272 * </pre>
273 *
274 * For example:
275 *
276 * <pre>
277 * ERROR=red bold
278 * </pre>
279 *
280 * You can use whitespace around the equal sign and between the value elements. The names in values MUST come from
281 * the {@linkplain AnsiEscape} enum, case is normalized to upper-case internally.
282 *
283 * @param values
284 * the source array to parse.
285 * @param dontEscapeKeys
286 * do not escape these keys, leave the values as is in the map
287 * @return a new map
288 */
289 public static Map<String, String> createMap(final String[] values, final String[] dontEscapeKeys) {
290 final String[] sortedIgnoreKeys = dontEscapeKeys != null ? dontEscapeKeys.clone() : new String[0];
291 Arrays.sort(sortedIgnoreKeys);
292 final Map<String, String> map = new HashMap<String, String>();
293 for (final String string : values) {
294 final String[] keyValue = string.split(Patterns.toWhitespaceSeparator("="));
295 if (keyValue.length > 1) {
296 final String key = keyValue[0].toUpperCase(Locale.ENGLISH);
297 final String value = keyValue[1];
298 final boolean escape = Arrays.binarySearch(sortedIgnoreKeys, key) < 0;
299 map.put(key, escape ? createSequence(value.split("\\s")) : value);
300 }
301 }
302 return map;
303 }
304
305 /**
306 * Creates an ANSI escape sequence from the given {@linkplain AnsiEscape} names.
307 *
308 * @param names
309 * {@linkplain AnsiEscape} names.
310 * @return An ANSI escape sequence.
311 */
312 public static String createSequence(final String... names) {
313 if (names == null) {
314 return getDefaultStyle();
315 }
316 final StringBuilder sb = new StringBuilder(AnsiEscape.CSI.getCode());
317 boolean first = true;
318 for (final String name : names) {
319 try {
320 final AnsiEscape escape = EnglishEnums.valueOf(AnsiEscape.class, name.trim());
321 if (!first) {
322 sb.append(AnsiEscape.SEPARATOR.getCode());
323 }
324 first = false;
325 sb.append(escape.getCode());
326 } catch (final Exception ex) {
327 // Ignore the error.
328 }
329 }
330 sb.append(AnsiEscape.SUFFIX.getCode());
331 return sb.toString();
332 }
333
334 }