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