1 package org.apache.maven.plugin.changes;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.LinkedHashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.ResourceBundle;
28
29 import org.apache.commons.lang.StringUtils;
30 import org.apache.maven.doxia.sink.Sink;
31 import org.apache.maven.doxia.util.HtmlTools;
32 import org.apache.maven.plugin.issues.AbstractIssuesReportGenerator;
33 import org.apache.maven.plugins.changes.model.Action;
34 import org.apache.maven.plugins.changes.model.Component;
35 import org.apache.maven.plugins.changes.model.DueTo;
36 import org.apache.maven.plugins.changes.model.FixedIssue;
37 import org.apache.maven.plugins.changes.model.Release;
38
39
40
41
42
43
44 public class ChangesReportGenerator extends AbstractIssuesReportGenerator
45 {
46
47
48
49
50 private static final String URL_TOKEN = "%URL%";
51
52
53
54
55 private static final String ISSUE_TOKEN = "%ISSUE%";
56
57 static final String DEFAULT_ISSUE_SYSTEM_KEY = "default";
58
59 private static final String NO_TEAMLIST = "none";
60
61
62
63
64
65
66
67 private String system;
68
69 private String teamlist;
70
71 private String url;
72
73 private Map issueLinksPerSystem;
74
75 private boolean addActionDate;
76
77
78
79
80 private boolean escapeHTML;
81
82
83
84
85 private List releaseList;
86
87 public ChangesReportGenerator()
88 {
89 issueLinksPerSystem = new HashMap();
90 }
91
92 public ChangesReportGenerator( List releaseList )
93 {
94 this();
95 this.releaseList = releaseList;
96 }
97
98
99
100
101 public boolean isEscapeHTML()
102 {
103 return escapeHTML;
104 }
105
106
107
108
109 public void setEscapeHTML( boolean escapeHTML )
110 {
111 this.escapeHTML = escapeHTML;
112 }
113
114
115
116
117 public String getSystem()
118 {
119 return system;
120 }
121
122
123
124
125 public void setSystem( String system )
126 {
127 this.system = system;
128 }
129
130 public void setTeamlist( final String teamlist )
131 {
132 this.teamlist = teamlist;
133 }
134
135 public String getTeamlist()
136 {
137 return teamlist;
138 }
139
140 public void setUrl( String url )
141 {
142 this.url = url;
143 }
144
145 public String getUrl()
146 {
147 return url;
148 }
149
150 public Map getIssueLinksPerSystem()
151 {
152 return issueLinksPerSystem;
153 }
154
155 public void setIssueLinksPerSystem( Map issueLinksPerSystem )
156 {
157 if ( this.issueLinksPerSystem != null && issueLinksPerSystem == null )
158 {
159 return;
160 }
161 this.issueLinksPerSystem = issueLinksPerSystem;
162 }
163
164 public boolean isAddActionDate()
165 {
166 return addActionDate;
167 }
168
169 public void setAddActionDate( boolean addActionDate )
170 {
171 this.addActionDate = addActionDate;
172 }
173
174
175
176
177
178
179
180 public boolean canGenerateIssueLinks( String system )
181 {
182 if ( !this.issueLinksPerSystem.containsKey( system ) )
183 {
184 return false;
185 }
186 String issueLink = (String) this.issueLinksPerSystem.get( system );
187
188
189 if ( StringUtils.isBlank( issueLink ) )
190 {
191 return false;
192 }
193
194
195 if ( issueLink.indexOf( URL_TOKEN ) >= 0 && StringUtils.isBlank( getUrl() ) )
196 {
197 return false;
198 }
199 return true;
200 }
201
202 public void doGenerateEmptyReport( ResourceBundle bundle, Sink sink, String message )
203 {
204 sinkBeginReport( sink, bundle );
205
206 sink.text( message );
207
208 sinkEndReport( sink );
209 }
210
211 public void doGenerateReport( ResourceBundle bundle, Sink sink )
212 {
213 sinkBeginReport( sink, bundle );
214
215 constructReleaseHistory(sink, bundle, releaseList);
216
217 constructReleases(sink, bundle, releaseList);
218
219 sinkEndReport( sink );
220 }
221
222
223
224
225
226
227
228
229 private void constructAction( Sink sink, ResourceBundle bundle, Action action )
230 {
231 sink.tableRow();
232
233 sinkShowTypeIcon(sink, action.getType());
234
235 sink.tableCell();
236
237 if ( escapeHTML )
238 {
239 sink.text( action.getAction() );
240 }
241 else
242 {
243 sink.rawText( action.getAction() );
244 }
245
246
247 if ( StringUtils.isNotEmpty( action.getIssue() ) || ( !action.getFixedIssues().isEmpty() ) )
248 {
249 sink.text( " " + bundle.getString( "report.changes.text.fixes" ) + " " );
250
251
252 String system = action.getSystem();
253
254 if ( StringUtils.isEmpty( system ) )
255 {
256 system = this.system;
257 }
258
259 if ( StringUtils.isEmpty( system ) )
260 {
261 system = DEFAULT_ISSUE_SYSTEM_KEY;
262 }
263 if ( !canGenerateIssueLinks( system ) )
264 {
265 constructIssueText( action.getIssue(), sink, action.getFixedIssues() );
266 }
267 else
268 {
269 constructIssueLink( action.getIssue(), system, sink, action.getFixedIssues() );
270 }
271 sink.text( "." );
272 }
273
274 if ( StringUtils.isNotEmpty( action.getDueTo() ) || ( !action.getDueTos().isEmpty() ) )
275 {
276 constructDueTo( sink, action, bundle, action.getDueTos() );
277 }
278
279 sink.tableCell_();
280
281 if ( NO_TEAMLIST.equals( teamlist ) )
282 {
283 sinkCell( sink, action.getDev() );
284 }
285 else
286 {
287 sinkCellLink( sink, action.getDev(), teamlist + "#" + action.getDev() );
288 }
289
290 if ( this.isAddActionDate() )
291 {
292 sinkCell( sink, action.getDate() );
293 }
294
295 sink.tableRow_();
296 }
297
298
299
300
301
302
303
304
305
306 private void constructDueTo( Sink sink, Action action, ResourceBundle bundle, List dueTos )
307 {
308
309
310 Map<String,String> namesEmailMap = new LinkedHashMap<String,String>();
311
312
313 if ( StringUtils.isNotEmpty( action.getDueTo() ) || StringUtils.isNotEmpty( action.getDueToEmail() ) )
314 {
315 namesEmailMap.put(action.getDueTo(), action.getDueToEmail());
316 }
317
318 for ( Iterator iterator = dueTos.iterator(); iterator.hasNext(); )
319 {
320 DueTo dueTo = (DueTo) iterator.next();
321 namesEmailMap.put( dueTo.getName(), dueTo.getEmail() );
322 }
323
324 if ( namesEmailMap.isEmpty() )
325 {
326 return;
327 }
328
329 sink.text( " " + bundle.getString( "report.changes.text.thanx" ) + " " );
330 int i = 0;
331 for ( String currentDueTo : namesEmailMap.keySet() )
332 {
333 String currentDueToEmail = namesEmailMap.get( currentDueTo );
334 i++;
335
336 if ( StringUtils.isNotEmpty( currentDueToEmail ) )
337 {
338 sinkLink( sink, currentDueTo, "mailto:" + currentDueToEmail );
339 }
340 else if ( StringUtils.isNotEmpty( currentDueTo ) )
341 {
342 sink.text( currentDueTo );
343 }
344
345 if ( i < namesEmailMap.size() )
346 {
347 sink.text( ", " );
348 }
349 }
350
351 sink.text(".");
352 }
353
354
355
356
357
358
359
360
361
362 private void constructIssueLink( String issue, String system, Sink sink, List fixes )
363 {
364 if ( StringUtils.isNotEmpty( issue ) )
365 {
366 sink.link( parseIssueLink( issue, system ) );
367
368 sink.text( issue );
369
370 sink.link_();
371
372 if ( !fixes.isEmpty() )
373 {
374 sink.text( ", " );
375 }
376 }
377
378 for ( Iterator iterator = fixes.iterator(); iterator.hasNext(); )
379 {
380 FixedIssue fixedIssue = (FixedIssue) iterator.next();
381 String currentIssueId = fixedIssue.getIssue();
382 if ( StringUtils.isNotEmpty( currentIssueId ) )
383 {
384 sink.link( parseIssueLink( currentIssueId, system ) );
385
386 sink.text( currentIssueId );
387
388 sink.link_();
389 }
390
391 if ( iterator.hasNext() )
392 {
393 sink.text( ", " );
394 }
395 }
396 }
397
398
399
400
401
402
403
404
405
406 private void constructIssueText( String issue, Sink sink, List fixes )
407 {
408 if ( StringUtils.isNotEmpty( issue ) )
409 {
410 sink.text(issue);
411
412 if ( !fixes.isEmpty() )
413 {
414 sink.text( ", " );
415 }
416 }
417
418 for ( Iterator iterator = fixes.iterator(); iterator.hasNext(); )
419 {
420 FixedIssue fixedIssue = (FixedIssue) iterator.next();
421
422 String currentIssueId = fixedIssue.getIssue();
423 if ( StringUtils.isNotEmpty( currentIssueId ) )
424 {
425 sink.text(currentIssueId);
426 }
427
428 if ( iterator.hasNext() )
429 {
430 sink.text( ", " );
431 }
432 }
433 }
434
435 private void constructReleaseHistory( Sink sink, ResourceBundle bundle, List releaseList )
436 {
437 sink.section2();
438
439 sinkSectionTitle2Anchor( sink, bundle.getString( "report.changes.label.releasehistory" ),
440 bundle.getString( "report.changes.label.releasehistory" ) );
441
442 sink.table();
443
444 sink.tableRow();
445
446 sinkHeader( sink, bundle.getString( "report.issues.label.fixVersion" ) );
447
448 sinkHeader( sink, bundle.getString( "report.changes.label.releaseDate" ) );
449
450 sinkHeader( sink, bundle.getString( "report.changes.label.releaseDescription" ) );
451
452 sink.tableRow_();
453
454 for ( int idx = 0; idx < releaseList.size(); idx++ )
455 {
456 Release release = (Release) releaseList.get( idx );
457
458 sink.tableRow();
459
460 sinkCellLink( sink, release.getVersion(), "#" + HtmlTools.encodeId( release.getVersion() ) );
461
462 sinkCell( sink, release.getDateRelease() );
463
464 sinkCell( sink, release.getDescription() );
465
466 sink.tableRow_();
467 }
468
469 sink.table_();
470
471
472
473
474
475
476
477
478
479
480 sink.section2_();
481 }
482
483
484
485
486
487
488
489
490 private void constructReleases( Sink sink, ResourceBundle bundle, List releaseList )
491 {
492 for ( int idx = 0; idx < releaseList.size(); idx++ )
493 {
494 Release release = (Release) releaseList.get(idx);
495 constructRelease( sink, bundle, release );
496 }
497 }
498
499
500
501
502
503
504
505
506 private void constructRelease( Sink sink, ResourceBundle bundle, Release release )
507 {
508 sink.section2();
509
510 final String date = ( release.getDateRelease() == null ) ? "" : " - " + release.getDateRelease();
511
512 sinkSectionTitle2Anchor(sink, bundle.getString("report.changes.label.release") + " "
513 + release.getVersion() + date, release.getVersion());
514
515 if ( isReleaseEmpty( release ) )
516 {
517 sink.paragraph();
518 sink.text( bundle.getString("report.changes.text.no.changes") );
519 sink.paragraph_();
520 }
521 else
522 {
523 sink.table();
524
525 sink.tableRow();
526 sinkHeader( sink, bundle.getString( "report.issues.label.type" ) );
527 sinkHeader( sink, bundle.getString( "report.issues.label.summary" ) );
528 sinkHeader(sink, bundle.getString("report.issues.label.assignee"));
529 if ( this.isAddActionDate() )
530 {
531 sinkHeader( sink, bundle.getString( "report.issues.label.updated" ) );
532 }
533 sink.tableRow_();
534
535 for (Iterator iterator = release.getActions().iterator(); iterator.hasNext();)
536 {
537 Action action = (Action) iterator.next();
538 constructAction(sink, bundle, action);
539 }
540
541 for (Iterator iterator = release.getComponents().iterator(); iterator.hasNext();)
542 {
543 Component component = (Component) iterator.next();
544 constructComponent( sink, bundle, component );
545 }
546
547 sink.table_();
548
549 sink.section2_();
550 }
551 }
552
553
554
555
556
557
558
559
560
561 private void constructComponent( Sink sink, ResourceBundle bundle, Component component )
562 {
563 if ( !component.getActions().isEmpty() )
564 {
565 sink.tableRow();
566
567 sink.tableHeaderCell();
568 sink.tableHeaderCell_();
569
570 sink.tableHeaderCell();
571 sink.text(component.getName());
572 sink.tableHeaderCell_();
573
574 sink.tableHeaderCell();
575 sink.tableHeaderCell_();
576
577 if ( isAddActionDate() )
578 {
579 sink.tableHeaderCell();
580 sink.tableHeaderCell_();
581 }
582
583 sink.tableRow_();
584
585 for ( Iterator iterator = component.getActions().iterator(); iterator.hasNext(); )
586 {
587 Action action = (Action) iterator.next();
588 constructAction( sink, bundle, action );
589 }
590 }
591 }
592
593
594
595
596
597
598
599 private boolean isReleaseEmpty( Release release ) {
600 if ( !release.getActions().isEmpty() )
601 {
602 return false;
603 }
604
605 for (Iterator iterator = release.getComponents().iterator(); iterator.hasNext();)
606 {
607 Component component = (Component) iterator.next();
608 if ( !component.getActions().isEmpty() )
609 {
610 return false;
611 }
612 }
613
614 return true;
615 }
616
617
618
619
620
621
622
623
624 private String parseIssueLink( String issue, String system )
625 {
626 String parseLink;
627 String issueLink = (String) this.issueLinksPerSystem.get( system );
628 parseLink = issueLink.replaceFirst( ISSUE_TOKEN, issue );
629 if ( parseLink.indexOf( URL_TOKEN ) >= 0 )
630 {
631 String url = this.url.substring( 0, this.url.lastIndexOf( "/" ) );
632 parseLink = parseLink.replaceFirst( URL_TOKEN, url );
633 }
634
635 return parseLink;
636 }
637
638 }