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