View Javadoc

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 device used to
28   * display them.
29   * </p>
30   */
31  public enum AnsiEscape {
32  
33      PREFIX("\u001b["),
34      SUFFIX("m"),
35      SEPARATOR(";"),
36  
37      /**
38       * General Attributes.
39       */
40      NORMAL("0"),
41      BRIGHT("1"),
42      DIM("2"),
43      UNDERLINE("3"),
44      BLINK("5"),
45      REVERSE("7"),
46      HIDDEN("8"),
47  
48      /**
49       * Foreground Colors.
50       */
51      BLACK("30"),
52      FG_BLACK("30"),
53      RED("31"),
54      FG_RED("31"),
55      GREEN("32"),
56      FG_GREEN("32"),
57      YELLOW("33"),
58      FG_YELLOW("33"),
59      BLUE("34"),
60      FG_BLUE("34"),
61      MAGENTA("35"),
62      FG_MAGENTA("35"),
63      CYAN("36"),
64      FG_CYAN("36"),
65      WHITE("37"),
66      FG_WHITE("37"),
67      DEFAULT("39"),
68      FG_DEFAULT("39"),
69  
70      /**
71       * Background Colors.
72       */
73      BG_BLACK("40"),
74      BG_RED("41"),
75      BG_GREEN("42"),
76      BG_YELLOW("43"),
77      BG_BLUE("44"),
78      BG_MAGENTA("45"),
79      BG_CYAN("46"),
80      BG_WHITE("47");
81  
82      private static final String WHITESPACE_REGEX = "\\s*";
83      
84      private final String code;
85  
86      private AnsiEscape(String code) {
87          this.code = code;
88      }
89  
90      public static String getDefaultStyle() {
91          return PREFIX.getCode() + SUFFIX.getCode();
92      }
93  
94      private static String toRegexSeparator(String separator) {
95          return WHITESPACE_REGEX + separator + WHITESPACE_REGEX;
96      }
97  
98      public String getCode() {
99          return code;
100     }
101 
102     /**
103      * Creates a Map from a source array where values are ANSI escape sequences. The format is:
104      * 
105      * <pre>
106      * Key1=Value, Key2=Value, ...
107      * </pre>
108      * 
109      * For example:
110      * 
111      * <pre>
112      * ERROR=red bold, WARN=yellow bold, INFO=green, ...
113      * </pre>
114      * 
115      * You can use whitespace around the comma and equal sign. The names in values MUST come from the {@linkplain AnsiEscape} enum, case is
116      * normalized to upper-case internally.
117      * 
118      * @param values
119      *            the source string to parse.
120      * @param dontEscapeKeys
121      *            do not escape these keys, leave the values as is in the map
122      * @return a new map
123      */
124     public static Map<String, String> createMap(String values, String[] dontEscapeKeys) {
125         return createMap(values.split(toRegexSeparator(",")), dontEscapeKeys);
126     }
127 
128     /**
129      * Creates a Map from a source array where values are ANSI escape sequences. Each array entry must be in the format:
130      * 
131      * <pre>
132      * Key1 = Value
133      * </pre>
134      * 
135      * For example:
136      * 
137      * <pre>
138      * ERROR=red bold
139      * </pre>
140      * 
141      * You can use whitespace around the equal sign and between the value elements. The names in values MUST come from the
142      * {@linkplain AnsiEscape} enum, case is normalized to upper-case internally.
143      * 
144      * @param values
145      *            the source array to parse.
146      * @param dontEscapeKeys
147      *            do not escape these keys, leave the values as is in the map
148      * @return a new map
149      */
150     public static Map<String, String> createMap(String[] values, String[] dontEscapeKeys) {
151         final String[] sortedIgnoreKeys = dontEscapeKeys != null ? dontEscapeKeys.clone() : new String[0];
152         Arrays.sort(sortedIgnoreKeys);
153         Map<String, String> map = new HashMap<String, String>();
154         for (String string : values) {
155             String[] keyValue = string.split(toRegexSeparator("="));
156             if (keyValue.length > 1) {
157                 final String key = keyValue[0].toUpperCase(Locale.ENGLISH);
158                 final String value = keyValue[1];
159                 final boolean escape = Arrays.binarySearch(sortedIgnoreKeys, key) < 0;
160                 map.put(key, escape ? createSequence(value.split("\\s")) : value);
161             }
162         }
163         return map;
164     }
165 
166     /**
167      * Creates an ANSI escape sequence from the given {@linkplain AnsiEscape} names.
168      * 
169      * @param names
170      *            {@linkplain AnsiEscape} names.
171      * @return An ANSI escape sequence.
172      */
173     public static String createSequence(String[] names) {
174         if (names == null) {
175             return getDefaultStyle();
176         }
177         StringBuilder sb = new StringBuilder(AnsiEscape.PREFIX.getCode());
178         boolean first = true;
179         for (String name : names) {
180             try {
181                 AnsiEscape escape = AnsiEscape.valueOf(name.trim().toUpperCase(Locale.ENGLISH));
182                 if (!first) {
183                     sb.append(AnsiEscape.SEPARATOR.getCode());
184                 }
185                 first = false;
186                 sb.append(escape.getCode());
187             } catch (Exception ex) {
188                 // Ignore the error.
189             }
190         }
191         sb.append(AnsiEscape.SUFFIX.getCode());
192         return sb.toString();
193     }
194 
195 }