Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ChangesMojo |
|
| 2.9;2,9 |
1 | package org.apache.maven.plugin.changes; | |
2 | ||
3 | /* | |
4 | * Licensed to the Apache Software Foundation (ASF) under one | |
5 | * or more contributor license agreements. See the NOTICE file | |
6 | * distributed with this work for additional information | |
7 | * regarding copyright ownership. The ASF licenses this file | |
8 | * to you under the Apache License, Version 2.0 (the | |
9 | * "License"); you may not use this file except in compliance | |
10 | * with the License. You may obtain a copy of the License at | |
11 | * | |
12 | * http://www.apache.org/licenses/LICENSE-2.0 | |
13 | * | |
14 | * Unless required by applicable law or agreed to in writing, | |
15 | * software distributed under the License is distributed on an | |
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
17 | * KIND, either express or implied. See the License for the | |
18 | * specific language governing permissions and limitations | |
19 | * under the License. | |
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.Locale; | |
30 | import java.util.Map; | |
31 | import java.util.Properties; | |
32 | import java.util.ResourceBundle; | |
33 | ||
34 | import org.apache.commons.collections.map.CaseInsensitiveMap; | |
35 | import org.apache.maven.execution.MavenSession; | |
36 | import org.apache.maven.reporting.MavenReportException; | |
37 | import org.apache.maven.shared.filtering.MavenFileFilter; | |
38 | import org.apache.maven.shared.filtering.MavenFileFilterRequest; | |
39 | import org.apache.maven.shared.filtering.MavenFilteringException; | |
40 | import org.codehaus.plexus.util.FileUtils; | |
41 | import org.codehaus.plexus.util.IOUtil; | |
42 | import org.codehaus.plexus.util.ReaderFactory; | |
43 | import org.codehaus.plexus.util.StringUtils; | |
44 | import org.codehaus.plexus.util.xml.XmlStreamReader; | |
45 | ||
46 | /** | |
47 | * Goal which creates a nicely formatted Changes Report in html format from a changes.xml file. | |
48 | * | |
49 | * @goal changes-report | |
50 | * @author <a href="mailto:jruiz@exist.com">Johnny R. Ruiz III</a> | |
51 | * @version $Id$ | |
52 | * @threadSafe | |
53 | */ | |
54 | 0 | public class ChangesMojo |
55 | extends AbstractChangesReport | |
56 | { | |
57 | /** | |
58 | * A flag whether the report should also include the dates of individual actions. If set to <code>false</code>, only | |
59 | * the dates of releases will be written to the report. | |
60 | * | |
61 | * @parameter expression="${changes.addActionDate}" default-value="false" | |
62 | * @since 2.1 | |
63 | */ | |
64 | private boolean addActionDate; | |
65 | ||
66 | /** | |
67 | * Whether HTML code within an action should be escaped. By changing this to | |
68 | * <code>false</code> you can restore the behavior that was in version 2.2 | |
69 | * of this plugin, allowing you to use HTML code to format the content of an | |
70 | * action. | |
71 | * <p> | |
72 | * <strong>Note:</strong> If you use HTML code in an action you need to | |
73 | * place it inside a CDATA section. | |
74 | * </p> | |
75 | * <strong>Note:</strong> Putting any kind of markup inside a CDATA section | |
76 | * might mess up the Changes Report or other generated documents, such as | |
77 | * PDFs, that are based on your <code>changes.xml</code> file if you are not | |
78 | * careful. | |
79 | * | |
80 | * @parameter default-value="true" | |
81 | * @since 2.4 | |
82 | * @deprecated using markup inside CDATA sections does not work for all output formats! | |
83 | */ | |
84 | private boolean escapeHTML; | |
85 | ||
86 | /** | |
87 | * The directory for interpolated changes.xml. | |
88 | * | |
89 | * @parameter expression="${project.build.directory}/changes" | |
90 | * @required | |
91 | * @readonly | |
92 | * @since 2.2 | |
93 | */ | |
94 | private File filteredOutputDirectory; | |
95 | ||
96 | /** | |
97 | * applying filtering filtering "a la" resources plugin | |
98 | * | |
99 | * @parameter default-value="false" | |
100 | * @since 2.2 | |
101 | */ | |
102 | private boolean filteringChanges; | |
103 | ||
104 | /** | |
105 | * Template string that is used to discover the URL to use to display an issue report. | |
106 | * There are 2 template tokens you can use. <code>%URL%</code>: this is computed by getting the | |
107 | * <code><issueManagement>/<url></code> value from the POM, and removing the last '/' | |
108 | * and everything that comes after it. <code>%ISSUE%</code>: this is the issue number. | |
109 | * <p> | |
110 | * <strong>Note:</strong> In versions of this plugin prior to 2.0-beta-2 this parameter was called | |
111 | * <code>link_template</code>. | |
112 | * </p> | |
113 | * | |
114 | * @parameter expression="${changes.issueLinkTemplate}" default-value="%URL%/ViewIssue.jspa?key=%ISSUE%" | |
115 | * @since 2.0-beta-2 | |
116 | * @deprecated As of 2.1 use issueLinkTemplatePerSystem : this one will be with system default | |
117 | */ | |
118 | private String issueLinkTemplate; | |
119 | ||
120 | /** | |
121 | * Template strings per system that is used to discover the URL to use to display an issue report. Each key in this | |
122 | * map denotes the (case-insensitive) identifier of the issue tracking system and its value gives the URL template. | |
123 | * <p> | |
124 | * There are 2 template tokens you can use. <code>%URL%</code>: this is computed by getting the | |
125 | * <code><issueManagement>/<url></code> value from the POM, and removing the last '/' | |
126 | * and everything that comes after it. <code>%ISSUE%</code>: this is the issue number. | |
127 | * </p> | |
128 | * <p> | |
129 | * <strong>Note:</strong> The deprecated issueLinkTemplate will be used for a system called "default". | |
130 | * </p> | |
131 | * <p> | |
132 | * <strong>Note:</strong> Starting with version 2.4 you usually don't need | |
133 | * to specify this, unless you need to link to an issue management system in | |
134 | * your Changes report that isn't supported out of the box. See the | |
135 | * <a href="./usage.html">Usage page</a> for more | |
136 | * information. | |
137 | * </p> | |
138 | * | |
139 | * @parameter | |
140 | * @since 2.1 | |
141 | */ | |
142 | private Map issueLinkTemplatePerSystem; | |
143 | ||
144 | /** | |
145 | * @component | |
146 | * @since 2.2 | |
147 | */ | |
148 | private MavenFileFilter mavenFileFilter; | |
149 | ||
150 | /** | |
151 | * Format to use for publishDate. The value will be available with the following expression ${publishDate} | |
152 | * | |
153 | * @see SimpleDateFormat | |
154 | * @parameter default-value="yyyy-MM-dd" | |
155 | * @since 2.2 | |
156 | */ | |
157 | private String publishDateFormat; | |
158 | ||
159 | /** | |
160 | * Locale to use for publishDate when formatting | |
161 | * | |
162 | * @see Locale | |
163 | * @parameter default-value="en" | |
164 | * @since 2.2 | |
165 | */ | |
166 | private String publishDateLocale; | |
167 | ||
168 | /** | |
169 | * @parameter expression="${session}" | |
170 | * @readonly | |
171 | * @required | |
172 | * @since 2.2 | |
173 | */ | |
174 | protected MavenSession session; | |
175 | ||
176 | /** | |
177 | * @parameter default-value="${project.issueManagement.system}" | |
178 | * @readonly | |
179 | * @since 2.4 | |
180 | */ | |
181 | private String system; | |
182 | ||
183 | /** | |
184 | * The URI of a file containing all the team members. If this is set to the | |
185 | * special value "none", no links will be generated for the team members. | |
186 | * | |
187 | * @parameter default-value="team-list.html" | |
188 | * @since 2.4 | |
189 | */ | |
190 | private String teamlist; | |
191 | ||
192 | /** | |
193 | * @parameter default-value="${project.issueManagement.url}" | |
194 | * @readonly | |
195 | */ | |
196 | private String url; | |
197 | ||
198 | /** | |
199 | * The path of the <code>changes.xml</code> file that will be converted into an HTML report. | |
200 | * | |
201 | * @parameter expression="${changes.xmlPath}" default-value="src/changes/changes.xml" | |
202 | */ | |
203 | private File xmlPath; | |
204 | ||
205 | private CaseInsensitiveMap caseInsensitiveIssueLinkTemplatePerSystem; | |
206 | ||
207 | /* --------------------------------------------------------------------- */ | |
208 | /* Public methods */ | |
209 | /* --------------------------------------------------------------------- */ | |
210 | ||
211 | public boolean canGenerateReport() | |
212 | { | |
213 | 0 | return xmlPath.isFile(); |
214 | } | |
215 | ||
216 | public void executeReport( Locale locale ) | |
217 | throws MavenReportException | |
218 | { | |
219 | ||
220 | 0 | if ( !xmlPath.exists() ) |
221 | { | |
222 | 0 | getLog().warn( "changes.xml file " + xmlPath.getAbsolutePath() + " does not exist." ); |
223 | 0 | return; |
224 | } | |
225 | 0 | if ( filteringChanges ) |
226 | { | |
227 | 0 | if ( !filteredOutputDirectory.exists() ) |
228 | { | |
229 | 0 | filteredOutputDirectory.mkdirs(); |
230 | } | |
231 | 0 | XmlStreamReader xmlStreamReader = null; |
232 | try | |
233 | { | |
234 | // so we get encoding from the file itself | |
235 | 0 | xmlStreamReader = ReaderFactory.newXmlReader( xmlPath ); |
236 | 0 | String encoding = xmlStreamReader.getEncoding(); |
237 | 0 | File resultFile = new File( filteredOutputDirectory, "changes.xml" ); |
238 | 0 | Date now = new Date(); |
239 | 0 | SimpleDateFormat simpleDateFormat = |
240 | new SimpleDateFormat( publishDateFormat, new Locale( publishDateLocale ) ); | |
241 | 0 | Properties additionalProperties = new Properties(); |
242 | 0 | additionalProperties.put( "publishDate", simpleDateFormat.format( now ) ); |
243 | 0 | MavenFileFilterRequest mavenFileFilterRequest = |
244 | new MavenFileFilterRequest( xmlPath, resultFile, true, project, Collections.EMPTY_LIST, false, | |
245 | encoding, session, additionalProperties ); | |
246 | 0 | mavenFileFilter.copyFile( mavenFileFilterRequest ); |
247 | 0 | xmlPath = resultFile; |
248 | } | |
249 | 0 | catch ( IOException e ) |
250 | { | |
251 | 0 | throw new MavenReportException( "Exception during filtering changes file : " + e.getMessage(), e ); |
252 | } | |
253 | 0 | catch ( MavenFilteringException e ) |
254 | { | |
255 | 0 | throw new MavenReportException( "Exception during filtering changes file : " + e.getMessage(), e ); |
256 | } | |
257 | finally | |
258 | { | |
259 | 0 | if ( xmlStreamReader != null ) |
260 | { | |
261 | 0 | IOUtil.close( xmlStreamReader ); |
262 | } | |
263 | } | |
264 | ||
265 | } | |
266 | ||
267 | 0 | ChangesXML changesXml = new ChangesXML( xmlPath, getLog() ); |
268 | 0 | ChangesReportGenerator report = new ChangesReportGenerator( changesXml.getReleaseList() ); |
269 | ||
270 | 0 | report.setAuthor( changesXml.getAuthor() ); |
271 | 0 | report.setTitle( changesXml.getTitle() ); |
272 | ||
273 | 0 | report.setEscapeHTML ( escapeHTML ); |
274 | ||
275 | // Create a case insensitive version of issueLinkTemplatePerSystem | |
276 | // We need something case insensitive to maintain backward compatibility | |
277 | 0 | if ( issueLinkTemplatePerSystem == null ) |
278 | { | |
279 | 0 | caseInsensitiveIssueLinkTemplatePerSystem = new CaseInsensitiveMap(); |
280 | } | |
281 | else | |
282 | { | |
283 | 0 | caseInsensitiveIssueLinkTemplatePerSystem = new CaseInsensitiveMap( issueLinkTemplatePerSystem ); |
284 | } | |
285 | ||
286 | // Set good default values for issue management systems here, but only | |
287 | // if they have not been configured already by the user | |
288 | 0 | addIssueLinkTemplate( ChangesReportGenerator.DEFAULT_ISSUE_SYSTEM_KEY, issueLinkTemplate ); |
289 | 0 | addIssueLinkTemplate( "Bugzilla", "%URL%/show_bug.cgi?id=%ISSUE%" ); |
290 | 0 | addIssueLinkTemplate( "GoogleCode", "%URL%/detail?id=%ISSUE%" ); |
291 | 0 | addIssueLinkTemplate( "JIRA", "%URL%/%ISSUE%" ); |
292 | 0 | addIssueLinkTemplate( "Mantis", "%URL%/view.php?id=%ISSUE%" ); |
293 | 0 | addIssueLinkTemplate( "Redmine", "%URL%/issues/show/%ISSUE%" ); |
294 | 0 | addIssueLinkTemplate( "Scarab", "%URL%/issues/id/%ISSUE%" ); |
295 | 0 | addIssueLinkTemplate( "SourceForge", "http://sourceforge.net/support/tracker.php?aid=%ISSUE%" ); |
296 | 0 | addIssueLinkTemplate( "Trac", "%URL%/ticket/%ISSUE%" ); |
297 | 0 | addIssueLinkTemplate( "YouTrack", "%URL%/issue/%ISSUE%" ); |
298 | // @todo Add more issue management systems here | |
299 | ||
300 | // Show the current issueLinkTemplatePerSystem configuration | |
301 | 0 | logIssueLinkTemplatePerSystem( caseInsensitiveIssueLinkTemplatePerSystem ); |
302 | ||
303 | 0 | report.setIssueLinksPerSystem( caseInsensitiveIssueLinkTemplatePerSystem ); |
304 | ||
305 | 0 | report.setSystem( system ); |
306 | ||
307 | 0 | report.setTeamlist ( teamlist ); |
308 | ||
309 | 0 | report.setUrl( url ); |
310 | ||
311 | 0 | report.setAddActionDate( addActionDate ); |
312 | ||
313 | 0 | if ( StringUtils.isEmpty( url ) ) |
314 | { | |
315 | 0 | getLog().warn( "No issue management URL defined in POM. Links to your issues will not work correctly." ); |
316 | } | |
317 | ||
318 | 0 | report.doGenerateReport( getBundle( locale ), getSink() ); |
319 | ||
320 | // Copy the images | |
321 | 0 | copyStaticResources(); |
322 | 0 | } |
323 | ||
324 | public String getDescription( Locale locale ) | |
325 | { | |
326 | 0 | return getBundle( locale ).getString( "report.issues.description" ); |
327 | } | |
328 | ||
329 | public String getName( Locale locale ) | |
330 | { | |
331 | 0 | return getBundle( locale ).getString( "report.issues.name" ); |
332 | } | |
333 | ||
334 | public String getOutputName() | |
335 | { | |
336 | 0 | return "changes-report"; |
337 | } | |
338 | ||
339 | /* --------------------------------------------------------------------- */ | |
340 | /* Private methods */ | |
341 | /* --------------------------------------------------------------------- */ | |
342 | ||
343 | /** | |
344 | * Add the issue link template for the given issue management system, | |
345 | * but only if it has not already been configured. | |
346 | * | |
347 | * @param system The issue management system | |
348 | * @param issueLinkTemplate The issue link template to use | |
349 | * @since 2.4 | |
350 | */ | |
351 | private void addIssueLinkTemplate( String system, String issueLinkTemplate ) | |
352 | { | |
353 | 0 | if ( caseInsensitiveIssueLinkTemplatePerSystem == null ) |
354 | { | |
355 | 0 | caseInsensitiveIssueLinkTemplatePerSystem = new CaseInsensitiveMap(); |
356 | } | |
357 | 0 | if ( !caseInsensitiveIssueLinkTemplatePerSystem.containsKey( system ) ) |
358 | { | |
359 | 0 | caseInsensitiveIssueLinkTemplatePerSystem.put( system, issueLinkTemplate ); |
360 | } | |
361 | 0 | } |
362 | ||
363 | private void copyStaticResources() | |
364 | throws MavenReportException | |
365 | { | |
366 | 0 | final String pluginResourcesBase = "org/apache/maven/plugin/changes"; |
367 | 0 | String resourceNames[] = { |
368 | "images/add.gif", | |
369 | "images/fix.gif", | |
370 | "images/icon_help_sml.gif", | |
371 | "images/remove.gif", | |
372 | "images/rss.png", | |
373 | "images/update.gif" }; | |
374 | try | |
375 | { | |
376 | 0 | getLog().debug( "Copying static resources." ); |
377 | 0 | for ( int i = 0; i < resourceNames.length; i++ ) |
378 | { | |
379 | 0 | URL url = this.getClass().getClassLoader().getResource( pluginResourcesBase + "/" + resourceNames[i] ); |
380 | 0 | FileUtils.copyURLToFile( url, new File( getReportOutputDirectory(), resourceNames[i] ) ); |
381 | } | |
382 | } | |
383 | 0 | catch ( IOException e ) |
384 | { | |
385 | 0 | throw new MavenReportException( "Unable to copy static resources." ); |
386 | 0 | } |
387 | 0 | } |
388 | ||
389 | private ResourceBundle getBundle( Locale locale ) | |
390 | { | |
391 | 0 | return ResourceBundle.getBundle( "changes-report", locale, this.getClass().getClassLoader() ); |
392 | } | |
393 | ||
394 | protected String getTeamlist() | |
395 | { | |
396 | 0 | return teamlist; |
397 | } | |
398 | ||
399 | private void logIssueLinkTemplatePerSystem( Map issueLinkTemplatePerSystem ) | |
400 | { | |
401 | 0 | if ( getLog().isDebugEnabled() ) |
402 | { | |
403 | 0 | if ( issueLinkTemplatePerSystem == null ) |
404 | { | |
405 | 0 | getLog().debug( "No issueLinkTemplatePerSystem configuration was found" ); |
406 | } | |
407 | else | |
408 | { | |
409 | 0 | Iterator iterator = issueLinkTemplatePerSystem.entrySet().iterator(); |
410 | 0 | while ( iterator.hasNext() ) |
411 | { | |
412 | 0 | Map.Entry entry = (Map.Entry) iterator.next(); |
413 | 0 | getLog().debug( "issueLinkTemplatePerSystem[" + entry.getKey() + "] = " + entry.getValue() ); |
414 | 0 | } |
415 | } | |
416 | } | |
417 | 0 | } |
418 | } |