Coverage Report - org.apache.maven.tools.plugin.generator.GeneratorUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
GeneratorUtils
76 %
127/165
71 %
54/76
5,7
GeneratorUtils$MojoParserCallback
60 %
44/73
45 %
36/80
5,7
GeneratorUtils$MojoParserCallback$Counter
0 %
0/1
N/A
5,7
 
 1  
 package org.apache.maven.tools.plugin.generator;
 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.ByteArrayInputStream;
 23  
 import java.io.ByteArrayOutputStream;
 24  
 import java.io.File;
 25  
 import java.io.IOException;
 26  
 import java.io.StringReader;
 27  
 import java.io.UnsupportedEncodingException;
 28  
 import java.net.MalformedURLException;
 29  
 import java.net.URL;
 30  
 import java.net.URLClassLoader;
 31  
 import java.util.ArrayList;
 32  
 import java.util.HashMap;
 33  
 import java.util.LinkedList;
 34  
 import java.util.List;
 35  
 import java.util.Map;
 36  
 import java.util.Stack;
 37  
 import java.util.regex.Matcher;
 38  
 import java.util.regex.Pattern;
 39  
 
 40  
 import javax.swing.text.MutableAttributeSet;
 41  
 import javax.swing.text.html.HTML;
 42  
 import javax.swing.text.html.HTMLEditorKit;
 43  
 import javax.swing.text.html.parser.ParserDelegator;
 44  
 
 45  
 import org.apache.maven.artifact.DependencyResolutionRequiredException;
 46  
 import org.apache.maven.model.Dependency;
 47  
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 48  
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
 49  
 import org.apache.maven.project.MavenProject;
 50  
 import org.apache.maven.reporting.MavenReport;
 51  
 import org.codehaus.plexus.component.repository.ComponentDependency;
 52  
 import org.codehaus.plexus.util.StringUtils;
 53  
 import org.codehaus.plexus.util.xml.XMLWriter;
 54  
 import org.w3c.tidy.Tidy;
 55  
 
 56  
 /**
 57  
  * Convenience methods to play with Maven plugins.
 58  
  *
 59  
  * @author jdcasey
 60  
  * @version $Id: GeneratorUtils.java 1342928 2012-05-26 17:12:27Z hboutemy $
 61  
  */
 62  
 public final class GeneratorUtils
 63  
 {
 64  
     private GeneratorUtils()
 65  0
     {
 66  
         // nop
 67  0
     }
 68  
 
 69  
     /**
 70  
      * @param w not null writer
 71  
      * @param pluginDescriptor not null
 72  
      */
 73  
     public static void writeDependencies( XMLWriter w, PluginDescriptor pluginDescriptor )
 74  
     {
 75  2
         w.startElement( "dependencies" );
 76  
 
 77  
         @SuppressWarnings( "unchecked" )
 78  2
         List<ComponentDependency> deps = pluginDescriptor.getDependencies();
 79  2
         for ( ComponentDependency dep : deps )
 80  
         {
 81  2
             w.startElement( "dependency" );
 82  
 
 83  2
             element( w, "groupId", dep.getGroupId() );
 84  
 
 85  2
             element( w, "artifactId", dep.getArtifactId() );
 86  
 
 87  2
             element( w, "type", dep.getType() );
 88  
 
 89  2
             element( w, "version", dep.getVersion() );
 90  
 
 91  2
             w.endElement();
 92  
         }
 93  
 
 94  2
         w.endElement();
 95  2
     }
 96  
 
 97  
     /**
 98  
      * @param w not null writer
 99  
      * @param name  not null
 100  
      * @param value could be null
 101  
      */
 102  
     public static void element( XMLWriter w, String name, String value )
 103  
     {
 104  46
         w.startElement( name );
 105  
 
 106  46
         if ( value == null )
 107  
         {
 108  7
             value = "";
 109  
         }
 110  
 
 111  46
         w.writeText( value );
 112  
 
 113  46
         w.endElement();
 114  46
     }
 115  
 
 116  
     public static void element( XMLWriter w, String name, String value, boolean asText )
 117  
     {
 118  4
         element( w, name, asText ? GeneratorUtils.toText( value ) : value );
 119  4
     }
 120  
     
 121  
     /**
 122  
      * @param dependencies not null list of <code>Dependency</code>
 123  
      * @return list of component dependencies
 124  
      */
 125  
     public static List<ComponentDependency> toComponentDependencies( List<Dependency> dependencies )
 126  
     {
 127  0
         List<ComponentDependency> componentDeps = new LinkedList<ComponentDependency>();
 128  
 
 129  0
         for ( Dependency dependency : dependencies )
 130  
         {
 131  0
             ComponentDependency cd = new ComponentDependency();
 132  
 
 133  0
             cd.setArtifactId( dependency.getArtifactId() );
 134  0
             cd.setGroupId( dependency.getGroupId() );
 135  0
             cd.setVersion( dependency.getVersion() );
 136  0
             cd.setType( dependency.getType() );
 137  
 
 138  0
             componentDeps.add( cd );
 139  0
         }
 140  
 
 141  0
         return componentDeps;
 142  
     }
 143  
 
 144  
     /**
 145  
      * Returns a literal replacement <code>String</code> for the specified <code>String</code>. This method
 146  
      * produces a <code>String</code> that will work as a literal replacement <code>s</code> in the
 147  
      * <code>appendReplacement</code> method of the {@link Matcher} class. The <code>String</code> produced will
 148  
      * match the sequence of characters in <code>s</code> treated as a literal sequence. Slashes ('\') and dollar
 149  
      * signs ('$') will be given no special meaning. TODO: copied from Matcher class of Java 1.5, remove once target
 150  
      * platform can be upgraded
 151  
      *
 152  
      * @see <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/Matcher.html">java.util.regex.Matcher</a>
 153  
      * @param s The string to be literalized
 154  
      * @return A literal string replacement
 155  
      */
 156  
     private static String quoteReplacement( String s )
 157  
     {
 158  17
         if ( ( s.indexOf( '\\' ) == -1 ) && ( s.indexOf( '$' ) == -1 ) )
 159  
         {
 160  17
             return s;
 161  
         }
 162  
 
 163  0
         StringBuilder sb = new StringBuilder();
 164  0
         for ( int i = 0; i < s.length(); i++ )
 165  
         {
 166  0
             char c = s.charAt( i );
 167  0
             if ( c == '\\' )
 168  
             {
 169  0
                 sb.append( '\\' );
 170  0
                 sb.append( '\\' );
 171  
             }
 172  0
             else if ( c == '$' )
 173  
             {
 174  0
                 sb.append( '\\' );
 175  0
                 sb.append( '$' );
 176  
             }
 177  
             else
 178  
             {
 179  0
                 sb.append( c );
 180  
             }
 181  
         }
 182  
 
 183  0
         return sb.toString();
 184  
     }
 185  
 
 186  
     /**
 187  
      * Decodes javadoc inline tags into equivalent HTML tags. For instance, the inline tag "{@code <A&B>}" should be
 188  
      * rendered as "<code>&lt;A&amp;B&gt;</code>".
 189  
      *
 190  
      * @param description The javadoc description to decode, may be <code>null</code>.
 191  
      * @return The decoded description, never <code>null</code>.
 192  
      */
 193  
     static String decodeJavadocTags( String description )
 194  
     {
 195  33
         if ( StringUtils.isEmpty( description ) )
 196  
         {
 197  2
             return "";
 198  
         }
 199  
 
 200  31
         StringBuffer decoded = new StringBuffer( description.length() + 1024 );
 201  
 
 202  31
         Matcher matcher = Pattern.compile( "\\{@(\\w+)\\s*([^\\}]*)\\}" ).matcher( description );
 203  48
         while ( matcher.find() )
 204  
         {
 205  17
             String tag = matcher.group( 1 );
 206  17
             String text = matcher.group( 2 );
 207  17
             text = StringUtils.replace( text, "&", "&amp;" );
 208  17
             text = StringUtils.replace( text, "<", "&lt;" );
 209  17
             text = StringUtils.replace( text, ">", "&gt;" );
 210  17
             if ( "code".equals( tag ) )
 211  
             {
 212  3
                 text = "<code>" + text + "</code>";
 213  
             }
 214  14
             else if ( "link".equals( tag ) || "linkplain".equals( tag ) || "value".equals( tag ) )
 215  
             {
 216  10
                 String pattern = "(([^#\\.\\s]+\\.)*([^#\\.\\s]+))?" + "(#([^\\(\\s]*)(\\([^\\)]*\\))?\\s*(\\S.*)?)?";
 217  10
                 final int label = 7;
 218  10
                 final int clazz = 3;
 219  10
                 final int member = 5;
 220  10
                 final int args = 6;
 221  10
                 Matcher link = Pattern.compile( pattern ).matcher( text );
 222  10
                 if ( link.matches() )
 223  
                 {
 224  10
                     text = link.group( label );
 225  10
                     if ( StringUtils.isEmpty( text ) )
 226  
                     {
 227  8
                         text = link.group( clazz );
 228  8
                         if ( StringUtils.isEmpty( text ) )
 229  
                         {
 230  4
                             text = "";
 231  
                         }
 232  8
                         if ( StringUtils.isNotEmpty( link.group( member ) ) )
 233  
                         {
 234  6
                             if ( StringUtils.isNotEmpty( text ) )
 235  
                             {
 236  2
                                 text += '.';
 237  
                             }
 238  6
                             text += link.group( member );
 239  6
                             if ( StringUtils.isNotEmpty( link.group( args ) ) )
 240  
                             {
 241  4
                                 text += "()";
 242  
                             }
 243  
                         }
 244  
                     }
 245  
                 }
 246  10
                 if ( !"linkplain".equals( tag ) )
 247  
                 {
 248  1
                     text = "<code>" + text + "</code>";
 249  
                 }
 250  
             }
 251  17
             matcher.appendReplacement( decoded, ( text != null ) ? quoteReplacement( text ) : "" );
 252  17
         }
 253  31
         matcher.appendTail( decoded );
 254  
 
 255  31
         return decoded.toString();
 256  
     }
 257  
 
 258  
     /**
 259  
      * Fixes some javadoc comment to become a valid XHTML snippet.
 260  
      *
 261  
      * @param description Javadoc description with HTML tags, may be <code>null</code>.
 262  
      * @return The description with valid XHTML tags, never <code>null</code>.
 263  
      */
 264  
     public static String makeHtmlValid( String description )
 265  
     {
 266  18
         if ( StringUtils.isEmpty( description ) )
 267  
         {
 268  2
             return "";
 269  
         }
 270  
 
 271  16
         String commentCleaned = decodeJavadocTags( description );
 272  
 
 273  
         // Using jTidy to clean comment
 274  16
         Tidy tidy = new Tidy();
 275  16
         tidy.setDocType( "loose" );
 276  16
         tidy.setXHTML( true );
 277  16
         tidy.setXmlOut( true );
 278  16
         tidy.setInputEncoding( "UTF-8" );
 279  16
         tidy.setOutputEncoding( "UTF-8" );
 280  16
         tidy.setMakeClean( true );
 281  16
         tidy.setNumEntities( true );
 282  16
         tidy.setQuoteNbsp( false );
 283  16
         tidy.setQuiet( true );
 284  16
         tidy.setShowWarnings( false );
 285  
         try
 286  
         {
 287  16
             ByteArrayOutputStream out = new ByteArrayOutputStream( commentCleaned.length() + 256 );
 288  16
             tidy.parse( new ByteArrayInputStream( commentCleaned.getBytes( "UTF-8" ) ), out );
 289  16
             commentCleaned = out.toString( "UTF-8" );
 290  
         }
 291  0
         catch ( UnsupportedEncodingException e )
 292  
         {
 293  
             // cannot happen as every JVM must support UTF-8, see also class javadoc for java.nio.charset.Charset
 294  16
         }
 295  
 
 296  16
         if ( StringUtils.isEmpty( commentCleaned ) )
 297  
         {
 298  0
             return "";
 299  
         }
 300  
 
 301  
         // strip the header/body stuff
 302  16
         String ls = System.getProperty( "line.separator" );
 303  16
         int startPos = commentCleaned.indexOf( "<body>" + ls ) + 6 + ls.length();
 304  16
         int endPos = commentCleaned.indexOf( ls + "</body>" );
 305  16
         commentCleaned = commentCleaned.substring( startPos, endPos );
 306  
 
 307  16
         return commentCleaned;
 308  
     }
 309  
 
 310  
     /**
 311  
      * Converts a HTML fragment as extracted from a javadoc comment to a plain text string. This method tries to retain
 312  
      * as much of the text formatting as possible by means of the following transformations:
 313  
      * <ul>
 314  
      * <li>List items are converted to leading tabs (U+0009), followed by the item number/bullet, another tab and
 315  
      * finally the item contents. Each tab denotes an increase of indentation.</li>
 316  
      * <li>Flow breaking elements as well as literal line terminators in preformatted text are converted to a newline
 317  
      * (U+000A) to denote a mandatory line break.</li>
 318  
      * <li>Consecutive spaces and line terminators from character data outside of preformatted text will be normalized
 319  
      * to a single space. The resulting space denotes a possible point for line wrapping.</li>
 320  
      * <li>Each space in preformatted text will be converted to a non-breaking space (U+00A0).</li>
 321  
      * </ul>
 322  
      *
 323  
      * @param html The HTML fragment to convert to plain text, may be <code>null</code>.
 324  
      * @return A string with HTML tags converted into pure text, never <code>null</code>.
 325  
      * @since 2.4.3
 326  
      */
 327  
     public static String toText( String html )
 328  
     {
 329  11
         if ( StringUtils.isEmpty( html ) )
 330  
         {
 331  3
             return "";
 332  
         }
 333  
 
 334  8
         final StringBuilder sb = new StringBuilder();
 335  
 
 336  8
         HTMLEditorKit.Parser parser = new ParserDelegator();
 337  8
         HTMLEditorKit.ParserCallback htmlCallback = new MojoParserCallback( sb );
 338  
 
 339  
         try
 340  
         {
 341  8
             parser.parse( new StringReader( makeHtmlValid( html ) ), htmlCallback, true );
 342  
         }
 343  0
         catch ( IOException e )
 344  
         {
 345  0
             throw new RuntimeException( e );
 346  8
         }
 347  
 
 348  8
         return sb.toString().replace( '\"', '\'' ); // for CDATA
 349  
     }
 350  
 
 351  
     /**
 352  
      * ParserCallback implementation.
 353  
      */
 354  
     private static class MojoParserCallback
 355  
         extends HTMLEditorKit.ParserCallback
 356  
     {
 357  
         /**
 358  
          * Holds the index of the current item in a numbered list.
 359  
          */
 360  0
         class Counter
 361  
         {
 362  
             public int value;
 363  
         }
 364  
 
 365  
         /**
 366  
          * A flag whether the parser is currently in the body element.
 367  
          */
 368  
         private boolean body;
 369  
 
 370  
         /**
 371  
          * A flag whether the parser is currently processing preformatted text, actually a counter to track nesting.
 372  
          */
 373  
         private int preformatted;
 374  
 
 375  
         /**
 376  
          * The current indentation depth for the output.
 377  
          */
 378  
         private int depth;
 379  
 
 380  
         /**
 381  
          * A stack of {@link Counter} objects corresponding to the nesting of (un-)ordered lists. A
 382  
          * <code>null</code> element denotes an unordered list.
 383  
          */
 384  8
         private Stack<Counter> numbering = new Stack<Counter>();
 385  
 
 386  
         /**
 387  
          * A flag whether an implicit line break is pending in the output buffer. This flag is used to postpone the
 388  
          * output of implicit line breaks until we are sure that are not to be merged with other implicit line
 389  
          * breaks.
 390  
          */
 391  
         private boolean pendingNewline;
 392  
 
 393  
         /**
 394  
          * A flag whether we have just parsed a simple tag.
 395  
          */
 396  
         private boolean simpleTag;
 397  
 
 398  
         /**
 399  
          * The current buffer.
 400  
          */
 401  
         private final StringBuilder sb;
 402  
 
 403  
         /**
 404  
          * @param sb not null
 405  
          */
 406  
         public MojoParserCallback( StringBuilder sb )
 407  8
         {
 408  8
             this.sb = sb;
 409  8
         }
 410  
 
 411  
         /** {@inheritDoc} */
 412  
         public void handleSimpleTag( HTML.Tag t, MutableAttributeSet a, int pos )
 413  
         {
 414  1
             simpleTag = true;
 415  1
             if ( body && HTML.Tag.BR.equals( t ) )
 416  
             {
 417  1
                 newline( false );
 418  
             }
 419  1
         }
 420  
 
 421  
         /** {@inheritDoc} */
 422  
         public void handleStartTag( HTML.Tag t, MutableAttributeSet a, int pos )
 423  
         {
 424  28
             simpleTag = false;
 425  28
             if ( body && ( t.breaksFlow() || t.isBlock() ) )
 426  
             {
 427  0
                 newline( true );
 428  
             }
 429  28
             if ( HTML.Tag.OL.equals( t ) )
 430  
             {
 431  0
                 numbering.push( new Counter() );
 432  
             }
 433  28
             else if ( HTML.Tag.UL.equals( t ) )
 434  
             {
 435  0
                 numbering.push( null );
 436  
             }
 437  28
             else if ( HTML.Tag.LI.equals( t ) )
 438  
             {
 439  0
                 Counter counter = numbering.peek();
 440  0
                 if ( counter == null )
 441  
                 {
 442  0
                     text( "-\t" );
 443  
                 }
 444  
                 else
 445  
                 {
 446  0
                     text( ++counter.value + ".\t" );
 447  
                 }
 448  0
                 depth++;
 449  0
             }
 450  28
             else if ( HTML.Tag.DD.equals( t ) )
 451  
             {
 452  0
                 depth++;
 453  
             }
 454  28
             else if ( t.isPreformatted() )
 455  
             {
 456  0
                 preformatted++;
 457  
             }
 458  28
             else if ( HTML.Tag.BODY.equals( t ) )
 459  
             {
 460  8
                 body = true;
 461  
             }
 462  28
         }
 463  
 
 464  
         /** {@inheritDoc} */
 465  
         public void handleEndTag( HTML.Tag t, int pos )
 466  
         {
 467  28
             if ( HTML.Tag.OL.equals( t ) || HTML.Tag.UL.equals( t ) )
 468  
             {
 469  0
                 numbering.pop();
 470  
             }
 471  28
             else if ( HTML.Tag.LI.equals( t ) || HTML.Tag.DD.equals( t ) )
 472  
             {
 473  0
                 depth--;
 474  
             }
 475  28
             else if ( t.isPreformatted() )
 476  
             {
 477  0
                 preformatted--;
 478  
             }
 479  28
             else if ( HTML.Tag.BODY.equals( t ) )
 480  
             {
 481  8
                 body = false;
 482  
             }
 483  28
             if ( body && ( t.breaksFlow() || t.isBlock() ) && !HTML.Tag.LI.equals( t ) )
 484  
             {
 485  0
                 if ( ( HTML.Tag.P.equals( t ) || HTML.Tag.PRE.equals( t ) || HTML.Tag.OL.equals( t )
 486  
                     || HTML.Tag.UL.equals( t ) || HTML.Tag.DL.equals( t ) )
 487  
                     && numbering.isEmpty() )
 488  
                 {
 489  0
                     pendingNewline = false;
 490  0
                     newline( pendingNewline );
 491  
                 }
 492  
                 else
 493  
                 {
 494  0
                     newline( true );
 495  
                 }
 496  
             }
 497  28
         }
 498  
 
 499  
         /** {@inheritDoc} */
 500  
         public void handleText( char[] data, int pos )
 501  
         {
 502  
             /*
 503  
              * NOTE: Parsers before JRE 1.6 will parse XML-conform simple tags like <br/> as "<br>" followed by
 504  
              * the text event ">..." so we need to watch out for the closing angle bracket.
 505  
              */
 506  16
             int offset = 0;
 507  16
             if ( simpleTag && data[0] == '>' )
 508  
             {
 509  0
                 simpleTag = false;
 510  0
                 for ( ++offset; offset < data.length && data[offset] <= ' '; )
 511  
                 {
 512  0
                     offset++;
 513  
                 }
 514  
             }
 515  16
             if ( offset < data.length )
 516  
             {
 517  16
                 String text = new String( data, offset, data.length - offset );
 518  16
                 text( text );
 519  
             }
 520  16
         }
 521  
 
 522  
         /** {@inheritDoc} */
 523  
         public void flush()
 524  
         {
 525  0
             flushPendingNewline();
 526  0
         }
 527  
 
 528  
         /**
 529  
          * Writes a line break to the plain text output.
 530  
          *
 531  
          * @param implicit A flag whether this is an explicit or implicit line break. Explicit line breaks are
 532  
          *            always written to the output whereas consecutive implicit line breaks are merged into a single
 533  
          *            line break.
 534  
          */
 535  
         private void newline( boolean implicit )
 536  
         {
 537  1
             if ( implicit )
 538  
             {
 539  0
                 pendingNewline = true;
 540  
             }
 541  
             else
 542  
             {
 543  1
                 flushPendingNewline();
 544  1
                 sb.append( '\n' );
 545  
             }
 546  1
         }
 547  
 
 548  
         /**
 549  
          * Flushes a pending newline (if any).
 550  
          */
 551  
         private void flushPendingNewline()
 552  
         {
 553  17
             if ( pendingNewline )
 554  
             {
 555  0
                 pendingNewline = false;
 556  0
                 if ( sb.length() > 0 )
 557  
                 {
 558  0
                     sb.append( '\n' );
 559  
                 }
 560  
             }
 561  17
         }
 562  
 
 563  
         /**
 564  
          * Writes the specified character data to the plain text output. If the last output was a line break, the
 565  
          * character data will automatically be prefixed with the current indent.
 566  
          *
 567  
          * @param data The character data, must not be <code>null</code>.
 568  
          */
 569  
         private void text( String data )
 570  
         {
 571  16
             flushPendingNewline();
 572  16
             if ( sb.length() <= 0 || sb.charAt( sb.length() - 1 ) == '\n' )
 573  
             {
 574  9
                 for ( int i = 0; i < depth; i++ )
 575  
                 {
 576  0
                     sb.append( '\t' );
 577  
                 }
 578  
             }
 579  
             String text;
 580  16
             if ( preformatted > 0 )
 581  
             {
 582  0
                 text = data;
 583  
             }
 584  
             else
 585  
             {
 586  16
                 text = data.replace( '\n', ' ' );
 587  
             }
 588  16
             sb.append( text );
 589  16
         }
 590  
     }
 591  
 
 592  
     /**
 593  
      * Find the best package name, based on the number of hits of actual Mojo classes.
 594  
      *
 595  
      * @param pluginDescriptor not null
 596  
      * @return the best name of the package for the generated mojo
 597  
      */
 598  
     public static String discoverPackageName( PluginDescriptor pluginDescriptor )
 599  
     {
 600  2
         Map<String, Integer> packageNames = new HashMap<String, Integer>();
 601  
         @SuppressWarnings( "unchecked" )
 602  2
         List<MojoDescriptor> mojoDescriptors = pluginDescriptor.getMojos();
 603  2
         if ( mojoDescriptors == null )
 604  
         {
 605  0
             return "";
 606  
         }
 607  2
         for ( MojoDescriptor descriptor : mojoDescriptors )
 608  
         {
 609  
 
 610  2
             String impl = descriptor.getImplementation();
 611  2
             if ( StringUtils.equals( descriptor.getGoal(), "help" ) && StringUtils.equals( "HelpMojo", impl ) )
 612  
             {
 613  0
                 continue;
 614  
             }
 615  2
             if ( impl.lastIndexOf( '.' ) != -1 )
 616  
             {
 617  2
                 String name = impl.substring( 0, impl.lastIndexOf( '.' ) );
 618  2
                 if ( packageNames.get( name ) != null )
 619  
                 {
 620  0
                     int next = ( packageNames.get( name ) ).intValue() + 1;
 621  0
                     packageNames.put( name,  Integer.valueOf( next ) );
 622  0
                 }
 623  
                 else
 624  
                 {
 625  2
                     packageNames.put( name, Integer.valueOf( 1 ) );
 626  
                 }
 627  2
             }
 628  
             else
 629  
             {
 630  0
                 packageNames.put( "", Integer.valueOf( 1 ) );
 631  
             }
 632  2
         }
 633  
 
 634  2
         String packageName = "";
 635  2
         int max = 0;
 636  2
         for ( Map.Entry<String, Integer> entry : packageNames.entrySet() )
 637  
         {
 638  2
             int value = entry.getValue().intValue();
 639  2
             if ( value > max )
 640  
             {
 641  2
                 max = value;
 642  2
                 packageName = entry.getKey();
 643  
             }
 644  2
         }
 645  
 
 646  2
         return packageName;
 647  
     }
 648  
 
 649  
     /**
 650  
      * @param impl a Mojo implementation, not null
 651  
      * @param project a MavenProject instance, could be null
 652  
      * @return <code>true</code> is the Mojo implementation implements <code>MavenReport</code>,
 653  
      * <code>false</code> otherwise.
 654  
      * @throws IllegalArgumentException if any
 655  
      */
 656  
     @SuppressWarnings( "unchecked" )
 657  
     public static boolean isMavenReport( String impl, MavenProject project )
 658  
         throws IllegalArgumentException
 659  
     {
 660  4
         if ( impl == null )
 661  
         {
 662  1
             throw new IllegalArgumentException( "mojo implementation should be declared" );
 663  
         }
 664  
 
 665  3
         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
 666  3
         if ( project != null )
 667  
         {
 668  
             List<String> classPathStrings;
 669  
             try
 670  
             {
 671  2
                 classPathStrings = project.getCompileClasspathElements();
 672  2
                 if ( project.getExecutionProject() != null )
 673  
                 {
 674  0
                     classPathStrings.addAll( project.getExecutionProject().getCompileClasspathElements() );
 675  
                 }
 676  
             }
 677  0
             catch ( DependencyResolutionRequiredException e )
 678  
             {
 679  0
                 throw new IllegalArgumentException( e );
 680  2
             }
 681  
 
 682  2
             List<URL> urls = new ArrayList<URL>( classPathStrings.size() );
 683  2
             for ( String classPathString : classPathStrings )
 684  
             {
 685  
                 try
 686  
                 {
 687  2
                     urls.add( new File( classPathString ).toURL() );
 688  
                 }
 689  0
                 catch ( MalformedURLException e )
 690  
                 {
 691  0
                     throw new IllegalArgumentException( e );
 692  2
                 }
 693  
             }
 694  
 
 695  2
             classLoader = new URLClassLoader( urls.toArray( new URL[urls.size()] ), classLoader );
 696  
         }
 697  
 
 698  
         try
 699  
         {
 700  3
             Class<?> clazz = Class.forName( impl, false, classLoader );
 701  
 
 702  1
             return MavenReport.class.isAssignableFrom( clazz );
 703  
         }
 704  2
         catch ( ClassNotFoundException e )
 705  
         {
 706  2
             return false;
 707  
         }
 708  
     }
 709  
 
 710  
 }