Coverage Report - org.apache.maven.report.projectinfo.LicenseReport
 
Classes in this File Line Coverage Branch Coverage Complexity
LicenseReport
37%
15/41
12%
2/16
4
LicenseReport$LicenseRenderer
44%
37/85
26%
9/34
4
 
 1  
 package org.apache.maven.report.projectinfo;
 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 org.apache.commons.validator.UrlValidator;
 23  
 import org.apache.maven.doxia.sink.Sink;
 24  
 import org.apache.maven.model.License;
 25  
 import org.apache.maven.project.MavenProject;
 26  
 import org.apache.maven.reporting.AbstractMavenReportRenderer;
 27  
 import org.apache.maven.settings.Settings;
 28  
 import org.codehaus.plexus.i18n.I18N;
 29  
 import org.codehaus.plexus.util.StringUtils;
 30  
 
 31  
 import java.io.File;
 32  
 import java.io.IOException;
 33  
 import java.net.MalformedURLException;
 34  
 import java.net.URL;
 35  
 import java.util.Iterator;
 36  
 import java.util.List;
 37  
 import java.util.Locale;
 38  
 import java.util.regex.Matcher;
 39  
 import java.util.regex.Pattern;
 40  
 
 41  
 /**
 42  
  * Generates the Project License report.
 43  
  *
 44  
  * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton </a>
 45  
  * @version $Id: LicenseReport.java 748487 2009-02-27 11:22:04Z vsiveton $
 46  
  * @since 2.0
 47  
  * @goal license
 48  
  */
 49  1
 public class LicenseReport
 50  
     extends AbstractProjectInfoReport
 51  
 {
 52  
     // ----------------------------------------------------------------------
 53  
     // Mojo parameters
 54  
     // ----------------------------------------------------------------------
 55  
 
 56  
     /**
 57  
      * The Maven Settings.
 58  
      *
 59  
      * @parameter default-value="${settings}"
 60  
      * @required
 61  
      * @readonly
 62  
      */
 63  
     private Settings settings;
 64  
 
 65  
     /**
 66  
      * Whether the system is currently offline.
 67  
      *
 68  
      * @parameter expression="${settings.offline}"
 69  
      */
 70  
     private boolean offline;
 71  
 
 72  
     // ----------------------------------------------------------------------
 73  
     // Public methods
 74  
     // ----------------------------------------------------------------------
 75  
 
 76  
     /** {@inheritDoc} */
 77  
     public String getName( Locale locale )
 78  
     {
 79  1
         return i18n.getString( "project-info-report", locale, "report.license.name" );
 80  
     }
 81  
 
 82  
     /** {@inheritDoc} */
 83  
     public String getDescription( Locale locale )
 84  
     {
 85  0
         return i18n.getString( "project-info-report", locale, "report.license.description" );
 86  
     }
 87  
 
 88  
     /** {@inheritDoc} */
 89  
     public void executeReport( Locale locale )
 90  
     {
 91  1
         LicenseRenderer r = new LicenseRenderer( getSink(), getProject(), i18n, locale, settings );
 92  
 
 93  1
         r.render();
 94  1
     }
 95  
 
 96  
     /** {@inheritDoc} */
 97  
     public boolean canGenerateReport()
 98  
     {
 99  1
         if ( !offline )
 100  
         {
 101  1
             return true;
 102  
         }
 103  
 
 104  0
         List licenses = project.getModel().getLicenses();
 105  0
         for ( Iterator i = licenses.iterator(); i.hasNext(); )
 106  
         {
 107  0
             License license = (License) i.next();
 108  
 
 109  0
             String url = license.getUrl();
 110  
 
 111  0
             URL licenseUrl = null;
 112  
             try
 113  
             {
 114  0
                 licenseUrl = getLicenseURL( project, url );
 115  
             }
 116  0
             catch ( MalformedURLException e )
 117  
             {
 118  0
                 getLog().error( e.getMessage() );
 119  
             }
 120  0
             catch ( IOException e )
 121  
             {
 122  0
                 getLog().error( e.getMessage() );
 123  0
             }
 124  
 
 125  0
             if ( licenseUrl != null && licenseUrl.getProtocol().equals( "file" ) )
 126  
             {
 127  0
                 return true;
 128  
             }
 129  
         }
 130  
 
 131  0
         return false;
 132  
     }
 133  
 
 134  
     /** {@inheritDoc} */
 135  
     public String getOutputName()
 136  
     {
 137  3
         return "license";
 138  
     }
 139  
 
 140  
     /**
 141  
      * @param project not null
 142  
      * @param url not null
 143  
      * @return a valid URL object from the url string
 144  
      * @throws MalformedURLException if any
 145  
      * @throws IOException if any
 146  
      */
 147  
     protected static URL getLicenseURL( MavenProject project, String url )
 148  
         throws MalformedURLException, IOException
 149  
     {
 150  1
         URL licenseUrl = null;
 151  1
         UrlValidator urlValidator = new UrlValidator( UrlValidator.ALLOW_ALL_SCHEMES );
 152  
         // UrlValidator does not accept file URLs because the file
 153  
         // URLs do not contain a valid authority (no hostname).
 154  
         // As a workaround accept license URLs that start with the
 155  
         // file scheme.
 156  1
         if ( urlValidator.isValid( url ) || StringUtils.defaultString( url ).startsWith( "file://" ) )
 157  
         {
 158  
             try
 159  
             {
 160  1
                 licenseUrl = new URL( url );
 161  
             }
 162  0
             catch ( MalformedURLException e )
 163  
             {
 164  0
                 throw new MalformedURLException( "The license url '" + url + "' seems to be invalid: "
 165  
                     + e.getMessage() );
 166  1
             }
 167  
         }
 168  
         else
 169  
         {
 170  0
             File licenseFile = new File( project.getBasedir(), url );
 171  0
             if ( !licenseFile.exists() )
 172  
             {
 173  
                 // Workaround to allow absolute path names while
 174  
                 // staying compatible with the way it was...
 175  0
                 licenseFile = new File( url );
 176  
             }
 177  0
             if ( !licenseFile.exists() )
 178  
             {
 179  0
                 throw new IOException( "Maven can't find the file '" + licenseFile + "' on the system." );
 180  
             }
 181  
             try
 182  
             {
 183  0
                 licenseUrl = licenseFile.toURL();
 184  
             }
 185  0
             catch ( MalformedURLException e )
 186  
             {
 187  0
                 throw new MalformedURLException( "The license url '" + url + "' seems to be invalid: "
 188  
                     + e.getMessage() );
 189  0
             }
 190  
         }
 191  
 
 192  1
         return licenseUrl;
 193  
     }
 194  
 
 195  
     // ----------------------------------------------------------------------
 196  
     // Private
 197  
     // ----------------------------------------------------------------------
 198  
 
 199  
     /**
 200  
      * Internal renderer class
 201  
      */
 202  1
     private static class LicenseRenderer
 203  
         extends AbstractMavenReportRenderer
 204  
     {
 205  
         private MavenProject project;
 206  
 
 207  
         private Settings settings;
 208  
 
 209  
         private I18N i18n;
 210  
 
 211  
         private Locale locale;
 212  
 
 213  
         LicenseRenderer( Sink sink, MavenProject project, I18N i18n, Locale locale, Settings settings )
 214  
         {
 215  1
             super( sink );
 216  
 
 217  1
             this.project = project;
 218  
 
 219  1
             this.settings = settings;
 220  
 
 221  1
             this.i18n = i18n;
 222  
 
 223  1
             this.locale = locale;
 224  1
         }
 225  
 
 226  
         /** {@inheritDoc} */
 227  
         public String getTitle()
 228  
         {
 229  1
             return i18n.getString( "project-info-report", locale, "report.license.title" );
 230  
         }
 231  
 
 232  
         /** {@inheritDoc} */
 233  
         public void renderBody()
 234  
         {
 235  1
             List licenses = project.getModel().getLicenses();
 236  
 
 237  1
             if ( licenses.isEmpty() )
 238  
             {
 239  0
                 startSection( getTitle() );
 240  
 
 241  0
                 paragraph( i18n.getString( "project-info-report", locale, "report.license.nolicense" ) );
 242  
 
 243  0
                 endSection();
 244  
 
 245  0
                 return;
 246  
             }
 247  
 
 248  
             // Overview
 249  1
             startSection( i18n.getString( "project-info-report", locale, "report.license.overview.title" ) );
 250  
 
 251  1
             paragraph( i18n.getString( "project-info-report", locale, "report.license.overview.intro" ) );
 252  
 
 253  1
             endSection();
 254  
 
 255  
             // License
 256  1
             startSection( i18n.getString( "project-info-report", locale, "report.license.title" ) );
 257  
 
 258  1
             for ( Iterator i = licenses.iterator(); i.hasNext(); )
 259  
             {
 260  1
                 License license = (License) i.next();
 261  
 
 262  1
                 String name = license.getName();
 263  1
                 String url = license.getUrl();
 264  1
                 String comments = license.getComments();
 265  
 
 266  1
                 startSection( name );
 267  
 
 268  1
                 if ( !StringUtils.isEmpty( comments ) )
 269  
                 {
 270  0
                     paragraph( comments );
 271  
                 }
 272  
 
 273  1
                 if ( url != null )
 274  
                 {
 275  1
                     URL licenseUrl = null;
 276  
                     try
 277  
                     {
 278  1
                         licenseUrl = getLicenseURL( project, url );
 279  
                     }
 280  0
                     catch ( MalformedURLException e )
 281  
                     {
 282  
                         // I18N message
 283  0
                         paragraph( e.getMessage() );
 284  
                     }
 285  0
                     catch ( IOException e )
 286  
                     {
 287  
                         // I18N message
 288  0
                         paragraph( e.getMessage() );
 289  1
                     }
 290  
 
 291  1
                     if ( licenseUrl != null )
 292  
                     {
 293  1
                         String licenseContent = null;
 294  
                         try
 295  
                         {
 296  
                             // All licenses are supposed in English...
 297  1
                             licenseContent = ProjectInfoReportUtils.getInputStream( licenseUrl, settings );
 298  
                         }
 299  0
                         catch ( IOException e )
 300  
                         {
 301  0
                             paragraph( "Can't read the url [" + licenseUrl + "] : " + e.getMessage() );
 302  1
                         }
 303  
 
 304  1
                         if ( licenseContent != null )
 305  
                         {
 306  
                             // TODO: we should check for a text/html mime type instead, and possibly use a html parser to do this a bit more cleanly/reliably.
 307  1
                             String licenseContentLC = licenseContent.toLowerCase( Locale.ENGLISH );
 308  1
                             int bodyStart = licenseContentLC.indexOf( "<body" );
 309  1
                             int bodyEnd = licenseContentLC.indexOf( "</body>" );
 310  1
                             if ( ( licenseContentLC.startsWith( "<!doctype html" )
 311  
                                 || licenseContentLC.startsWith( "<html>" ) ) && bodyStart >= 0 && bodyEnd >= 0 )
 312  
                             {
 313  0
                                 bodyStart = licenseContentLC.indexOf( ">", bodyStart ) + 1;
 314  0
                                 String body = licenseContent.substring( bodyStart, bodyEnd );
 315  
 
 316  0
                                 link( licenseUrl.toExternalForm(), "[Original text]" );
 317  0
                                 paragraph( "Copy of the license follows." );
 318  
 
 319  0
                                 body = replaceRelativeLinks( body, baseURL( licenseUrl ).toExternalForm() );
 320  0
                                 sink.rawText( body );
 321  
                             }
 322  
                             else
 323  
                             {
 324  1
                                 verbatimText( licenseContent );
 325  
                             }
 326  
                         }
 327  
                     }
 328  
                 }
 329  
 
 330  1
                 endSection();
 331  
             }
 332  
 
 333  1
             endSection();
 334  1
         }
 335  
 
 336  
         private static URL baseURL( URL aUrl )
 337  
         {
 338  0
             String urlTxt = aUrl.toExternalForm();
 339  0
             int lastSlash = urlTxt.lastIndexOf( '/' );
 340  0
             if ( lastSlash > -1 )
 341  
             {
 342  
                 try
 343  
                 {
 344  0
                     return new URL( urlTxt.substring( 0, lastSlash + 1 ) );
 345  
                 }
 346  0
                 catch ( MalformedURLException e )
 347  
                 {
 348  0
                     throw new AssertionError( e );
 349  
                 }
 350  
             }
 351  
 
 352  0
             return aUrl;
 353  
         }
 354  
 
 355  
         private static String replaceRelativeLinks( String html, String baseURL )
 356  
         {
 357  0
             String url = baseURL;
 358  0
             if ( !url.endsWith( "/" ) )
 359  
             {
 360  0
                 url += "/";
 361  
             }
 362  
 
 363  0
             String serverURL = url.substring( 0, url.indexOf( '/', url.indexOf( "//" ) + 2 ) );
 364  
 
 365  0
             String content = replaceParts( html, url, serverURL, "[aA]", "[hH][rR][eE][fF]" );
 366  0
             content = replaceParts( content, url, serverURL, "[iI][mM][gG]", "[sS][rR][cC]" );
 367  0
             return content;
 368  
         }
 369  
 
 370  
         private static String replaceParts( String html, String baseURL, String serverURL, String tagPattern,
 371  
                                             String attributePattern )
 372  
         {
 373  0
             Pattern anchor = Pattern.compile(
 374  
                 "(<\\s*" + tagPattern + "\\s+[^>]*" + attributePattern + "\\s*=\\s*\")([^\"]*)\"([^>]*>)" );
 375  0
             StringBuffer sb = new StringBuffer( html );
 376  
 
 377  0
             int indx = 0;
 378  0
             boolean done = false;
 379  0
             while ( !done )
 380  
             {
 381  0
                 Matcher mAnchor = anchor.matcher( sb );
 382  0
                 if ( mAnchor.find( indx ) )
 383  
                 {
 384  0
                     indx = mAnchor.end( 3 );
 385  
 
 386  0
                     if ( mAnchor.group( 2 ).startsWith( "#" ) )
 387  
                     {
 388  
                         // relative link - don't want to alter this one!
 389  
                     }
 390  0
                     if ( mAnchor.group( 2 ).startsWith( "/" ) )
 391  
                     {
 392  
                         // root link
 393  0
                         sb.insert( mAnchor.start( 2 ), serverURL );
 394  0
                         indx += serverURL.length();
 395  
                     }
 396  0
                     else if ( mAnchor.group( 2 ).indexOf( ':' ) < 0 )
 397  
                     {
 398  
                         // relative link
 399  0
                         sb.insert( mAnchor.start( 2 ), baseURL );
 400  0
                         indx += baseURL.length();
 401  
                     }
 402  
                 }
 403  
                 else
 404  
                 {
 405  0
                     done = true;
 406  
                 }
 407  
             }
 408  0
             return sb.toString();
 409  
         }
 410  
     }
 411  
 }