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    /**
025     * Converts text into ANSI escape sequences.
026     * <p>
027     * The names for colors and attributes are standard, but the exact shade/hue/value of colors are not, and depend on the
028     * device used to display them.
029     * </p>
030     */
031    public enum AnsiEscape {
032    
033        /**
034         * Escape prefix.
035         */
036        PREFIX("\u001b["),
037        /**
038         * Escape suffix.
039         */
040        SUFFIX("m"),
041    
042        /**
043         * Escape separator.
044         */
045        SEPARATOR(";"),
046    
047        /**
048         * Normal general attribute.
049         */
050        NORMAL("0"),
051    
052        /**
053         * Bright general attribute.
054         */
055        BRIGHT("1"),
056    
057        /**
058         * Dim general attribute.
059         */
060        DIM("2"),
061    
062        /**
063         * Underline general attribute.
064         */
065        UNDERLINE("3"),
066    
067        /**
068         * Blink general attribute.
069         */
070        BLINK("5"),
071    
072        /**
073         * Reverse general attribute.
074         */
075        REVERSE("7"),
076    
077        /**
078         * Normal general attribute.
079         */
080        HIDDEN("8"),
081    
082        /**
083         * Black foreground color.
084         */
085        BLACK("30"),
086    
087        /**
088         * Black foreground color.
089         */
090        FG_BLACK("30"),
091    
092        /**
093         * Red foreground color.
094         */
095        RED("31"),
096    
097        /**
098         * Red foreground color.
099         */
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    }