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.io.IOException;
24 import java.net.URL;
25 import java.text.SimpleDateFormat;
26 import java.util.Collections;
27 import java.util.Date;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.Properties;
33 import java.util.ResourceBundle;
34
35 import org.apache.commons.collections.map.CaseInsensitiveMap;
36 import org.apache.maven.execution.MavenSession;
37 import org.apache.maven.project.MavenProject;
38 import org.apache.maven.reporting.MavenReportException;
39 import org.apache.maven.shared.filtering.MavenFileFilter;
40 import org.apache.maven.shared.filtering.MavenFileFilterRequest;
41 import org.apache.maven.shared.filtering.MavenFilteringException;
42 import org.codehaus.plexus.util.FileUtils;
43 import org.codehaus.plexus.util.IOUtil;
44 import org.codehaus.plexus.util.ReaderFactory;
45 import org.codehaus.plexus.util.StringUtils;
46 import org.codehaus.plexus.util.xml.XmlStreamReader;
47
48
49
50
51
52
53
54
55
56 public class ChangesMojo
57 extends AbstractChangesReport
58 {
59
60
61
62
63
64
65
66 private boolean aggregated;
67
68
69
70
71
72
73
74
75 private boolean addActionDate;
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95 private boolean escapeHTML;
96
97
98
99
100
101
102
103
104
105 private File filteredOutputDirectory;
106
107
108
109
110
111
112
113 private boolean filteringChanges;
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 private String issueLinkTemplate;
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153 private Map issueLinkTemplatePerSystem;
154
155
156
157
158
159 private MavenFileFilter mavenFileFilter;
160
161
162
163
164
165
166
167
168 private String publishDateFormat;
169
170
171
172
173
174
175
176
177 private String publishDateLocale;
178
179
180
181
182
183
184
185 protected MavenSession session;
186
187
188
189
190
191
192 private String system;
193
194
195
196
197
198
199
200
201 private String teamlist;
202
203
204
205
206
207 private String url;
208
209
210
211
212
213
214 private File xmlPath;
215
216 private ReleaseUtils releaseUtils = new ReleaseUtils( getLog() );
217
218 private CaseInsensitiveMap caseInsensitiveIssueLinkTemplatePerSystem;
219
220
221
222
223
224 public boolean canGenerateReport()
225 {
226 return xmlPath.isFile();
227 }
228
229 public void executeReport( Locale locale )
230 throws MavenReportException
231 {
232 Date now = new Date();
233 SimpleDateFormat simpleDateFormat =
234 new SimpleDateFormat(publishDateFormat, new Locale(publishDateLocale));
235 Properties additionalProperties = new Properties();
236 additionalProperties.put("publishDate", simpleDateFormat.format(now));
237
238 ChangesXML changesXml = getChangesFromFile( xmlPath, project, additionalProperties);
239 if ( changesXml == null ) return;
240
241 if ( aggregated )
242 {
243 final String basePath = project.getBasedir().getAbsolutePath();
244 final String absolutePath = xmlPath.getAbsolutePath();
245 if ( !absolutePath.startsWith( basePath ) )
246 {
247 getLog().warn( "xmlPath should be within the project dir for aggregated changes report." );
248 return;
249 }
250 final String relativePath = absolutePath.substring( basePath.length() );
251
252 List releaseList = changesXml.getReleaseList();
253 for ( Iterator iterator = project.getCollectedProjects().iterator(); iterator.hasNext(); )
254 {
255 final MavenProject childProject = (MavenProject) iterator.next();
256 final File changesFile = new File( childProject.getBasedir(), relativePath );
257 final ChangesXML childXml = getChangesFromFile( changesFile, childProject, additionalProperties );
258 if ( childXml != null )
259 {
260 releaseList = releaseUtils.mergeReleases( releaseList, childProject.getName(), childXml.getReleaseList() );
261 }
262 }
263 changesXml.setReleaseList( releaseList );
264 }
265
266 ChangesReportGenerator report = new ChangesReportGenerator( changesXml.getReleaseList() );
267
268 report.setAuthor( changesXml.getAuthor() );
269 report.setTitle( changesXml.getTitle() );
270
271 report.setEscapeHTML ( escapeHTML );
272
273
274
275 if ( issueLinkTemplatePerSystem == null )
276 {
277 caseInsensitiveIssueLinkTemplatePerSystem = new CaseInsensitiveMap();
278 }
279 else
280 {
281 caseInsensitiveIssueLinkTemplatePerSystem = new CaseInsensitiveMap( issueLinkTemplatePerSystem );
282 }
283
284
285
286 addIssueLinkTemplate( ChangesReportGenerator.DEFAULT_ISSUE_SYSTEM_KEY, issueLinkTemplate );
287 addIssueLinkTemplate( "Bitbucket", "%URL%/issue/%ISSUE%" );
288 addIssueLinkTemplate( "Bugzilla", "%URL%/show_bug.cgi?id=%ISSUE%" );
289 addIssueLinkTemplate( "GitHub", "%URL%/%ISSUE%" );
290 addIssueLinkTemplate( "GoogleCode", "%URL%/detail?id=%ISSUE%" );
291 addIssueLinkTemplate( "JIRA", "%URL%/%ISSUE%" );
292 addIssueLinkTemplate( "Mantis", "%URL%/view.php?id=%ISSUE%" );
293 addIssueLinkTemplate( "MKS", "%URL%/viewissue?selection=%ISSUE%" );
294 addIssueLinkTemplate( "Redmine", "%URL%/issues/show/%ISSUE%" );
295 addIssueLinkTemplate( "Scarab", "%URL%/issues/id/%ISSUE%" );
296 addIssueLinkTemplate( "SourceForge", "http://sourceforge.net/support/tracker.php?aid=%ISSUE%" );
297 addIssueLinkTemplate( "Trac", "%URL%/ticket/%ISSUE%" );
298 addIssueLinkTemplate( "Trackplus", "%URL%/printItem.action?key=%ISSUE%" );
299 addIssueLinkTemplate( "YouTrack", "%URL%/issue/%ISSUE%" );
300
301
302
303
304 logIssueLinkTemplatePerSystem( caseInsensitiveIssueLinkTemplatePerSystem );
305
306 report.setIssueLinksPerSystem( caseInsensitiveIssueLinkTemplatePerSystem );
307
308 report.setSystem( system );
309
310 report.setTeamlist ( teamlist );
311
312 report.setUrl( url );
313
314 report.setAddActionDate( addActionDate );
315
316 if ( StringUtils.isEmpty( url ) )
317 {
318 getLog().warn( "No issue management URL defined in POM. Links to your issues will not work correctly." );
319 }
320
321 report.doGenerateReport( getBundle( locale ), getSink() );
322
323
324 copyStaticResources();
325 }
326
327 public String getDescription( Locale locale )
328 {
329 return getBundle( locale ).getString( "report.issues.description" );
330 }
331
332 public String getName( Locale locale )
333 {
334 return getBundle( locale ).getString( "report.issues.name" );
335 }
336
337 public String getOutputName()
338 {
339 return "changes-report";
340 }
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356 private ChangesXML getChangesFromFile( File changesXml, MavenProject project, Properties additionalProperties )
357 throws MavenReportException
358 {
359 if ( !changesXml.exists() )
360 {
361 getLog().warn( "changes.xml file " + changesXml.getAbsolutePath() + " does not exist." );
362 return null;
363 }
364
365 if ( filteringChanges )
366 {
367 if ( !filteredOutputDirectory.exists() )
368 {
369 filteredOutputDirectory.mkdirs();
370 }
371 XmlStreamReader xmlStreamReader = null;
372 try
373 {
374
375 xmlStreamReader = ReaderFactory.newXmlReader( changesXml );
376 String encoding = xmlStreamReader.getEncoding();
377 File resultFile = new File( filteredOutputDirectory, project.getGroupId() + "." + project.getArtifactId() + "-changes.xml" );
378
379 final MavenFileFilterRequest mavenFileFilterRequest =
380 new MavenFileFilterRequest( changesXml, resultFile, true, project, Collections.EMPTY_LIST, false,
381 encoding, session, additionalProperties );
382 mavenFileFilter.copyFile( mavenFileFilterRequest );
383 changesXml = resultFile;
384 }
385 catch ( IOException e )
386 {
387 throw new MavenReportException( "Exception during filtering changes file : " + e.getMessage(), e );
388 }
389 catch ( MavenFilteringException e )
390 {
391 throw new MavenReportException( "Exception during filtering changes file : " + e.getMessage(), e );
392 }
393 finally
394 {
395 if ( xmlStreamReader != null )
396 {
397 IOUtil.close( xmlStreamReader );
398 }
399 }
400
401 }
402 return new ChangesXML( changesXml, getLog() );
403 }
404
405
406
407
408
409
410
411
412
413 private void addIssueLinkTemplate( String system, String issueLinkTemplate )
414 {
415 if ( caseInsensitiveIssueLinkTemplatePerSystem == null )
416 {
417 caseInsensitiveIssueLinkTemplatePerSystem = new CaseInsensitiveMap();
418 }
419 if ( !caseInsensitiveIssueLinkTemplatePerSystem.containsKey( system ) )
420 {
421 caseInsensitiveIssueLinkTemplatePerSystem.put( system, issueLinkTemplate );
422 }
423 }
424
425 private void copyStaticResources()
426 throws MavenReportException
427 {
428 final String pluginResourcesBase = "org/apache/maven/plugin/changes";
429 String resourceNames[] = {
430 "images/add.gif",
431 "images/fix.gif",
432 "images/icon_help_sml.gif",
433 "images/remove.gif",
434 "images/rss.png",
435 "images/update.gif" };
436 try
437 {
438 getLog().debug( "Copying static resources." );
439 for ( int i = 0; i < resourceNames.length; i++ )
440 {
441 URL url = this.getClass().getClassLoader().getResource( pluginResourcesBase + "/" + resourceNames[i] );
442 FileUtils.copyURLToFile( url, new File( getReportOutputDirectory(), resourceNames[i] ) );
443 }
444 }
445 catch ( IOException e )
446 {
447 throw new MavenReportException( "Unable to copy static resources." );
448 }
449 }
450
451 private ResourceBundle getBundle( Locale locale )
452 {
453 return ResourceBundle.getBundle( "changes-report", locale, this.getClass().getClassLoader() );
454 }
455
456 protected String getTeamlist()
457 {
458 return teamlist;
459 }
460
461 private void logIssueLinkTemplatePerSystem( Map issueLinkTemplatePerSystem )
462 {
463 if ( getLog().isDebugEnabled() )
464 {
465 if ( issueLinkTemplatePerSystem == null )
466 {
467 getLog().debug( "No issueLinkTemplatePerSystem configuration was found" );
468 }
469 else
470 {
471 Iterator iterator = issueLinkTemplatePerSystem.entrySet().iterator();
472 while ( iterator.hasNext() )
473 {
474 Map.Entry entry = (Map.Entry) iterator.next();
475 getLog().debug( "issueLinkTemplatePerSystem[" + entry.getKey() + "] = " + entry.getValue() );
476 }
477 }
478 }
479 }
480 }