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