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 /**
25 * Converts text into ANSI escape sequences.
26 * <p>
27 * The names for colors and attributes are standard, but the exact shade/hue/value of colors are not, and depend on the
28 * device used to display them.
29 * </p>
30 */
31 public enum AnsiEscape {
32
33 /**
34 * Escape prefix.
35 */
36 PREFIX("\u001b["),
37 /**
38 * Escape suffix.
39 */
40 SUFFIX("m"),
41
42 /**
43 * Escape separator.
44 */
45 SEPARATOR(";"),
46
47 /**
48 * Normal general attribute.
49 */
50 NORMAL("0"),
51
52 /**
53 * Bright general attribute.
54 */
55 BRIGHT("1"),
56
57 /**
58 * Dim general attribute.
59 */
60 DIM("2"),
61
62 /**
63 * Underline general attribute.
64 */
65 UNDERLINE("3"),
66
67 /**
68 * Blink general attribute.
69 */
70 BLINK("5"),
71
72 /**
73 * Reverse general attribute.
74 */
75 REVERSE("7"),
76
77 /**
78 * Normal general attribute.
79 */
80 HIDDEN("8"),
81
82 /**
83 * Black foreground color.
84 */
85 BLACK("30"),
86
87 /**
88 * Black foreground color.
89 */
90 FG_BLACK("30"),
91
92 /**
93 * Red foreground color.
94 */
95 RED("31"),
96
97 /**
98 * Red foreground color.
99 */
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 }