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