1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.pattern;
18
19 import static org.fusesource.jansi.AnsiRenderer.Code.BG_RED;
20 import static org.fusesource.jansi.AnsiRenderer.Code.BOLD;
21 import static org.fusesource.jansi.AnsiRenderer.Code.RED;
22 import static org.fusesource.jansi.AnsiRenderer.Code.WHITE;
23 import static org.fusesource.jansi.AnsiRenderer.Code.YELLOW;
24
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.Locale;
28 import java.util.Map;
29
30 import org.apache.logging.log4j.status.StatusLogger;
31 import org.fusesource.jansi.Ansi;
32 import org.fusesource.jansi.AnsiRenderer;
33 import org.fusesource.jansi.AnsiRenderer.Code;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 public final class JAnsiTextRenderer implements TextRenderer {
82
83 public static final Map<String, Code[]> DefaultExceptionStyleMap;
84 static final Map<String, Code[]> DefaultMessageStyleMap;
85 private static final Map<String, Map<String, Code[]>> PrefedinedStyleMaps;
86
87 private static void put(final Map<String, Code[]> map, final String name, final Code... codes) {
88 map.put(name, codes);
89 }
90
91 static {
92 final Map<String, Map<String, Code[]>> tempPreDefs = new HashMap<>();
93
94 {
95
96 final Map<String, Code[]> map = new HashMap<>();
97 put(map, "Prefix", WHITE);
98 put(map, "Name", BG_RED, WHITE);
99 put(map, "NameMessageSeparator", BG_RED, WHITE);
100 put(map, "Message", BG_RED, WHITE, BOLD);
101 put(map, "At", WHITE);
102 put(map, "CauseLabel", WHITE);
103 put(map, "Text", WHITE);
104 put(map, "More", WHITE);
105 put(map, "Suppressed", WHITE);
106
107 put(map, "StackTraceElement.ClassName", YELLOW);
108 put(map, "StackTraceElement.ClassMethodSeparator", YELLOW);
109 put(map, "StackTraceElement.MethodName", YELLOW);
110 put(map, "StackTraceElement.NativeMethod", YELLOW);
111 put(map, "StackTraceElement.FileName", RED);
112 put(map, "StackTraceElement.LineNumber", RED);
113 put(map, "StackTraceElement.Container", RED);
114 put(map, "StackTraceElement.ContainerSeparator", WHITE);
115 put(map, "StackTraceElement.UnknownSource", RED);
116
117 put(map, "ExtraClassInfo.Inexact", YELLOW);
118 put(map, "ExtraClassInfo.Container", YELLOW);
119 put(map, "ExtraClassInfo.ContainerSeparator", YELLOW);
120 put(map, "ExtraClassInfo.Location", YELLOW);
121 put(map, "ExtraClassInfo.Version", YELLOW);
122
123 DefaultExceptionStyleMap = Collections.unmodifiableMap(map);
124 tempPreDefs.put("Spock", DefaultExceptionStyleMap);
125 }
126
127 {
128
129 final Map<String, Code[]> map = new HashMap<>();
130 put(map, "Prefix", WHITE);
131 put(map, "Name", BG_RED, YELLOW, BOLD);
132 put(map, "NameMessageSeparator", BG_RED, YELLOW);
133 put(map, "Message", BG_RED, WHITE, BOLD);
134 put(map, "At", WHITE);
135 put(map, "CauseLabel", WHITE);
136 put(map, "Text", WHITE);
137 put(map, "More", WHITE);
138 put(map, "Suppressed", WHITE);
139
140 put(map, "StackTraceElement.ClassName", BG_RED, WHITE);
141 put(map, "StackTraceElement.ClassMethodSeparator", BG_RED, YELLOW);
142 put(map, "StackTraceElement.MethodName", BG_RED, YELLOW);
143 put(map, "StackTraceElement.NativeMethod", BG_RED, YELLOW);
144 put(map, "StackTraceElement.FileName", RED);
145 put(map, "StackTraceElement.LineNumber", RED);
146 put(map, "StackTraceElement.Container", RED);
147 put(map, "StackTraceElement.ContainerSeparator", WHITE);
148 put(map, "StackTraceElement.UnknownSource", RED);
149
150 put(map, "ExtraClassInfo.Inexact", YELLOW);
151 put(map, "ExtraClassInfo.Container", WHITE);
152 put(map, "ExtraClassInfo.ContainerSeparator", WHITE);
153 put(map, "ExtraClassInfo.Location", YELLOW);
154 put(map, "ExtraClassInfo.Version", YELLOW);
155
156 tempPreDefs.put("Kirk", Collections.unmodifiableMap(map));
157 }
158 {
159 final Map<String, Code[]> temp = new HashMap<>();
160
161 DefaultMessageStyleMap = Collections.unmodifiableMap(temp);
162 }
163 PrefedinedStyleMaps = Collections.unmodifiableMap(tempPreDefs);
164 }
165
166 private final String beginToken;
167 private final int beginTokenLen;
168 private final String endToken;
169 private final int endTokenLen;
170 private final Map<String, Code[]> styleMap;
171
172 public JAnsiTextRenderer(final String[] formats, final Map<String, Code[]> defaultStyleMap) {
173 String tempBeginToken = AnsiRenderer.BEGIN_TOKEN;
174 String tempEndToken = AnsiRenderer.END_TOKEN;
175 Map<String, Code[]> map;
176 if (formats.length > 1) {
177 final String allStylesStr = formats[1];
178
179 final String[] allStyleAssignmentsArr = allStylesStr.split(" ");
180 map = new HashMap<>(allStyleAssignmentsArr.length + defaultStyleMap.size());
181 map.putAll(defaultStyleMap);
182 for (final String styleAssignmentStr : allStyleAssignmentsArr) {
183 final String[] styleAssignmentArr = styleAssignmentStr.split("=");
184 if (styleAssignmentArr.length != 2) {
185 StatusLogger.getLogger().warn("{} parsing style \"{}\", expected format: StyleName=Code(,Code)*",
186 getClass().getSimpleName(), styleAssignmentStr);
187 } else {
188 final String styleName = styleAssignmentArr[0];
189 final String codeListStr = styleAssignmentArr[1];
190 final String[] codeNames = codeListStr.split(",");
191 if (codeNames.length == 0) {
192 StatusLogger.getLogger().warn(
193 "{} parsing style \"{}\", expected format: StyleName=Code(,Code)*",
194 getClass().getSimpleName(), styleAssignmentStr);
195 } else {
196 switch (styleName) {
197 case "BeginToken":
198 tempBeginToken = codeNames[0];
199 break;
200 case "EndToken":
201 tempEndToken = codeNames[0];
202 break;
203 case "StyleMapName":
204 final String predefinedMapName = codeNames[0];
205 final Map<String, Code[]> predefinedMap = PrefedinedStyleMaps.get(predefinedMapName);
206 if (predefinedMap != null) {
207 map.putAll(predefinedMap);
208 } else {
209 StatusLogger.getLogger().warn("Unknown predefined map name {}, pick one of {}",
210 predefinedMapName, null);
211 }
212 break;
213 default:
214 final Code[] codes = new Code[codeNames.length];
215 for (int i = 0; i < codes.length; i++) {
216 codes[i] = toCode(codeNames[i]);
217 }
218 map.put(styleName, codes);
219 }
220 }
221 }
222 }
223 } else {
224 map = defaultStyleMap;
225 }
226 styleMap = map;
227 beginToken = tempBeginToken;
228 endToken = tempEndToken;
229 beginTokenLen = tempBeginToken.length();
230 endTokenLen = tempEndToken.length();
231 }
232
233 public Map<String, Code[]> getStyleMap() {
234 return styleMap;
235 }
236
237 private void render(final Ansi ansi, final Code code) {
238 if (code.isColor()) {
239 if (code.isBackground()) {
240 ansi.bg(code.getColor());
241 } else {
242 ansi.fg(code.getColor());
243 }
244 } else if (code.isAttribute()) {
245 ansi.a(code.getAttribute());
246 }
247 }
248
249 private void render(final Ansi ansi, final Code... codes) {
250 for (final Code code : codes) {
251 render(ansi, code);
252 }
253 }
254
255
256
257
258
259
260
261
262
263
264 private String render(final String text, final String... names) {
265 final Ansi ansi = Ansi.ansi();
266 for (final String name : names) {
267 final Code[] codes = styleMap.get(name);
268 if (codes != null) {
269 render(ansi, codes);
270 } else {
271 render(ansi, toCode(name));
272 }
273 }
274 return ansi.a(text).reset().toString();
275 }
276
277
278 @Override
279 public void render(final String input, final StringBuilder output, final String styleName)
280 throws IllegalArgumentException {
281 output.append(render(input, styleName));
282 }
283
284 @Override
285 public void render(final StringBuilder input, final StringBuilder output) throws IllegalArgumentException {
286 int i = 0;
287 int j, k;
288
289 while (true) {
290 j = input.indexOf(beginToken, i);
291 if (j == -1) {
292 if (i == 0) {
293 output.append(input);
294 return;
295 }
296 output.append(input.substring(i, input.length()));
297 return;
298 }
299 output.append(input.substring(i, j));
300 k = input.indexOf(endToken, j);
301
302 if (k == -1) {
303 output.append(input);
304 return;
305 }
306 j += beginTokenLen;
307 final String spec = input.substring(j, k);
308
309 final String[] items = spec.split(AnsiRenderer.CODE_TEXT_SEPARATOR, 2);
310 if (items.length == 1) {
311 output.append(input);
312 return;
313 }
314 final String replacement = render(items[1], items[0].split(","));
315
316 output.append(replacement);
317
318 i = k + endTokenLen;
319 }
320 }
321
322 private Code toCode(final String name) {
323 return Code.valueOf(name.toUpperCase(Locale.ENGLISH));
324 }
325
326 @Override
327 public String toString() {
328 return "JAnsiMessageRenderer [beginToken=" + beginToken + ", beginTokenLen=" + beginTokenLen + ", endToken="
329 + endToken + ", endTokenLen=" + endTokenLen + ", styleMap=" + styleMap + "]";
330 }
331
332 }