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