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         * The Control Sequence Introducer (or Control Sequence Initiator).
038         * <p>
039         * Most sequences are more than two characters and start with the characters ESC and [ (the left bracket).
040         * </p>
041         */
042        CSI("\u001b["),
043        
044        /**
045         * Escape suffix.
046         */
047        SUFFIX("m"),
048    
049        /**
050         * Escape separator.
051         */
052        SEPARATOR(";"),
053    
054        /**
055         * Normal general attribute.
056         */
057        NORMAL("0"),
058    
059        /**
060         * Bright general attribute.
061         */
062        BRIGHT("1"),
063    
064        /**
065         * Dim general attribute.
066         */
067        DIM("2"),
068    
069        /**
070         * Underline general attribute.
071         */
072        UNDERLINE("3"),
073    
074        /**
075         * Blink general attribute.
076         */
077        BLINK("5"),
078    
079        /**
080         * Reverse general attribute.
081         */
082        REVERSE("7"),
083    
084        /**
085         * Normal general attribute.
086         */
087        HIDDEN("8"),
088    
089        /**
090         * Black foreground color.
091         */
092        BLACK("30"),
093    
094        /**
095         * Black foreground color.
096         */
097        FG_BLACK("30"),
098    
099        /**
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    }