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