1 package org.apache.maven.plugins.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.ByteArrayOutputStream;
23 import java.io.IOException;
24 import java.io.UnsupportedEncodingException;
25 import java.util.Locale;
26 import java.util.Properties;
27 import java.util.ResourceBundle;
28
29 import org.apache.maven.plugins.annotations.Mojo;
30 import org.apache.maven.plugins.annotations.Parameter;
31 import org.apache.maven.plugins.pmd.exec.CpdExecutor;
32 import org.apache.maven.plugins.pmd.exec.CpdRequest;
33 import org.apache.maven.plugins.pmd.exec.CpdResult;
34 import org.apache.maven.reporting.MavenReportException;
35 import org.apache.maven.shared.utils.logging.MessageUtils;
36 import org.apache.maven.toolchain.Toolchain;
37 import org.codehaus.plexus.util.StringUtils;
38 import org.codehaus.plexus.util.WriterFactory;
39
40 import net.sourceforge.pmd.cpd.JavaTokenizer;
41 import net.sourceforge.pmd.cpd.renderer.CPDRenderer;
42
43
44
45
46
47
48
49
50
51
52 @Mojo( name = "cpd", threadSafe = true )
53 public class CpdReport
54 extends AbstractPmdReport
55 {
56
57
58
59
60
61
62 @Parameter( defaultValue = "java" )
63 private String language;
64
65
66
67
68 @Parameter( property = "minimumTokens", defaultValue = "100" )
69 private int minimumTokens;
70
71
72
73
74
75
76 @Parameter( property = "cpd.skip", defaultValue = "false" )
77 private boolean skip;
78
79
80
81
82
83
84
85
86 @Parameter( property = "cpd.ignoreLiterals", defaultValue = "false" )
87 private boolean ignoreLiterals;
88
89
90
91
92
93
94 @Parameter( property = "cpd.ignoreIdentifiers", defaultValue = "false" )
95 private boolean ignoreIdentifiers;
96
97
98
99
100
101
102 @Parameter( property = "cpd.ignoreAnnotations", defaultValue = "false" )
103 private boolean ignoreAnnotations;
104
105
106
107
108
109
110 private CpdResult cpdResult;
111
112
113
114
115 public String getName( Locale locale )
116 {
117 return getBundle( locale ).getString( "report.cpd.name" );
118 }
119
120
121
122
123 public String getDescription( Locale locale )
124 {
125 return getBundle( locale ).getString( "report.cpd.description" );
126 }
127
128
129
130
131 @Override
132 public void executeReport( Locale locale )
133 throws MavenReportException
134 {
135 try
136 {
137 execute( locale );
138 }
139 finally
140 {
141 if ( getSink() != null )
142 {
143 getSink().close();
144 }
145 }
146 }
147
148 private void execute( Locale locale )
149 throws MavenReportException
150 {
151 if ( !skip && canGenerateReport() )
152 {
153 ClassLoader origLoader = Thread.currentThread().getContextClassLoader();
154 try
155 {
156 Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader() );
157
158 generateMavenSiteReport( locale );
159 }
160 finally
161 {
162 Thread.currentThread().setContextClassLoader( origLoader );
163 }
164
165 }
166 }
167
168 @Override
169 public boolean canGenerateReport()
170 {
171 if ( skip )
172 {
173 return false;
174 }
175
176 boolean result = super.canGenerateReport();
177 if ( result )
178 {
179 try
180 {
181 executeCpd();
182 if ( skipEmptyReport )
183 {
184 result = cpdResult.hasDuplications();
185 if ( result )
186 {
187 getLog().debug( "Skipping report since skipEmptyReport is true and there are no CPD issues." );
188 }
189 }
190 }
191 catch ( MavenReportException e )
192 {
193 throw new RuntimeException( e );
194 }
195 }
196 return result;
197 }
198
199 private void executeCpd()
200 throws MavenReportException
201 {
202 if ( cpdResult != null )
203 {
204
205 getLog().debug( "CPD has already been run - skipping redundant execution." );
206 return;
207 }
208
209 Properties languageProperties = new Properties();
210 if ( ignoreLiterals )
211 {
212 languageProperties.setProperty( JavaTokenizer.IGNORE_LITERALS, "true" );
213 }
214 if ( ignoreIdentifiers )
215 {
216 languageProperties.setProperty( JavaTokenizer.IGNORE_IDENTIFIERS, "true" );
217 }
218 if ( ignoreAnnotations )
219 {
220 languageProperties.setProperty( JavaTokenizer.IGNORE_ANNOTATIONS, "true" );
221 }
222 try
223 {
224 filesToProcess = getFilesToProcess();
225
226 CpdRequest request = new CpdRequest();
227 request.setMinimumTokens( minimumTokens );
228 request.setLanguage( language );
229 request.setLanguageProperties( languageProperties );
230 request.setSourceEncoding( determineEncoding( !filesToProcess.isEmpty() ) );
231 request.addFiles( filesToProcess.keySet() );
232
233 request.setShowPmdLog( showPmdLog );
234 request.setColorizedLog( MessageUtils.isColorEnabled() );
235 request.setLogLevel( determineCurrentRootLogLevel() );
236
237 request.setExcludeFromFailureFile( excludeFromFailureFile );
238 request.setTargetDirectory( targetDirectory.getAbsolutePath() );
239 request.setOutputEncoding( getOutputEncoding() );
240 request.setFormat( format );
241 request.setIncludeXmlInSite( includeXmlInSite );
242 request.setReportOutputDirectory( getReportOutputDirectory().getAbsolutePath() );
243
244 Toolchain tc = getToolchain();
245 if ( tc != null )
246 {
247 getLog().info( "Toolchain in maven-pmd-plugin: " + tc );
248 String javaExecutable = tc.findTool( "java" );
249 request.setJavaExecutable( javaExecutable );
250 }
251
252 cpdResult = CpdExecutor.execute( request );
253 }
254 catch ( UnsupportedEncodingException e )
255 {
256 throw new MavenReportException( "Encoding '" + getSourceEncoding() + "' is not supported.", e );
257 }
258 catch ( IOException e )
259 {
260 throw new MavenReportException( e.getMessage(), e );
261 }
262 }
263
264 private void generateMavenSiteReport( Locale locale )
265 {
266 CpdReportGenerator gen = new CpdReportGenerator( getSink(), filesToProcess, getBundle( locale ), aggregate );
267 gen.generate( cpdResult.getDuplications() );
268 }
269
270 private String determineEncoding( boolean showWarn )
271 throws UnsupportedEncodingException
272 {
273 String encoding = WriterFactory.FILE_ENCODING;
274 if ( StringUtils.isNotEmpty( getSourceEncoding() ) )
275 {
276
277 encoding = getSourceEncoding();
278
279 WriterFactory.newWriter( new ByteArrayOutputStream(), encoding );
280
281 }
282 else if ( showWarn )
283 {
284 getLog().warn( "File encoding has not been set, using platform encoding " + WriterFactory.FILE_ENCODING
285 + ", i.e. build is platform dependent!" );
286 encoding = WriterFactory.FILE_ENCODING;
287 }
288 return encoding;
289 }
290
291
292
293
294 public String getOutputName()
295 {
296 return "cpd";
297 }
298
299 private static ResourceBundle getBundle( Locale locale )
300 {
301 return ResourceBundle.getBundle( "cpd-report", locale, CpdReport.class.getClassLoader() );
302 }
303
304
305
306
307
308
309
310
311 @Deprecated
312 public CPDRenderer createRenderer() throws MavenReportException
313 {
314 return CpdExecutor.createRenderer( format, getOutputEncoding() );
315 }
316 }