    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    }