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 try {
101 return directory.relativize(Path.of(file)).toString();
102 } catch (IllegalArgumentException e) {
103
104 return file;
105 }
106 }
107
108
109
110
111
112
113 @Override
114 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
115 String message = diagnostic.getMessage(locale);
116 if (message == null || message.isBlank()) {
117 return;
118 }
119 MessageBuilder record = messageBuilderFactory.builder();
120 record.a(message);
121 JavaFileObject source = diagnostic.getSource();
122 Diagnostic.Kind kind = diagnostic.getKind();
123 String style;
124 switch (kind) {
125 case ERROR:
126 style = ".error:-bold,f:red";
127 break;
128 case MANDATORY_WARNING:
129 case WARNING:
130 style = ".warning:-bold,f:yellow";
131 break;
132 default:
133 style = ".info:-bold,f:blue";
134 if (diagnostic.getLineNumber() == Diagnostic.NOPOS) {
135 source = null;
136 }
137 break;
138 }
139 if (source != null) {
140 record.newline().a(" at ").a(relativize(source.getName()));
141 long line = diagnostic.getLineNumber();
142 long column = diagnostic.getColumnNumber();
143 if (line != Diagnostic.NOPOS || column != Diagnostic.NOPOS) {
144 record.style(style).a('[');
145 if (line != Diagnostic.NOPOS) {
146 record.a(line);
147 }
148 if (column != Diagnostic.NOPOS) {
149 record.a(',').a(column);
150 }
151 record.a(']').resetStyle();
152 }
153 }
154 String log = record.toString();
155 switch (kind) {
156 case ERROR:
157 if (firstError == null) {
158 firstError = message;
159 }
160 logger.error(log);
161 numErrors++;
162 break;
163 case MANDATORY_WARNING:
164 case WARNING:
165 logger.warn(log);
166 numWarnings++;
167 break;
168 default:
169 logger.info(log);
170 break;
171 }
172
173 String code = diagnostic.getCode();
174 if (code != null) {
175 codeCount.merge(code, 1, (old, initial) -> old + 1);
176 }
177 }
178
179
180
181
182
183
184 Optional<String> firstError(Exception cause) {
185 return Optional.ofNullable(cause != null && firstError == null ? cause.getMessage() : firstError);
186 }
187
188
189
190
191 void logSummary() {
192 MessageBuilder message = messageBuilderFactory.builder();
193 final String patternForCount;
194 if (!codeCount.isEmpty()) {
195 @SuppressWarnings("unchecked")
196 Map.Entry<String, Integer>[] entries = codeCount.entrySet().toArray(Map.Entry[]::new);
197 Arrays.sort(entries, (a, b) -> Integer.compare(b.getValue(), a.getValue()));
198 patternForCount = patternForCount(Math.max(entries[0].getValue(), Math.max(numWarnings, numErrors)));
199 message.strong("Summary of compiler messages:").newline();
200 for (Map.Entry<String, Integer> entry : entries) {
201 int count = entry.getValue();
202 message.format(patternForCount, count, entry.getKey()).newline();
203 }
204 } else {
205 patternForCount = patternForCount(Math.max(numWarnings, numErrors));
206 }
207 if ((numWarnings | numErrors) != 0) {
208 message.strong("Total:");
209 }
210 if (numWarnings != 0) {
211 writeCount(message, patternForCount, numWarnings, "warning");
212 }
213 if (numErrors != 0) {
214 writeCount(message, patternForCount, numErrors, "error");
215 }
216 logger.info(message.toString());
217 }
218
219
220
221
222
223
224 private static String patternForCount(int n) {
225 return " %" + Integer.toString(n).length() + "d %s";
226 }
227
228
229
230
231 private static void writeCount(MessageBuilder message, String patternForCount, int count, String name) {
232 message.newline();
233 message.format(patternForCount, count, name);
234 if (count > 1) {
235 message.append('s');
236 }
237 }
238 }