1 package org.apache.maven.plugin.pmd;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
45
46
47
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
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
65
66
67
68 @Parameter( property = "cpd.failOnViolation", defaultValue = "true", required = true )
69 protected boolean failOnViolation;
70
71
72
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
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
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 }