View Javadoc
1   package org.apache.maven.plugin.checkstyle;
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 com.puppycrawl.tools.checkstyle.DefaultLogger;
23  import com.puppycrawl.tools.checkstyle.XMLLogger;
24  import com.puppycrawl.tools.checkstyle.api.AuditListener;
25  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
26  
27  import org.apache.maven.artifact.Artifact;
28  import org.apache.maven.model.Dependency;
29  import org.apache.maven.model.Plugin;
30  import org.apache.maven.model.Resource;
31  import org.apache.maven.plugin.AbstractMojo;
32  import org.apache.maven.plugin.MojoExecution;
33  import org.apache.maven.plugin.MojoExecutionException;
34  import org.apache.maven.plugin.MojoFailureException;
35  import org.apache.maven.plugin.descriptor.PluginDescriptor;
36  import org.apache.maven.plugins.annotations.Component;
37  import org.apache.maven.plugins.annotations.LifecyclePhase;
38  import org.apache.maven.plugins.annotations.Mojo;
39  import org.apache.maven.plugins.annotations.Parameter;
40  import org.apache.maven.plugins.annotations.ResolutionScope;
41  import org.apache.maven.project.MavenProject;
42  import org.codehaus.plexus.configuration.PlexusConfiguration;
43  import org.codehaus.plexus.util.FileUtils;
44  import org.codehaus.plexus.util.ReaderFactory;
45  import org.codehaus.plexus.util.StringUtils;
46  import org.codehaus.plexus.util.xml.pull.MXParser;
47  import org.codehaus.plexus.util.xml.pull.XmlPullParser;
48  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
49  
50  import java.io.BufferedReader;
51  import java.io.ByteArrayOutputStream;
52  import java.io.File;
53  import java.io.FileNotFoundException;
54  import java.io.FileOutputStream;
55  import java.io.IOException;
56  import java.io.OutputStream;
57  import java.io.Reader;
58  import java.util.ArrayList;
59  import java.util.List;
60  
61  /**
62   * Performs Checkstyle analysis and outputs violations or a count of violations
63   * to the console, potentially failing the build.
64   * It can also be configured to re-use an earlier analysis.
65   *
66   * @author <a href="mailto:joakim@erdfelt.net">Joakim Erdfelt</a>
67   * @version $Id: CheckstyleViolationCheckMojo.java 1586934 2014-04-13 08:26:28Z rfscholte $
68   */
69  @Mojo( name = "check", defaultPhase = LifecyclePhase.VERIFY, requiresDependencyResolution = ResolutionScope.TEST,
70         threadSafe = true )
71  public class CheckstyleViolationCheckMojo
72      extends AbstractMojo
73  {
74  
75      private static final String JAVA_FILES = "**\\/*.java";
76  
77      private static final String CHECKSTYLE_FILE_HEADER = "<?xml version=\"1.0\"?>\n"
78              + "<!DOCTYPE module PUBLIC \"-//Puppy Crawl//DTD Check Configuration 1.2//EN\"\n"
79              + "        \"http://www.puppycrawl.com/dtds/configuration_1_2.dtd\">\n";
80  
81      /**
82       * Specifies the path and filename to save the Checkstyle output. The format
83       * of the output file is determined by the <code>outputFileFormat</code>
84       * parameter.
85       */
86      @Parameter( property = "checkstyle.output.file", defaultValue = "${project.build.directory}/checkstyle-result.xml" )
87      private File outputFile;
88  
89      /**
90       * Specifies the format of the output to be used when writing to the output
91       * file. Valid values are "plain" and "xml".
92       */
93      @Parameter( property = "checkstyle.output.format", defaultValue = "xml" )
94      private String outputFileFormat;
95  
96      /**
97       * Fail the build on a violation. The goal checks for the violations
98       * after logging them (if {@link #logViolationsToConsole} is 'true').
99       * Compare this to {@link #failsOnError} which fails the build immediately
100      * before examining the output log.
101      */
102     @Parameter( property = "checkstyle.failOnViolation", defaultValue = "true" )
103     private boolean failOnViolation;
104 
105     /**
106      * The maximum number of allowed violations. The execution fails only if the
107      * number of violations is above this limit.
108      *
109      * @since 2.3
110      */
111     @Parameter( property = "checkstyle.maxAllowedViolations", defaultValue = "0" )
112     private int maxAllowedViolations;
113 
114     /**
115      * The lowest severity level that is considered a violation.
116      * Valid values are "error", "warning" and "info".
117      *
118      * @since 2.2
119      */
120     @Parameter( property = "checkstyle.violationSeverity", defaultValue = "error" )
121     private String violationSeverity = "error";
122 
123     /**
124      * Skip entire check.
125      *
126      * @since 2.2
127      */
128     @Parameter( property = "checkstyle.skip", defaultValue = "false" )
129     private boolean skip;
130 
131     /**
132      * Skip checktyle execution will only scan the outputFile.
133      *
134      * @since 2.5
135      */
136     @Parameter( property = "checkstyle.skipExec", defaultValue = "false" )
137     private boolean skipExec;
138 
139     /**
140      * Output the detected violations to the console.
141      *
142      * @since 2.3
143      */
144     @Parameter( property = "checkstyle.console", defaultValue = "false" )
145     private boolean logViolationsToConsole;
146 
147     /**
148      * Specifies the location of the resources to be used for Checkstyle.
149      *
150      * @since 2.11
151      */
152     @Parameter( defaultValue = "${project.resources}", readonly = true )
153     protected List<Resource> resources;
154 
155     /**
156      * <p>
157      * Specifies the location of the XML configuration to use.
158      * </p>
159      * <p/>
160      * <p>
161      * Potential values are a filesystem path, a URL, or a classpath resource.
162      * This parameter expects that the contents of the location conform to the
163      * xml format (Checkstyle <a
164      * href="http://checkstyle.sourceforge.net/config.html#Modules">Checker
165      * module</a>) configuration of rulesets.
166      * </p>
167      * <p/>
168      * <p>
169      * This parameter is resolved as resource, URL, then file. If successfully
170      * resolved, the contents of the configuration is copied into the
171      * <code>${project.build.directory}/checkstyle-configuration.xml</code>
172      * file before being passed to Checkstyle as a configuration.
173      * </p>
174      * <p/>
175      * <p>
176      * There are 4 predefined rulesets.
177      * </p>
178      * <p/>
179      * <ul>
180      * <li><code>config/sun_checks.xml</code>: Sun Checks.</li>
181      * <li><code>config/turbine_checks.xml</code>: Turbine Checks.</li>
182      * <li><code>config/avalon_checks.xml</code>: Avalon Checks.</li>
183      * <li><code>config/maven_checks.xml</code>: Maven Source Checks.</li>
184      * </ul>
185      *
186      * @since 2.5
187      */
188     @Parameter( property = "checkstyle.config.location", defaultValue = "config/sun_checks.xml" )
189     private String configLocation;
190 
191     /**
192      * <p>
193      * Specifies the location of the properties file.
194      * </p>
195      * <p/>
196      * <p>
197      * This parameter is resolved as URL, File then resource. If successfully
198      * resolved, the contents of the properties location is copied into the
199      * <code>${project.build.directory}/checkstyle-checker.properties</code>
200      * file before being passed to Checkstyle for loading.
201      * </p>
202      * <p/>
203      * <p>
204      * The contents of the <code>propertiesLocation</code> will be made
205      * available to Checkstyle for specifying values for parameters within the
206      * xml configuration (specified in the <code>configLocation</code>
207      * parameter).
208      * </p>
209      *
210      * @since 2.5
211      */
212     @Parameter( property = "checkstyle.properties.location" )
213     private String propertiesLocation;
214 
215     /**
216      * Allows for specifying raw property expansion information.
217      */
218     @Parameter
219     private String propertyExpansion;
220 
221     /**
222      * <p>
223      * Specifies the location of the License file (a.k.a. the header file) that
224      * can be used by Checkstyle to verify that source code has the correct
225      * license header.
226      * </p>
227      * <p>
228      * You need to use ${checkstyle.header.file} in your Checkstyle xml
229      * configuration to reference the name of this header file.
230      * </p>
231      * <p>
232      * For instance:
233      * </p>
234      * <p>
235      * <code>
236      * &lt;module name="RegexpHeader">
237      * &lt;property name="headerFile" value="${checkstyle.header.file}"/>
238      * &lt;/module>
239      * </code>
240      * </p>
241      *
242      * @since 2.0-beta-2
243      */
244     @Parameter( property = "checkstyle.header.file", defaultValue = "LICENSE.txt" )
245     private String headerLocation;
246 
247     /**
248      * Specifies the cache file used to speed up Checkstyle on successive runs.
249      */
250     @Parameter( defaultValue = "${project.build.directory}/checkstyle-cachefile" )
251     private String cacheFile;
252 
253     /**
254      * The key to be used in the properties for the suppressions file.
255      *
256      * @since 2.1
257      */
258     @Parameter( property = "checkstyle.suppression.expression", defaultValue = "checkstyle.suppressions.file" )
259     private String suppressionsFileExpression;
260 
261     /**
262      * <p>
263      * Specifies the location of the suppressions XML file to use.
264      * </p>
265      * <p/>
266      * <p>
267      * This parameter is resolved as resource, URL, then file. If successfully
268      * resolved, the contents of the suppressions XML is copied into the
269      * <code>${project.build.directory}/checkstyle-supressions.xml</code> file
270      * before being passed to Checkstyle for loading.
271      * </p>
272      * <p/>
273      * <p>
274      * See <code>suppressionsFileExpression</code> for the property that will
275      * be made available to your checkstyle configuration.
276      * </p>
277      *
278      * @since 2.0-beta-2
279      */
280     @Parameter( property = "checkstyle.suppressions.location" )
281     private String suppressionsLocation;
282 
283     /**
284      * The file encoding to use when reading the source files. If the property <code>project.build.sourceEncoding</code>
285      * is not set, the platform default encoding is used. <strong>Note:</strong> This parameter always overrides the
286      * property <code>charset</code> from Checkstyle's <code>TreeWalker</code> module.
287      *
288      * @since 2.2
289      */
290     @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
291     private String encoding;
292 
293     /**
294      * @since 2.5
295      */
296     @Component( role = CheckstyleExecutor.class, hint = "default" )
297     protected CheckstyleExecutor checkstyleExecutor;
298 
299     /**
300      * Output errors to console.
301      */
302     @Parameter( property = "checkstyle.consoleOutput", defaultValue = "false" )
303     private boolean consoleOutput;
304 
305     /**
306      * The Maven Project Object.
307      */
308     @Parameter ( defaultValue = "${project}" )
309     protected MavenProject project;
310     
311     /**
312      * The Plugin Descriptor
313      */
314     @Parameter( defaultValue= "${plugin}" )
315     private PluginDescriptor plugin;
316 
317     // remove when requiring Maven 3.x, just use #plugin 
318     @Parameter( defaultValue= "${mojoExecution}" )
319     private MojoExecution mojoExecution;
320 
321     /**
322      * If <code>null</code>, the Checkstyle plugin will display violations on stdout.
323      * Otherwise, a text file will be created with the violations.
324      */
325     @Parameter
326     private File useFile;
327 
328     /**
329      * Specifies the names filter of the source files to be excluded for
330      * Checkstyle.
331      */
332     @Parameter( property = "checkstyle.excludes" )
333     private String excludes;
334 
335     /**
336      * Specifies the names filter of the source files to be used for Checkstyle.
337      */
338     @Parameter( property = "checkstyle.includes", defaultValue = JAVA_FILES, required = true )
339     private String includes;
340 
341     /**
342      * Specifies the names filter of the files to be excluded for
343      * Checkstyle when checking resources.
344      * @since 2.11
345      */
346     @Parameter( property = "checkstyle.resourceExcludes" )
347     private String resourceExcludes;
348 
349     /**
350      * Specifies the names filter of the files to be used for Checkstyle when checking resources.
351      * @since 2.11
352      */
353     @Parameter( property = "checkstyle.resourceIncludes", defaultValue = "**/*.properties", required = true )
354     private String resourceIncludes;
355 
356     /**
357      * If this is true, and checkstyle reported any violations or errors,
358      * the build fails immediately after running checkstyle, before checking the log
359      * for {@link #logViolationsToConsole}. If you want to use {@link #logViolationsToConsole},
360      * use {@link #failOnViolation} instead of this.
361      */
362     @Parameter( defaultValue = "false" )
363     private boolean failsOnError;
364 
365     /**
366      * Specifies the location of the test source directory to be used for
367      * Checkstyle.
368      *
369      * @since 2.2
370      */
371     @Parameter( defaultValue = "${project.build.testSourceDirectory}" )
372     private File testSourceDirectory;
373 
374     /**
375      * Include or not the test source directory to be used for Checkstyle.
376      *
377      * @since 2.2
378      */
379     @Parameter( defaultValue = "false" )
380     private boolean includeTestSourceDirectory;
381 
382     /**
383      * Specifies the location of the source directory to be used for Checkstyle.
384      */
385     @Parameter( defaultValue = "${project.build.sourceDirectory}", required = true )
386     private File sourceDirectory;
387 
388     /**
389      * Whether to apply Checkstyle to resource directories.
390      * @since 2.11
391      */
392     @Parameter( property = "checkstyle.includeResources", defaultValue = "true", required = true )
393     private boolean includeResources = true;
394 
395     /**
396      * Whether to apply Checkstyle to test resource directories.
397      * @since 2.11
398      */
399     @Parameter( property = "checkstyle.includeTestResources", defaultValue = "true", required = true )
400     private boolean includeTestResources = true;
401 
402     /**
403      * By using this property, you can specify the whole checkstyle rules
404      * inline directly inside this pom.
405      *
406      * <pre>
407      * &lt;plugin&gt;
408      *   ...
409      *   &lt;configuration&gt;
410      *     &lt;checkstyleRules&gt;
411      *       &lt;module name="Checker"&gt;
412      *         &lt;module name="FileTabCharacter"&gt;
413      *           &lt;property name="eachLine" value="true" /&gt;
414      *         &lt;/module&gt;
415      *         &lt;module name="TreeWalker"&gt;
416      *           &lt;module name="EmptyBlock"/&gt;
417      *         &lt;/module&gt;
418      *       &lt;/module&gt;
419      *     &lt;/checkstyleRules&gt;
420      *   &lt;/configuration&gt;
421      *   ...
422      * </pre>
423      *
424      * @since 2.12
425      */
426     @Parameter
427     private PlexusConfiguration checkstyleRules;
428 
429     /**
430      * dump file for inlined checkstyle rules 
431      */
432     @Parameter( property = "checkstyle.output.rules.file", defaultValue = "${project.build.directory}/checkstyle-rules.xml" )
433     private File rulesFiles;
434 
435     private ByteArrayOutputStream stringOutputStream;
436 
437     /** {@inheritDoc} */
438     public void execute()
439         throws MojoExecutionException, MojoFailureException
440     {
441 
442         if ( !skip )
443         {
444 
445             if ( !skipExec )
446             {
447 
448                 if ( checkstyleRules != null )
449                 {
450                     if ( ! "config/sun_checks.xml".equals( configLocation ) )
451                     {
452                         throw new MojoExecutionException( "If you use inline configuration for rules don't specify a configLocation" );
453                     }
454                     if ( checkstyleRules.getChildCount() > 1 )
455                     {
456                         throw new MojoExecutionException( "Currently only one root module is supported" );
457                     }
458                     PlexusConfiguration checkerModule = checkstyleRules.getChild( 0 );
459 
460                     try
461                     {
462                         FileUtils.forceMkdir( rulesFiles.getParentFile() );
463                         FileUtils.fileWrite( rulesFiles, CHECKSTYLE_FILE_HEADER + checkerModule.toString() );
464                     }
465                     catch ( final IOException e )
466                     {
467                         throw new MojoExecutionException( e.getMessage(), e );
468                     }
469                     configLocation = rulesFiles.getAbsolutePath();
470                 }
471 
472                 ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
473 
474                 try
475                 {
476                     CheckstyleExecutorRequest request = new CheckstyleExecutorRequest();
477                     request.setConsoleListener( getConsoleListener() ).setConsoleOutput( consoleOutput )
478                         .setExcludes( excludes ).setFailsOnError( failsOnError ).setIncludes( includes )
479                         .setResourceIncludes( resourceIncludes )
480                         .setResourceExcludes( resourceExcludes )
481                         .setIncludeResources( includeResources )
482                         .setIncludeTestResources( includeTestResources )
483                         .setIncludeTestSourceDirectory( includeTestSourceDirectory ).setListener( getListener() )
484                         .setLog( getLog() ).setProject( project ).setSourceDirectory( sourceDirectory )
485                         .setResources( resources )
486                         .setStringOutputStream( stringOutputStream ).setSuppressionsLocation( suppressionsLocation )
487                         .setTestSourceDirectory( testSourceDirectory ).setConfigLocation( configLocation )
488                         .setConfigurationArtifacts( collectArtifacts( "config" ) ).setPropertyExpansion( propertyExpansion )
489                         .setHeaderLocation( headerLocation ).setLicenseArtifacts( collectArtifacts( "license" ) )
490                         .setCacheFile( cacheFile ).setSuppressionsFileExpression( suppressionsFileExpression )
491                         .setEncoding( encoding ).setPropertiesLocation( propertiesLocation );
492                     checkstyleExecutor.executeCheckstyle( request );
493 
494                 }
495                 catch ( CheckstyleException e )
496                 {
497                     throw new MojoExecutionException( "Failed during checkstyle configuration", e );
498                 }
499                 catch ( CheckstyleExecutorException e )
500                 {
501                     throw new MojoExecutionException( "Failed during checkstyle execution", e );
502                 }
503                 finally
504                 {
505                     //be sure to restore original context classloader
506                     Thread.currentThread().setContextClassLoader( currentClassLoader );
507                 }
508 
509             }
510             if ( !"xml".equals( outputFileFormat ) )
511             {
512                 throw new MojoExecutionException( "Output format is '" + outputFileFormat
513                     + "', checkstyle:check requires format to be 'xml'." );
514             }
515 
516             if ( !outputFile.exists() )
517             {
518                 getLog().info(
519                                "Unable to perform checkstyle:check, "
520                                    + "unable to find checkstyle:checkstyle outputFile." );
521                 return;
522             }
523 
524             try
525             {
526                 XmlPullParser xpp = new MXParser();
527                 Reader freader = ReaderFactory.newXmlReader( outputFile );
528                 BufferedReader breader = new BufferedReader( freader );
529                 xpp.setInput( breader );
530 
531                 int violations = countViolations( xpp );
532                 if ( violations > maxAllowedViolations )
533                 {
534                     if ( failOnViolation )
535                     {
536                         String msg = "You have " + violations + " Checkstyle violation"
537                             + ( ( violations > 1 ) ? "s" : "" ) + ".";
538                         if ( maxAllowedViolations > 0 )
539                         {
540                             msg += " The maximum number of allowed violations is " + maxAllowedViolations + ".";
541                         }
542                         throw new MojoFailureException( msg );
543                     }
544 
545                     getLog().warn( "checkstyle:check violations detected but failOnViolation set to false" );
546                 }
547             }
548             catch ( IOException e )
549             {
550                 throw new MojoExecutionException( "Unable to read Checkstyle results xml: "
551                     + outputFile.getAbsolutePath(), e );
552             }
553             catch ( XmlPullParserException e )
554             {
555                 throw new MojoExecutionException( "Unable to read Checkstyle results xml: "
556                     + outputFile.getAbsolutePath(), e );
557             }
558         }
559     }
560 
561     private int countViolations( XmlPullParser xpp )
562         throws XmlPullParserException, IOException
563     {
564         int count = 0;
565 
566         int eventType = xpp.getEventType();
567         String file = "";
568         while ( eventType != XmlPullParser.END_DOCUMENT )
569         {
570             if ( eventType == XmlPullParser.START_TAG && "file".equals( xpp.getName() ) )
571             {
572                 file = xpp.getAttributeValue( "", "name" );
573                 file = file.substring( file.lastIndexOf( File.separatorChar ) + 1 );
574             }
575 
576             if ( eventType == XmlPullParser.START_TAG && "error".equals( xpp.getName() )
577                 && isViolation( xpp.getAttributeValue( "", "severity" ) ) )
578             {
579                 if ( logViolationsToConsole )
580                 {
581                     final String column = xpp.getAttributeValue( "", "column" ) == null ? "n/a" : xpp.getAttributeValue( "", "column" );
582                     final String logMessage = file + '[' + xpp.getAttributeValue( "", "line" ) + ':' + column + "] "
583                         + xpp.getAttributeValue( "", "message" );
584                     if ( "info".equals( xpp.getAttributeValue( "", "severity" ) ) )
585                     {
586                         getLog().info( logMessage );
587                     }
588                     else if ( "warning".equals( xpp.getAttributeValue( "", "severity" ) ) )
589                     {
590                         getLog().warn( logMessage );
591                     }
592                     else
593                     {
594                         getLog().error( logMessage );
595                     }
596                 }
597                 count++;
598             }
599             eventType = xpp.next();
600         }
601 
602         return count;
603     }
604 
605     /**
606      * Checks if the given severity is considered a violation.
607      *
608      * @param severity The severity to check
609      * @return <code>true</code> if the given severity is a violation, otherwise <code>false</code>
610      */
611     private boolean isViolation( String severity )
612     {
613         if ( "error".equals( severity ) )
614         {
615             return "error".equals( violationSeverity ) || "warning".equals( violationSeverity )
616                 || "info".equals( violationSeverity );
617         }
618         else if ( "warning".equals( severity ) )
619         {
620             return "warning".equals( violationSeverity ) || "info".equals( violationSeverity );
621         }
622         else if ( "info".equals( severity ) )
623         {
624             return "info".equals( violationSeverity );
625         }
626         else
627         {
628             return false;
629         }
630     }
631     private DefaultLogger getConsoleListener()
632         throws MojoExecutionException
633     {
634         DefaultLogger consoleListener;
635 
636         if ( useFile == null )
637         {
638             stringOutputStream = new ByteArrayOutputStream();
639             consoleListener = new DefaultLogger( stringOutputStream, false );
640         }
641         else
642         {
643             OutputStream out = getOutputStream( useFile );
644 
645             consoleListener = new DefaultLogger( out, true );
646         }
647 
648         return consoleListener;
649     }
650 
651     private OutputStream getOutputStream( File file )
652         throws MojoExecutionException
653     {
654         File parentFile = file.getAbsoluteFile().getParentFile();
655 
656         if ( !parentFile.exists() )
657         {
658             parentFile.mkdirs();
659         }
660 
661         FileOutputStream fileOutputStream;
662         try
663         {
664             fileOutputStream = new FileOutputStream( file );
665         }
666         catch ( FileNotFoundException e )
667         {
668             throw new MojoExecutionException( "Unable to create output stream: " + file, e );
669         }
670         return fileOutputStream;
671     }
672 
673     private AuditListener getListener()
674         throws MojoFailureException, MojoExecutionException
675     {
676         AuditListener listener = null;
677 
678         if ( StringUtils.isNotEmpty( outputFileFormat ) )
679         {
680             File resultFile = outputFile;
681 
682             OutputStream out = getOutputStream( resultFile );
683 
684             if ( "xml".equals( outputFileFormat ) )
685             {
686                 listener = new XMLLogger( out, true );
687             }
688             else if ( "plain".equals( outputFileFormat ) )
689             {
690                 listener = new DefaultLogger( out, true );
691             }
692             else
693             {
694                 throw new MojoFailureException( "Invalid output file format: (" + outputFileFormat
695                     + "). Must be 'plain' or 'xml'." );
696             }
697         }
698 
699         return listener;
700     }
701     
702     private List<Artifact> collectArtifacts( String hint )
703     {
704         if ( plugin == null || plugin.getGroupId() == null )
705         {
706             // Maven 2.x workaround
707             plugin = mojoExecution.getMojoDescriptor().getPluginDescriptor();
708         }
709         
710         List<Artifact> artifacts = new ArrayList<Artifact>();
711 
712         Plugin checkstylePlugin =
713             (Plugin) project.getBuild().getPluginsAsMap().get( plugin.getGroupId() + ":" + plugin.getArtifactId() );
714         if ( checkstylePlugin != null )
715         {
716             for ( Dependency dep : checkstylePlugin.getDependencies() )
717             {
718                 // @todo if we can filter on hints, it should be done here...
719                 String depKey = dep.getGroupId() + ":" + dep.getArtifactId();
720                 artifacts.add( (Artifact) plugin.getArtifactMap().get( depKey ) );
721             }
722         }
723 
724         return artifacts;
725     }
726 
727 }