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 device used to 028 * display them. 029 * </p> 030 */ 031 public enum AnsiEscape { 032 033 PREFIX("\u001b["), 034 SUFFIX("m"), 035 SEPARATOR(";"), 036 037 /** 038 * General Attributes. 039 */ 040 NORMAL("0"), 041 BRIGHT("1"), 042 DIM("2"), 043 UNDERLINE("3"), 044 BLINK("5"), 045 REVERSE("7"), 046 HIDDEN("8"), 047 048 /** 049 * Foreground Colors. 050 */ 051 BLACK("30"), 052 FG_BLACK("30"), 053 RED("31"), 054 FG_RED("31"), 055 GREEN("32"), 056 FG_GREEN("32"), 057 YELLOW("33"), 058 FG_YELLOW("33"), 059 BLUE("34"), 060 FG_BLUE("34"), 061 MAGENTA("35"), 062 FG_MAGENTA("35"), 063 CYAN("36"), 064 FG_CYAN("36"), 065 WHITE("37"), 066 FG_WHITE("37"), 067 DEFAULT("39"), 068 FG_DEFAULT("39"), 069 070 /** 071 * Background Colors. 072 */ 073 BG_BLACK("40"), 074 BG_RED("41"), 075 BG_GREEN("42"), 076 BG_YELLOW("43"), 077 BG_BLUE("44"), 078 BG_MAGENTA("45"), 079 BG_CYAN("46"), 080 BG_WHITE("47"); 081 082 private static final String WHITESPACE_REGEX = "\\s*"; 083 084 private final String code; 085 086 private AnsiEscape(String code) { 087 this.code = code; 088 } 089 090 public static String getDefaultStyle() { 091 return PREFIX.getCode() + SUFFIX.getCode(); 092 } 093 094 private static String toRegexSeparator(String separator) { 095 return WHITESPACE_REGEX + separator + WHITESPACE_REGEX; 096 } 097 098 public String getCode() { 099 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 }