View Javadoc
1   package org.apache.maven.plugin.pmd;
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.File;
23  import java.io.FileReader;
24  import java.io.IOException;
25  import java.io.LineNumberReader;
26  import java.util.ArrayList;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Set;
30  
31  import org.apache.maven.plugin.MojoExecutionException;
32  import org.apache.maven.plugin.MojoFailureException;
33  import org.apache.maven.plugin.pmd.model.CpdErrorDetail;
34  import org.apache.maven.plugin.pmd.model.CpdFile;
35  import org.apache.maven.plugin.pmd.model.Duplication;
36  import org.apache.maven.plugin.pmd.model.io.xpp3.CpdXpp3Reader;
37  import org.apache.maven.plugins.annotations.Execute;
38  import org.apache.maven.plugins.annotations.LifecyclePhase;
39  import org.apache.maven.plugins.annotations.Mojo;
40  import org.apache.maven.plugins.annotations.Parameter;
41  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
42  
43  /**
44   * Fail the build if there were any CPD violations in the source code.
45   *
46   * @version $Id: CpdViolationCheckMojo.html 938498 2015-01-31 17:43:24Z michaelo $
47   * @since 2.0
48   */
49  @Mojo( name = "cpd-check", defaultPhase = LifecyclePhase.VERIFY, threadSafe = true )
50  @Execute( goal = "cpd" )
51  public class CpdViolationCheckMojo
52      extends AbstractPmdViolationCheckMojo<Duplication>
53  {
54  
55      /**
56       * Skip the CPD violation checks. Most useful on the command line via "-Dcpd.skip=true".
57       */
58      @Parameter( property = "cpd.skip", defaultValue = "false" )
59      private boolean skip;
60  
61      private final List<Set<String>> exclusionList = new ArrayList<Set<String>>();
62  
63      /**
64       * Whether to fail the build if the validation check fails.
65       *
66       * @since 3.0
67       */
68      @Parameter( property = "cpd.failOnViolation", defaultValue = "true", required = true )
69      protected boolean failOnViolation;
70  
71      /**
72       * {@inheritDoc}
73       */
74      public void execute()
75          throws MojoExecutionException, MojoFailureException
76      {
77          if ( !skip )
78          {
79              executeCheck( "cpd.xml", "duplication", "CPD duplication", 10 );
80          }
81      }
82  
83      /**
84       * {@inheritDoc}
85       */
86      @Override
87      protected void printError( Duplication item, String severity )
88      {
89          int lines = item.getLines();
90  
91          StringBuilder buff = new StringBuilder( 100 );
92          buff.append( "CPD " ).append( severity ).append( ": Found " );
93          buff.append( lines ).append( " lines of duplicated code at locations:" );
94          this.getLog().info( buff.toString() );
95  
96          for ( CpdFile file : item.getFiles() )
97          {
98              buff.setLength( 0 );
99              buff.append( "    " );
100             buff.append( file.getPath() );
101             buff.append( " line " ).append( file.getLine() );
102             this.getLog().info( buff.toString() );
103         }
104 
105         this.getLog().debug( "CPD " + severity + ": Code Fragment " );
106         this.getLog().debug( item.getCodefragment() );
107     }
108 
109     /**
110      * {@inheritDoc}
111      */
112     @Override
113     protected List<Duplication> getErrorDetails( File cpdFile )
114         throws XmlPullParserException, IOException
115     {
116         CpdXpp3Reader reader = new CpdXpp3Reader();
117         CpdErrorDetail details = reader.read( new FileReader( cpdFile ), false );
118         return details.getDuplications();
119     }
120 
121     @Override
122     protected boolean isExcludedFromFailure( final Duplication errorDetail )
123     {
124         final Set<String> uniquePaths = new HashSet<String>();
125         for ( final CpdFile cpdFile : errorDetail.getFiles() )
126         {
127             uniquePaths.add( cpdFile.getPath() );
128         }
129         for ( final Set<String> singleExclusionGroup : exclusionList )
130         {
131             if ( uniquePaths.size() == singleExclusionGroup.size()
132                 && duplicationExcludedByGroup( uniquePaths, singleExclusionGroup ) )
133             {
134                 return true;
135             }
136         }
137         return false;
138     }
139 
140     private boolean duplicationExcludedByGroup( final Set<String> uniquePaths, final Set<String> singleExclusionGroup )
141     {
142         for ( final String path : uniquePaths )
143         {
144             if ( !fileExcludedByGroup( path, singleExclusionGroup ) )
145             {
146                 return false;
147             }
148         }
149         return true;
150     }
151 
152     private boolean fileExcludedByGroup( final String path, final Set<String> singleExclusionGroup )
153     {
154         final String formattedPath = path.replace( '\\', '.' ).replace( '/', '.' );
155         for ( final String className : singleExclusionGroup )
156         {
157             if ( formattedPath.contains( className ) )
158             {
159                 return true;
160             }
161         }
162         return false;
163     }
164 
165     @Override
166     protected void loadExcludeFromFailuresData( final String excludeFromFailureFile )
167         throws MojoExecutionException
168     {
169         LineNumberReader reader = null;
170         try
171         {
172             reader = new LineNumberReader( new FileReader( excludeFromFailureFile ) );
173             String line;
174             while ( ( line = reader.readLine() ) != null )
175             {
176                 exclusionList.add( createSetFromExclusionLine( line ) );
177             }
178         }
179         catch ( final IOException e )
180         {
181             throw new MojoExecutionException( "Cannot load file " + excludeFromFailureFile, e );
182         }
183         finally
184         {
185             if ( reader != null )
186             {
187                 try
188                 {
189                     reader.close();
190                 }
191                 catch ( final IOException e )
192                 {
193                     getLog().warn( "Cannot close file " + excludeFromFailureFile, e );
194                 }
195             }
196         }
197 
198     }
199 
200     private Set<String> createSetFromExclusionLine( final String line )
201     {
202         final Set<String> result = new HashSet<String>();
203         for ( final String className : line.split( "," ) )
204         {
205             result.add( className.trim() );
206         }
207         return result;
208     }
209 
210     @Override
211     protected int getPriority( Duplication errorDetail )
212     {
213         return 0;
214     }
215 
216     @Override
217     protected ViolationDetails<Duplication> newViolationDetailsInstance()
218     {
219         return new ViolationDetails<Duplication>();
220     }
221 
222     @Override
223     public boolean isFailOnViolation()
224     {
225         return failOnViolation;
226     }
227 }