1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.changes;
20
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.ResourceBundle;
26
27 import org.apache.commons.lang3.StringUtils;
28 import org.apache.maven.doxia.sink.Sink;
29 import org.apache.maven.doxia.util.DoxiaUtils;
30 import org.apache.maven.plugins.changes.issues.AbstractIssuesReportRenderer;
31 import org.apache.maven.plugins.changes.model.Action;
32 import org.apache.maven.plugins.changes.model.Component;
33 import org.apache.maven.plugins.changes.model.DueTo;
34 import org.apache.maven.plugins.changes.model.Release;
35
36
37
38
39
40
41 public class ChangesReportRenderer extends AbstractIssuesReportRenderer {
42
43
44
45
46 private static final String URL_TOKEN = "%URL%";
47
48
49
50
51 private static final String ISSUE_TOKEN = "%ISSUE%";
52
53 static final String DEFAULT_ISSUE_SYSTEM_KEY = "default";
54
55 private static final String NO_TEAM = "none";
56
57 private final ChangesXML changesXML;
58
59
60
61
62
63
64 private String system;
65
66 private String team;
67
68 private String url;
69
70 private Map<String, String> issueLinksPerSystem;
71
72 private boolean addActionDate;
73
74 private boolean linkToFeed;
75
76 public ChangesReportRenderer(Sink sink, ResourceBundle bundleName, ChangesXML changesXML) {
77 super(sink, bundleName);
78 this.issueLinksPerSystem = new HashMap<>();
79 this.changesXML = changesXML;
80 }
81
82 public void setSystem(String system) {
83 this.system = system;
84 }
85
86 public void setTeam(final String team) {
87 this.team = team;
88 }
89
90 public void setUrl(String url) {
91 this.url = url;
92 }
93
94 public void setIssueLinksPerSystem(Map<String, String> issueLinksPerSystem) {
95 if (this.issueLinksPerSystem != null && issueLinksPerSystem == null) {
96 return;
97 }
98 this.issueLinksPerSystem = issueLinksPerSystem;
99 }
100
101 public void setAddActionDate(boolean addActionDate) {
102 this.addActionDate = addActionDate;
103 }
104
105 public void setLinkToFeed(boolean generateLinkTofeed) {
106 this.linkToFeed = generateLinkTofeed;
107 }
108
109
110
111
112
113
114
115 private boolean canGenerateIssueLinks(String system) {
116 if (!this.issueLinksPerSystem.containsKey(system)) {
117 return false;
118 }
119 String issueLink = this.issueLinksPerSystem.get(system);
120
121
122 if (StringUtils.isBlank(issueLink)) {
123 return false;
124 }
125
126
127 if (issueLink.contains(URL_TOKEN) && StringUtils.isBlank(url)) {
128 return false;
129 }
130 return true;
131 }
132
133 @Override
134 protected void renderBody() {
135 constructReleaseHistory();
136 constructReleases();
137 }
138
139 @Override
140 public String getTitle() {
141 String title = changesXML.getTitle();
142 if (title == null) {
143 title = bundle.getString("report.issues.header");
144 }
145 return title;
146 }
147
148
149
150
151
152
153 private void constructAction(Action action) {
154 sink.tableRow();
155
156 sinkShowTypeIcon(action.getType());
157
158 sink.tableCell();
159
160 String actionDescription = action.getAction();
161
162 text(actionDescription);
163
164
165 if (StringUtils.isNotEmpty(action.getIssue())
166 || (!action.getFixedIssues().isEmpty())) {
167 if (StringUtils.isNotBlank(actionDescription) && !actionDescription.endsWith(".")) {
168 text(".");
169 }
170 text(" " + bundle.getString("report.changes.text.fixes") + " ");
171
172
173 String system = action.getSystem();
174
175 if (StringUtils.isEmpty(system)) {
176 system = this.system;
177 }
178
179 if (StringUtils.isEmpty(system)) {
180 system = DEFAULT_ISSUE_SYSTEM_KEY;
181 }
182 if (!canGenerateIssueLinks(system)) {
183 constructIssueText(action.getIssue(), action.getFixedIssues());
184 } else {
185 constructIssueLink(action.getIssue(), system, action.getFixedIssues());
186 }
187 text(".");
188 }
189
190 if (!action.getDueTos().isEmpty()) {
191 constructDueTo(action);
192 }
193
194 sink.tableCell_();
195
196 if (NO_TEAM.equals(team)) {
197 sinkCell(action.getDev());
198 } else {
199 sinkCellLink(action.getDev(), team + "#" + action.getDev());
200 }
201
202 if (addActionDate) {
203 sinkCell(action.getDate());
204 }
205
206 sink.tableRow_();
207 }
208
209
210
211
212
213
214 private void constructDueTo(Action action) {
215
216 text(" " + bundle.getString("report.changes.text.thanx") + " ");
217 int i = 0;
218 for (DueTo dueTo : action.getDueTos()) {
219 i++;
220
221 if (StringUtils.isNotEmpty(dueTo.getEmail())) {
222 String text = dueTo.getName();
223 link("mailto:" + dueTo.getEmail(), text);
224 } else {
225 text(dueTo.getName());
226 }
227
228 if (i < action.getDueTos().size()) {
229 text(", ");
230 }
231 }
232
233 text(".");
234 }
235
236
237
238
239
240
241
242
243 private void constructIssueLink(String issue, String system, List<String> fixes) {
244 if (StringUtils.isNotEmpty(issue)) {
245 link(parseIssueLink(issue, system), issue);
246 if (!fixes.isEmpty()) {
247 text(", ");
248 }
249 }
250
251 for (Iterator<String> iterator = fixes.iterator(); iterator.hasNext(); ) {
252 String currentIssueId = iterator.next();
253 if (StringUtils.isNotEmpty(currentIssueId)) {
254 link(parseIssueLink(currentIssueId, system), currentIssueId);
255 }
256
257 if (iterator.hasNext()) {
258 text(", ");
259 }
260 }
261 }
262
263
264
265
266
267
268
269 private void constructIssueText(String issue, List<String> fixes) {
270 if (StringUtils.isNotEmpty(issue)) {
271 text(issue);
272
273 if (!fixes.isEmpty()) {
274 text(", ");
275 }
276 }
277
278 for (Iterator<String> iterator = fixes.iterator(); iterator.hasNext(); ) {
279 String currentIssueId = iterator.next();
280 if (StringUtils.isNotEmpty(currentIssueId)) {
281 text(currentIssueId);
282 }
283
284 if (iterator.hasNext()) {
285 text(", ");
286 }
287 }
288 }
289
290 private void constructReleaseHistory() {
291 startSection(bundle.getString("report.changes.label.releasehistory"));
292
293 startTable();
294
295 tableHeader(new String[] {
296 bundle.getString("report.issues.label.fixVersion"),
297 bundle.getString("report.changes.label.releaseDate"),
298 bundle.getString("report.changes.label.releaseDescription")
299 });
300
301 for (Release release : changesXML.getReleaseList()) {
302 sink.tableRow();
303 sinkCellLink(release.getVersion(), "#" + DoxiaUtils.encodeId(release.getVersion()));
304 sinkCell(release.getDateRelease());
305 sinkCell(release.getDescription());
306 sink.tableRow_();
307 }
308
309 endTable();
310
311
312 if (linkToFeed) {
313 sink.paragraph();
314 text(bundle.getString("report.changes.text.rssfeed"));
315 sink.nonBreakingSpace();
316 sink.link("changes.rss");
317 sinkFigure("images/rss.png", "rss feed");
318 sink.link_();
319 sink.paragraph_();
320 }
321
322 endSection();
323 }
324
325
326
327
328 private void constructReleases() {
329 for (Release release : changesXML.getReleaseList()) {
330 constructRelease(release);
331 }
332 }
333
334
335
336
337
338
339 private void constructRelease(Release release) {
340
341 final String date = (release.getDateRelease() == null) ? "" : " \u2013 " + release.getDateRelease();
342
343 startSection(
344 bundle.getString("report.changes.label.release") + " " + release.getVersion() + date,
345 DoxiaUtils.encodeId(release.getVersion()));
346
347 if (isReleaseEmpty(release)) {
348 sink.paragraph();
349 text(bundle.getString("report.changes.text.no.changes"));
350 sink.paragraph_();
351 } else {
352 startTable();
353
354 sink.tableRow();
355 tableHeaderCell(bundle.getString("report.issues.label.type"));
356 tableHeaderCell(bundle.getString("report.issues.label.summary"));
357 tableHeaderCell(bundle.getString("report.issues.label.assignee"));
358 if (addActionDate) {
359 tableHeaderCell(bundle.getString("report.issues.label.updated"));
360 }
361 sink.tableRow_();
362
363 for (Action action : release.getActions()) {
364 constructAction(action);
365 }
366
367 for (Component component : release.getComponents()) {
368 constructComponent(component);
369 }
370
371 endTable();
372 }
373
374 endSection();
375 }
376
377
378
379
380
381
382
383 private void constructComponent(Component component) {
384 if (!component.getActions().isEmpty()) {
385 sink.tableRow();
386
387 sink.tableHeaderCell();
388 sink.tableHeaderCell_();
389
390 sink.tableHeaderCell();
391 text(component.getName());
392 sink.tableHeaderCell_();
393
394 sink.tableHeaderCell();
395 sink.tableHeaderCell_();
396
397 if (addActionDate) {
398 sink.tableHeaderCell();
399 sink.tableHeaderCell_();
400 }
401
402 sink.tableRow_();
403
404 for (Action action : component.getActions()) {
405 constructAction(action);
406 }
407 }
408 }
409
410
411
412
413
414
415
416 private boolean isReleaseEmpty(Release release) {
417 if (!release.getActions().isEmpty()) {
418 return false;
419 }
420
421 for (Component component : release.getComponents()) {
422 if (!component.getActions().isEmpty()) {
423 return false;
424 }
425 }
426
427 return true;
428 }
429
430
431
432
433
434
435
436
437 private String parseIssueLink(String issue, String system) {
438 String parseLink;
439 String issueLink = this.issueLinksPerSystem.get(system);
440 parseLink = issueLink.replaceFirst(ISSUE_TOKEN, issue);
441 if (parseLink.contains(URL_TOKEN)) {
442 String url = this.url.substring(0, this.url.lastIndexOf("/"));
443 parseLink = parseLink.replaceFirst(URL_TOKEN, url);
444 }
445
446 return parseLink;
447 }
448 }