Coverage Report - org.apache.maven.plugin.changes.ChangesMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
ChangesMojo
0%
0/106
0%
0/34
3.364
 
 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.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  
  * Goal which creates a nicely formatted Changes Report in html format from a changes.xml file.
 50  
  *
 51  
  * @goal changes-report
 52  
  * @author <a href="mailto:jruiz@exist.com">Johnny R. Ruiz III</a>
 53  
  * @version $Id: ChangesMojo.java 1328886 2012-04-22 14:09:45Z bimargulies $
 54  
  * @threadSafe
 55  
  */
 56  0
 public class ChangesMojo
 57  
     extends AbstractChangesReport
 58  
 {
 59  
     /**
 60  
      * A flag whether the report should also include changes from child modules. If set to <code>false</code>, only
 61  
      * the changes from current project will be written to the report.
 62  
      *
 63  
      * @parameter default-value="false"
 64  
      * @since 2.5
 65  
      */
 66  
     private boolean aggregated;
 67  
 
 68  
     /**
 69  
      * A flag whether the report should also include the dates of individual actions. If set to <code>false</code>, only
 70  
      * the dates of releases will be written to the report.
 71  
      *
 72  
      * @parameter expression="${changes.addActionDate}" default-value="false"
 73  
      * @since 2.1
 74  
      */
 75  
     private boolean addActionDate;
 76  
 
 77  
     /**
 78  
      * Whether HTML code within an action should be escaped. By changing this to
 79  
      * <code>false</code> you can restore the behavior that was in version 2.2
 80  
      * of this plugin, allowing you to use HTML code to format the content of an
 81  
      * action.
 82  
      * <p>
 83  
      * <strong>Note:</strong> If you use HTML code in an action you need to
 84  
      * place it inside a CDATA section.
 85  
      * </p>
 86  
      * <strong>Note:</strong> Putting any kind of markup inside a CDATA section
 87  
      * might mess up the Changes Report or other generated documents, such as
 88  
      * PDFs, that are based on your <code>changes.xml</code> file if you are not
 89  
      * careful.
 90  
      *
 91  
      * @parameter default-value="true"
 92  
      * @since 2.4
 93  
      * @deprecated using markup inside CDATA sections does not work for all output formats!
 94  
      */
 95  
     private boolean escapeHTML;
 96  
 
 97  
     /**
 98  
      * The directory for interpolated changes.xml.
 99  
      *
 100  
      * @parameter expression="${project.build.directory}/changes"
 101  
      * @required
 102  
      * @readonly
 103  
      * @since 2.2
 104  
      */
 105  
     private File filteredOutputDirectory;
 106  
 
 107  
     /**
 108  
      * applying filtering filtering "a la" resources plugin
 109  
      *
 110  
      * @parameter default-value="false"
 111  
      * @since 2.2
 112  
      */
 113  
     private boolean filteringChanges;
 114  
 
 115  
     /**
 116  
      * Template string that is used to discover the URL to use to display an issue report.
 117  
      * There are 2 template tokens you can use. <code>%URL%</code>: this is computed by getting the
 118  
      * <code>&lt;issueManagement&gt;/&lt;url&gt;</code> value from the POM, and removing the last '/'
 119  
      * and everything that comes after it. <code>%ISSUE%</code>: this is the issue number.
 120  
      * <p>
 121  
      * <strong>Note:</strong> In versions of this plugin prior to 2.0-beta-2 this parameter was called
 122  
      * <code>link_template</code>.
 123  
      * </p>
 124  
      *
 125  
      * @parameter expression="${changes.issueLinkTemplate}" default-value="%URL%/ViewIssue.jspa?key=%ISSUE%"
 126  
      * @since 2.0-beta-2
 127  
      * @deprecated As of 2.1 use issueLinkTemplatePerSystem : this one will be with system default
 128  
      */
 129  
     private String issueLinkTemplate;
 130  
 
 131  
     /**
 132  
      * Template strings per system that is used to discover the URL to use to display an issue report. Each key in this
 133  
      * map denotes the (case-insensitive) identifier of the issue tracking system and its value gives the URL template.
 134  
      * <p>
 135  
      * There are 2 template tokens you can use. <code>%URL%</code>: this is computed by getting the
 136  
      * <code>&lt;issueManagement&gt;/&lt;url&gt;</code> value from the POM, and removing the last '/'
 137  
      * and everything that comes after it. <code>%ISSUE%</code>: this is the issue number.
 138  
      * </p>
 139  
      * <p>
 140  
      * <strong>Note:</strong> The deprecated issueLinkTemplate will be used for a system called "default".
 141  
      * </p>
 142  
      * <p>
 143  
      * <strong>Note:</strong> Starting with version 2.4 you usually don't need
 144  
      * to specify this, unless you need to link to an issue management system in
 145  
      * your Changes report that isn't supported out of the box. See the
 146  
      * <a href="./usage.html">Usage page</a> for more
 147  
      * information.
 148  
      * </p>
 149  
      *
 150  
      * @parameter
 151  
      * @since 2.1
 152  
      */
 153  
     private Map issueLinkTemplatePerSystem;
 154  
 
 155  
     /**
 156  
      * @component
 157  
      * @since 2.2
 158  
      */
 159  
     private MavenFileFilter mavenFileFilter;
 160  
 
 161  
     /**
 162  
      * Format to use for publishDate. The value will be available with the following expression ${publishDate}
 163  
      *
 164  
      * @see java.text.SimpleDateFormat
 165  
      * @parameter default-value="yyyy-MM-dd"
 166  
      * @since 2.2
 167  
      */
 168  
     private String publishDateFormat;
 169  
 
 170  
    /**
 171  
     * Locale to use for publishDate when formatting
 172  
     *
 173  
     * @see java.util.Locale
 174  
     * @parameter default-value="en"
 175  
     * @since 2.2
 176  
     */
 177  
     private String publishDateLocale;
 178  
 
 179  
     /**
 180  
      * @parameter expression="${session}"
 181  
      * @readonly
 182  
      * @required
 183  
      * @since 2.2
 184  
      */
 185  
     protected MavenSession session;
 186  
 
 187  
     /**
 188  
      * @parameter default-value="${project.issueManagement.system}"
 189  
      * @readonly
 190  
      * @since 2.4
 191  
      */
 192  
     private String system;
 193  
 
 194  
     /**
 195  
      * The URI of a file containing all the team members. If this is set to the
 196  
      * special value "none", no links will be generated for the team members.
 197  
      *
 198  
      * @parameter default-value="team-list.html"
 199  
      * @since 2.4
 200  
      */
 201  
     private String teamlist;
 202  
 
 203  
     /**
 204  
      * @parameter default-value="${project.issueManagement.url}"
 205  
      * @readonly
 206  
      */
 207  
     private String url;
 208  
 
 209  
     /**
 210  
      * The path of the <code>changes.xml</code> file that will be converted into an HTML report.
 211  
      *
 212  
      * @parameter expression="${changes.xmlPath}" default-value="src/changes/changes.xml"
 213  
      */
 214  
     private File xmlPath;
 215  
 
 216  0
     private ReleaseUtils releaseUtils = new ReleaseUtils( getLog() );
 217  
 
 218  
     private CaseInsensitiveMap caseInsensitiveIssueLinkTemplatePerSystem;
 219  
 
 220  
     /* --------------------------------------------------------------------- */
 221  
     /* Public methods                                                        */
 222  
     /* --------------------------------------------------------------------- */
 223  
 
 224  
     public boolean canGenerateReport()
 225  
     {
 226  0
         return xmlPath.isFile();
 227  
     }
 228  
 
 229  
     public void executeReport( Locale locale )
 230  
         throws MavenReportException
 231  
     {
 232  0
         Date now = new Date();
 233  0
         SimpleDateFormat simpleDateFormat =
 234  
                 new SimpleDateFormat(publishDateFormat, new Locale(publishDateLocale));
 235  0
         Properties additionalProperties = new Properties();
 236  0
         additionalProperties.put("publishDate", simpleDateFormat.format(now));
 237  
 
 238  0
         ChangesXML changesXml = getChangesFromFile( xmlPath, project, additionalProperties);
 239  0
         if ( changesXml == null ) return;
 240  
 
 241  0
         if ( aggregated )
 242  
         {
 243  0
             final String basePath = project.getBasedir().getAbsolutePath();
 244  0
             final String absolutePath = xmlPath.getAbsolutePath();
 245  0
             if ( !absolutePath.startsWith( basePath ) )
 246  
             {
 247  0
                 getLog().warn( "xmlPath should be within the project dir for aggregated changes report." );
 248  0
                 return;
 249  
             }
 250  0
             final String relativePath = absolutePath.substring( basePath.length() );
 251  
 
 252  0
             List releaseList = changesXml.getReleaseList();
 253  0
             for ( Iterator iterator = project.getCollectedProjects().iterator(); iterator.hasNext(); )
 254  
             {
 255  0
                 final MavenProject childProject = (MavenProject) iterator.next();
 256  0
                 final File changesFile = new File( childProject.getBasedir(), relativePath );
 257  0
                 final ChangesXML childXml = getChangesFromFile( changesFile, childProject, additionalProperties );
 258  0
                 if ( childXml != null )
 259  
                 {
 260  0
                     releaseList = releaseUtils.mergeReleases( releaseList, childProject.getName(), childXml.getReleaseList() );
 261  
                 }
 262  0
             }
 263  0
             changesXml.setReleaseList( releaseList );
 264  
         }
 265  
 
 266  0
         ChangesReportGenerator report = new ChangesReportGenerator( changesXml.getReleaseList() );
 267  
 
 268  0
         report.setAuthor( changesXml.getAuthor() );
 269  0
         report.setTitle( changesXml.getTitle() );
 270  
 
 271  0
         report.setEscapeHTML ( escapeHTML );
 272  
 
 273  
         // Create a case insensitive version of issueLinkTemplatePerSystem
 274  
         // We need something case insensitive to maintain backward compatibility
 275  0
         if ( issueLinkTemplatePerSystem == null )
 276  
         {
 277  0
             caseInsensitiveIssueLinkTemplatePerSystem = new CaseInsensitiveMap();
 278  
         }
 279  
         else
 280  
         {
 281  0
             caseInsensitiveIssueLinkTemplatePerSystem = new CaseInsensitiveMap( issueLinkTemplatePerSystem );
 282  
         }
 283  
 
 284  
         // Set good default values for issue management systems here, but only
 285  
         // if they have not been configured already by the user
 286  0
         addIssueLinkTemplate( ChangesReportGenerator.DEFAULT_ISSUE_SYSTEM_KEY, issueLinkTemplate );
 287  0
         addIssueLinkTemplate( "Bitbucket", "%URL%/issue/%ISSUE%" );
 288  0
         addIssueLinkTemplate( "Bugzilla", "%URL%/show_bug.cgi?id=%ISSUE%" );
 289  0
         addIssueLinkTemplate( "GitHub", "%URL%/%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( "MKS", "%URL%/viewissue?selection=%ISSUE%" );
 294  0
         addIssueLinkTemplate( "Redmine", "%URL%/issues/show/%ISSUE%" );
 295  0
         addIssueLinkTemplate( "Scarab", "%URL%/issues/id/%ISSUE%" );
 296  0
         addIssueLinkTemplate( "SourceForge", "http://sourceforge.net/support/tracker.php?aid=%ISSUE%" );
 297  0
         addIssueLinkTemplate( "Trac", "%URL%/ticket/%ISSUE%" );
 298  0
         addIssueLinkTemplate( "Trackplus", "%URL%/printItem.action?key=%ISSUE%" );
 299  0
         addIssueLinkTemplate( "YouTrack", "%URL%/issue/%ISSUE%" );
 300  
         // @todo Add more issue management systems here
 301  
         // Remember to also add documentation in usage.apt.vm
 302  
 
 303  
         // Show the current issueLinkTemplatePerSystem configuration
 304  0
         logIssueLinkTemplatePerSystem( caseInsensitiveIssueLinkTemplatePerSystem );
 305  
 
 306  0
         report.setIssueLinksPerSystem( caseInsensitiveIssueLinkTemplatePerSystem );
 307  
 
 308  0
         report.setSystem( system );
 309  
 
 310  0
         report.setTeamlist ( teamlist );
 311  
 
 312  0
         report.setUrl( url );
 313  
 
 314  0
         report.setAddActionDate( addActionDate );
 315  
 
 316  0
         if ( StringUtils.isEmpty( url ) )
 317  
         {
 318  0
             getLog().warn( "No issue management URL defined in POM. Links to your issues will not work correctly." );
 319  
         }
 320  
 
 321  0
         report.doGenerateReport( getBundle( locale ), getSink() );
 322  
 
 323  
         // Copy the images
 324  0
         copyStaticResources();
 325  0
     }
 326  
 
 327  
     public String getDescription( Locale locale )
 328  
     {
 329  0
         return getBundle( locale ).getString( "report.issues.description" );
 330  
     }
 331  
 
 332  
     public String getName( Locale locale )
 333  
     {
 334  0
         return getBundle( locale ).getString( "report.issues.name" );
 335  
     }
 336  
 
 337  
     public String getOutputName()
 338  
     {
 339  0
         return "changes-report";
 340  
     }
 341  
 
 342  
     /* --------------------------------------------------------------------- */
 343  
     /* Private methods                                                       */
 344  
     /* --------------------------------------------------------------------- */
 345  
 
 346  
     /**
 347  
      * Parses specified changes.xml file. It also makes filtering if needed. If specified file doesn't exist
 348  
      * it will log warning and return <code>null</code>.
 349  
      *
 350  
      * @param changesXml changes xml file to parse
 351  
      * @param project maven project to parse changes for
 352  
      * @param additionalProperties additional properties used for filtering
 353  
      * @return parsed <code>ChangesXML</code> instance or null if file doesn't exist
 354  
      * @throws MavenReportException if any errors occurs while parsing
 355  
      */
 356  
     private ChangesXML getChangesFromFile( File changesXml, MavenProject project, Properties additionalProperties )
 357  
         throws MavenReportException
 358  
     {
 359  0
         if ( !changesXml.exists() )
 360  
         {
 361  0
             getLog().warn( "changes.xml file " + changesXml.getAbsolutePath() + " does not exist." );
 362  0
             return null;
 363  
         }
 364  
 
 365  0
         if ( filteringChanges )
 366  
         {
 367  0
             if ( !filteredOutputDirectory.exists() )
 368  
             {
 369  0
                 filteredOutputDirectory.mkdirs();
 370  
             }
 371  0
             XmlStreamReader xmlStreamReader = null;
 372  
             try
 373  
             {
 374  
                 // so we get encoding from the file itself
 375  0
                 xmlStreamReader = ReaderFactory.newXmlReader( changesXml );
 376  0
                 String encoding = xmlStreamReader.getEncoding();
 377  0
                 File resultFile = new File( filteredOutputDirectory, project.getGroupId() + "." + project.getArtifactId() + "-changes.xml" );
 378  
 
 379  0
                 final MavenFileFilterRequest mavenFileFilterRequest =
 380  
                         new MavenFileFilterRequest( changesXml, resultFile, true, project, Collections.EMPTY_LIST, false,
 381  
                                 encoding, session, additionalProperties );
 382  0
                 mavenFileFilter.copyFile( mavenFileFilterRequest );
 383  0
                 changesXml = resultFile;
 384  
             }
 385  0
             catch ( IOException e )
 386  
             {
 387  0
                 throw new MavenReportException( "Exception during filtering changes file : " + e.getMessage(), e );
 388  
             }
 389  0
             catch ( MavenFilteringException e )
 390  
             {
 391  0
                 throw new MavenReportException( "Exception during filtering changes file : " + e.getMessage(), e );
 392  
             }
 393  
             finally
 394  
             {
 395  0
                 if ( xmlStreamReader != null )
 396  
                 {
 397  0
                     IOUtil.close( xmlStreamReader );
 398  
                 }
 399  
             }
 400  
 
 401  
         }
 402  0
         return new ChangesXML( changesXml, getLog() );
 403  
     }
 404  
 
 405  
     /**
 406  
      * Add the issue link template for the given issue management system,
 407  
      * but only if it has not already been configured.
 408  
      *
 409  
      * @param system The issue management system
 410  
      * @param issueLinkTemplate The issue link template to use
 411  
      * @since 2.4
 412  
      */
 413  
     private void addIssueLinkTemplate( String system, String issueLinkTemplate )
 414  
     {
 415  0
         if ( caseInsensitiveIssueLinkTemplatePerSystem == null )
 416  
         {
 417  0
             caseInsensitiveIssueLinkTemplatePerSystem = new CaseInsensitiveMap();
 418  
         }
 419  0
         if ( !caseInsensitiveIssueLinkTemplatePerSystem.containsKey( system ) )
 420  
         {
 421  0
             caseInsensitiveIssueLinkTemplatePerSystem.put( system, issueLinkTemplate );
 422  
         }
 423  0
     }
 424  
 
 425  
     private void copyStaticResources()
 426  
         throws MavenReportException
 427  
     {
 428  0
         final String pluginResourcesBase = "org/apache/maven/plugin/changes";
 429  0
         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  0
             getLog().debug( "Copying static resources." );
 439  0
             for ( int i = 0; i < resourceNames.length; i++ )
 440  
             {
 441  0
                 URL url = this.getClass().getClassLoader().getResource( pluginResourcesBase + "/" + resourceNames[i] );
 442  0
                 FileUtils.copyURLToFile( url, new File( getReportOutputDirectory(), resourceNames[i] ) );
 443  
             }
 444  
         }
 445  0
         catch ( IOException e )
 446  
         {
 447  0
             throw new MavenReportException( "Unable to copy static resources." );
 448  0
         }
 449  0
     }
 450  
 
 451  
     private ResourceBundle getBundle( Locale locale )
 452  
     {
 453  0
         return ResourceBundle.getBundle( "changes-report", locale, this.getClass().getClassLoader() );
 454  
     }
 455  
 
 456  
     protected String getTeamlist()
 457  
     {
 458  0
         return teamlist;
 459  
     }
 460  
 
 461  
     private void logIssueLinkTemplatePerSystem( Map issueLinkTemplatePerSystem )
 462  
     {
 463  0
         if ( getLog().isDebugEnabled() )
 464  
         {
 465  0
             if ( issueLinkTemplatePerSystem == null )
 466  
             {
 467  0
                 getLog().debug( "No issueLinkTemplatePerSystem configuration was found" );
 468  
             }
 469  
             else
 470  
             {
 471  0
                 Iterator iterator = issueLinkTemplatePerSystem.entrySet().iterator();
 472  0
                 while ( iterator.hasNext() )
 473  
                 {
 474  0
                     Map.Entry entry = (Map.Entry) iterator.next();
 475  0
                     getLog().debug( "issueLinkTemplatePerSystem[" + entry.getKey() + "] = " + entry.getValue() );
 476  0
                 }
 477  
             }
 478  
         }
 479  0
     }
 480  
 }