1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.compiler;
20
21 import javax.tools.Diagnostic;
22 import javax.tools.DiagnosticListener;
23 import javax.tools.JavaFileObject;
24
25 import java.nio.file.Path;
26 import java.util.Arrays;
27 import java.util.LinkedHashMap;
28 import java.util.Locale;
29 import java.util.Map;
30 import java.util.Optional;
31
32 import org.apache.maven.api.plugin.Log;
33 import org.apache.maven.api.services.MessageBuilder;
34 import org.apache.maven.api.services.MessageBuilderFactory;
35
36
37
38
39
40
41 final class DiagnosticLogger implements DiagnosticListener<JavaFileObject> {
42
43
44
45 private final Log logger;
46
47
48
49
50 private final MessageBuilderFactory messageBuilderFactory;
51
52
53
54
55 private final Locale locale;
56
57
58
59
60 private final Path directory;
61
62
63
64
65 private int numErrors, numWarnings;
66
67
68
69
70 private final Map<String, Integer> codeCount;
71
72
73
74
75 private String firstError;
76
77
78
79
80
81
82
83
84
85 DiagnosticLogger(Log logger, MessageBuilderFactory messageBuilderFactory, Locale locale, Path directory) {
86 this.logger = logger;
87 this.messageBuilderFactory = messageBuilderFactory;
88 this.locale = locale;
89 this.directory = directory;
90 codeCount = new LinkedHashMap<>();
91 }
92
93
94
95
96
97
98
99 private String relativize(String file) {
100 if (directory != null) {
101 try {
102 return directory.relativize(Path.of(file)).toString();
103 } catch (IllegalArgumentException e) {
104
105 }
106 }
107 return file;
108 }
109
110
111
112
113
114
115 @Override
116 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
117 String message = diagnostic.getMessage(locale);
118 if (message == null || message.isBlank()) {
119 return;
120 }
121 MessageBuilder record = messageBuilderFactory.builder();
122 record.a(message);
123 JavaFileObject source = diagnostic.getSource();
124 Diagnostic.Kind kind = diagnostic.getKind();
125 String style;
126 switch (kind) {
127 case ERROR:
128 style = ".error:-bold,f:red";
129 break;
130 case MANDATORY_WARNING:
131 case WARNING:
132 style = ".warning:-bold,f:yellow";
133 break;
134 default:
135 style = ".info:-bold,f:blue";
136 if (diagnostic.getLineNumber() == Diagnostic.NOPOS) {
137 source = null;
138 }
139 break;
140 }
141 if (source != null) {
142 record.newline().a(" at ").a(relativize(source.getName()));
143 long line = diagnostic.getLineNumber();
144 long column = diagnostic.getColumnNumber();
145 if (line != Diagnostic.NOPOS || column != Diagnostic.NOPOS) {
146 record.style(style).a('[');
147 if (line != Diagnostic.NOPOS) {
148 record.a(line);
149 }
150 if (column != Diagnostic.NOPOS) {
151 record.a(',').a(column);
152 }
153 record.a(']').resetStyle();
154 }
155 }
156 String log = record.toString();
157 switch (kind) {
158 case ERROR:
159 if (firstError == null) {
160 firstError = message;
161 }
162 logger.error(log);
163 numErrors++;
164 break;
165 case MANDATORY_WARNING:
166 case WARNING:
167 logger.warn(log);
168 numWarnings++;
169 break;
170 default:
171 logger.info(log);
172 break;
173 }
174
175 String code = diagnostic.getCode();
176 if (code != null) {
177 codeCount.merge(code, 1, (old, initial) -> old + 1);
178 }
179 }
180
181
182
183
184
185
186 Optional<String> firstError(Throwable cause) {
187 return Optional.ofNullable(cause != null && firstError == null ? cause.getMessage() : firstError);
188 }
189
190
191
192
193 void logSummary() {
194 MessageBuilder message = messageBuilderFactory.builder();
195 final String patternForCount;
196 if (!codeCount.isEmpty()) {
197 @SuppressWarnings("unchecked")
198 Map.Entry<String, Integer>[] entries = codeCount.entrySet().toArray(Map.Entry[]::new);
199 Arrays.sort(entries, (a, b) -> Integer.compare(b.getValue(), a.getValue()));
200 patternForCount = patternForCount(Math.max(entries[0].getValue(), Math.max(numWarnings, numErrors)));
201 message.strong("Summary of compiler messages:").newline();
202 for (Map.Entry<String, Integer> entry : entries) {
203 int count = entry.getValue();
204 message.format(patternForCount, count, entry.getKey()).newline();
205 }
206 } else {
207 patternForCount = patternForCount(Math.max(numWarnings, numErrors));
208 }
209 if ((numWarnings | numErrors) != 0) {
210 message.strong("Total:");
211 }
212 if (numWarnings != 0) {
213 writeCount(message, patternForCount, numWarnings, "warning");
214 }
215 if (numErrors != 0) {
216 writeCount(message, patternForCount, numErrors, "error");
217 }
218 logger.info(message.toString());
219 }
220
221
222
223
224
225
226 private static String patternForCount(int n) {
227 return " %" + Integer.toString(n).length() + "d %s";
228 }
229
230
231
232
233 private static void writeCount(MessageBuilder message, String patternForCount, int count, String name) {
234 message.newline();
235 message.format(patternForCount, count, name);
236 if (count > 1) {
237 message.append('s');
238 }
239 }
240 }