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