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.rawText( 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 = getUrl();
347
348 if ( url.endsWith( "/" ) )
349 {
350 url = url.substring( 0, url.length() - 1 );
351 }
352 parseLink = parseLink.replaceFirst( URL_TOKEN, url );
353 }
354
355 return parseLink;
356 }
357
358 private void sinkBeginReport( Sink sink, ResourceBundle bundle )
359 {
360 sink.head();
361 String title = null;
362 if ( report.getTitle() != null )
363 {
364 title = report.getTitle();
365 }
366 else
367 {
368 title = bundle.getString( "report.changes.header" );
369 }
370 sink.title();
371 sink.text( title );
372 sink.title_();
373
374 if ( StringUtils.isNotEmpty( report.getAuthor() ) )
375 {
376 sink.author();
377 sink.text( report.getAuthor() );
378 sink.author_();
379 }
380
381 sink.head_();
382
383 sink.body();
384
385 sink.section1();
386
387 sinkSectionTitle1Anchor( sink, title, title );
388 }
389
390 private void sinkCell( Sink sink, String text )
391 {
392 sink.tableCell();
393
394 sink.text( text );
395
396 sink.tableCell_();
397 }
398
399 private void sinkCellLink( Sink sink, String text, String link )
400 {
401 sink.tableCell();
402
403 sinkLink( sink, text, link );
404
405 sink.tableCell_();
406 }
407
408 private void sinkEndReport( Sink sink )
409 {
410 sink.section1_();
411
412 sink.body_();
413
414 sink.flush();
415
416 sink.close();
417 }
418
419 private void sinkFigure( String image, Sink sink, String altText )
420 {
421 sink.figure();
422
423 sink.figureGraphics( image );
424
425 sink.figureCaption();
426
427 sink.text( altText );
428
429 sink.figureCaption_();
430
431 sink.figure_();
432 }
433
434 private void sinkHeader( Sink sink, String header )
435 {
436 sink.tableHeaderCell();
437
438 sink.text( header );
439
440 sink.tableHeaderCell_();
441 }
442
443 private void sinkLink( Sink sink, String text, String link )
444 {
445 sink.link( link );
446
447 sink.text( text );
448
449 sink.link_();
450 }
451
452 private void sinkSectionTitle1Anchor( Sink sink, String text, String anchor )
453 {
454 sink.sectionTitle1();
455 sink.text( text );
456 sink.sectionTitle1_();
457
458 sink.anchor( anchor );
459 sink.anchor_();
460 }
461
462 private void sinkSectionTitle2Anchor( Sink sink, String text, String anchor )
463 {
464 sink.sectionTitle2();
465 sink.text( text );
466 sink.sectionTitle2_();
467
468 sink.anchor( anchor );
469 sink.anchor_();
470 }
471
472 private void sinkShowTypeIcon( Sink sink, String type )
473 {
474 String image = "";
475 String altText = "";
476
477 if ( type == null )
478 {
479 image = "images/icon_help_sml.gif";
480 altText = "?";
481 }
482 else if ( type.equals( "fix" ) )
483 {
484 image = "images/fix.gif";
485 altText = "fix";
486 }
487 else if ( type.equals( "update" ) )
488 {
489 image = "images/update.gif";
490 altText = "update";
491 }
492 else if ( type.equals( "add" ) )
493 {
494 image = "images/add.gif";
495 altText = "add";
496 }
497 else if ( type.equals( "remove" ) )
498 {
499 image = "images/remove.gif";
500 altText = "remove";
501 }
502
503 sink.tableCell();
504
505 sinkFigure( image, sink, altText );
506
507 sink.tableCell_();
508 }
509
510
511
512
513
514 private void constructIssueLink( String issue, String system, Sink sink, List fixes )
515 {
516 if ( StringUtils.isNotEmpty( issue ) )
517 {
518 sink.link( parseIssueLink( issue, system ) );
519
520 sink.text( issue );
521
522 sink.link_();
523
524 if ( !fixes.isEmpty() )
525 {
526 sink.text( ", " );
527 }
528 }
529
530 for ( Iterator iterator = fixes.iterator(); iterator.hasNext(); )
531 {
532 FixedIssue fixedIssue = (FixedIssue) iterator.next();
533 String currentIssueId = fixedIssue.getIssue();
534 if ( StringUtils.isNotEmpty( currentIssueId ) )
535 {
536 sink.link( parseIssueLink( currentIssueId, system ) );
537
538 sink.text( currentIssueId );
539
540 sink.link_();
541 }
542
543 if ( iterator.hasNext() )
544 {
545 sink.text( ", " );
546 }
547 }
548 }
549
550
551
552
553
554 private void constructIssueText( String issue, Sink sink, List fixes )
555 {
556 if ( StringUtils.isNotEmpty( issue ) )
557 {
558 sink.text( issue );
559
560 if ( !fixes.isEmpty() )
561 {
562 sink.text( ", " );
563 }
564 }
565
566 for ( Iterator iterator = fixes.iterator(); iterator.hasNext(); )
567 {
568 FixedIssue fixedIssue = (FixedIssue) iterator.next();
569
570 String currentIssueId = fixedIssue.getIssue();
571 if ( StringUtils.isNotEmpty( currentIssueId ) )
572 {
573 sink.text( currentIssueId );
574 }
575
576 if ( iterator.hasNext() )
577 {
578 sink.text( ", " );
579 }
580 }
581 }
582
583
584
585
586
587
588 private void constructDueTo( Sink sink, Action action, ResourceBundle bundle, List dueTos )
589 {
590
591
592 Map namesEmailMap = new LinkedHashMap();
593
594
595 if ( StringUtils.isNotEmpty( action.getDueTo() ) || StringUtils.isNotEmpty( action.getDueToEmail() ) )
596 {
597 namesEmailMap.put( action.getDueTo(), action.getDueToEmail() );
598 }
599
600 for ( Iterator iterator = dueTos.iterator(); iterator.hasNext(); )
601 {
602 DueTo dueTo = (DueTo) iterator.next();
603 namesEmailMap.put( dueTo.getName(), dueTo.getEmail() );
604 }
605
606 if ( namesEmailMap.isEmpty() )
607 {
608 return;
609 }
610
611 sink.text( " " + bundle.getString( "report.changes.text.thanx" ) + " " );
612 int i = 0;
613 for ( Iterator iterator = namesEmailMap.keySet().iterator(); iterator.hasNext(); )
614 {
615 String currentDueTo = (String) iterator.next();
616 String currentDueToEmail = (String) namesEmailMap.get( currentDueTo );
617 i++;
618
619 if ( StringUtils.isNotEmpty( currentDueToEmail ) )
620 {
621 sinkLink( sink, currentDueTo, "mailto:" + currentDueToEmail );
622 }
623 else if ( StringUtils.isNotEmpty( currentDueTo ) )
624 {
625 sink.text( currentDueTo );
626 }
627
628 if ( i < namesEmailMap.size() )
629 {
630 sink.text( ", " );
631 }
632 }
633
634 sink.text( "." );
635 }
636
637 }