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 net.sourceforge.pmd.cpd.CPD;
23 import net.sourceforge.pmd.cpd.CPDConfiguration;
24 import net.sourceforge.pmd.cpd.CSVRenderer;
25 import net.sourceforge.pmd.cpd.JavaLanguage;
26 import net.sourceforge.pmd.cpd.JavaTokenizer;
27 import net.sourceforge.pmd.cpd.Renderer;
28 import net.sourceforge.pmd.cpd.XMLRenderer;
29 import org.apache.maven.plugins.annotations.Mojo;
30 import org.apache.maven.plugins.annotations.Parameter;
31 import org.apache.maven.reporting.MavenReportException;
32 import org.codehaus.plexus.util.FileUtils;
33 import org.codehaus.plexus.util.IOUtil;
34 import org.codehaus.plexus.util.StringUtils;
35 import org.codehaus.plexus.util.WriterFactory;
36
37 import java.io.ByteArrayOutputStream;
38 import java.io.File;
39 import java.io.FileOutputStream;
40 import java.io.IOException;
41 import java.io.OutputStreamWriter;
42 import java.io.UnsupportedEncodingException;
43 import java.io.Writer;
44 import java.util.Locale;
45 import java.util.Map;
46 import java.util.Properties;
47 import java.util.ResourceBundle;
48
49
50
51
52
53
54
55
56
57
58 @Mojo( name = "cpd", threadSafe = true )
59 public class CpdReport
60 extends AbstractPmdReport
61 {
62
63
64
65 @Parameter( property = "minimumTokens", defaultValue = "100" )
66 private int minimumTokens;
67
68
69
70
71
72
73
74 @Parameter( property = "cpd.skip", defaultValue = "false" )
75 private boolean skip;
76
77
78
79
80
81
82
83
84 @Parameter( property = "cpd.ignoreLiterals", defaultValue = "false" )
85 private boolean ignoreLiterals;
86
87
88
89
90
91
92 @Parameter( property = "cpd.ignoreIdentifiers", defaultValue = "false" )
93 private boolean ignoreIdentifiers;
94
95
96
97
98 public String getName( Locale locale )
99 {
100 return getBundle( locale ).getString( "report.cpd.name" );
101 }
102
103
104
105
106 public String getDescription( Locale locale )
107 {
108 return getBundle( locale ).getString( "report.cpd.description" );
109 }
110
111
112
113
114 public void executeReport( Locale locale )
115 throws MavenReportException
116 {
117 try
118 {
119 execute( locale );
120 }
121 finally
122 {
123 if ( getSink() != null )
124 {
125 getSink().close();
126 }
127 }
128 }
129
130 private void execute( Locale locale )
131 throws MavenReportException
132 {
133 if ( !skip && canGenerateReport() )
134 {
135 ClassLoader origLoader = Thread.currentThread().getContextClassLoader();
136 try
137 {
138 Thread.currentThread().setContextClassLoader( this.getClass().getClassLoader() );
139
140 CPD cpd = generateReport( locale );
141
142 if ( !isHtml() )
143 {
144 writeNonHtml( cpd );
145 }
146 }
147 finally
148 {
149 Thread.currentThread().setContextClassLoader( origLoader );
150 }
151
152 }
153 }
154
155 private CPD generateReport( Locale locale )
156 throws MavenReportException
157 {
158 Properties p = new Properties();
159 if ( ignoreLiterals )
160 {
161 p.setProperty( JavaTokenizer.IGNORE_LITERALS, "true" );
162 }
163 if ( ignoreIdentifiers )
164 {
165 p.setProperty( JavaTokenizer.IGNORE_IDENTIFIERS, "true" );
166 }
167
168 CPD cpd;
169 Map<File, PmdFileInfo> files = null;
170
171 try
172 {
173 files = getFilesToProcess();
174 String encoding = determineEncoding( !files.isEmpty() );
175
176 CPDConfiguration cpdConfiguration = new CPDConfiguration( minimumTokens, new JavaLanguage( p ), encoding );
177 cpd = new CPD( cpdConfiguration );
178
179 for ( File file : files.keySet() )
180 {
181 cpd.add( file );
182 }
183 }
184 catch ( UnsupportedEncodingException e )
185 {
186 throw new MavenReportException( "Encoding '" + getSourceEncoding() + "' is not supported.", e );
187 }
188 catch ( IOException e )
189 {
190 throw new MavenReportException( e.getMessage(), e );
191 }
192 cpd.go();
193
194 CpdReportGenerator gen = new CpdReportGenerator( getSink(), files, getBundle( locale ), aggregate );
195 gen.generate( cpd.getMatches() );
196
197 return cpd;
198 }
199
200 private String determineEncoding( boolean showWarn )
201 throws UnsupportedEncodingException
202 {
203 String encoding = WriterFactory.FILE_ENCODING;
204 if ( StringUtils.isNotEmpty( getSourceEncoding() ) )
205 {
206
207 encoding = getSourceEncoding();
208
209 WriterFactory.newWriter( new ByteArrayOutputStream(), encoding );
210
211 }
212 else if ( showWarn )
213 {
214 getLog().warn( "File encoding has not been set, using platform encoding " + WriterFactory.FILE_ENCODING
215 + ", i.e. build is platform dependent!" );
216 encoding = WriterFactory.FILE_ENCODING;
217 }
218 return encoding;
219 }
220
221 void writeNonHtml( CPD cpd )
222 throws MavenReportException
223 {
224 Renderer r = createRenderer();
225
226 if ( r == null )
227 {
228 return;
229 }
230
231 String buffer = r.render( cpd.getMatches() );
232 FileOutputStream tStream = null;
233 Writer writer = null;
234 try
235 {
236 targetDirectory.mkdirs();
237 File targetFile = new File( targetDirectory, "cpd." + format );
238 tStream = new FileOutputStream( targetFile );
239 writer = new OutputStreamWriter( tStream, getOutputEncoding() );
240 writer.write( buffer );
241 writer.close();
242
243 if ( includeXmlInSite ) {
244 File siteDir = getReportOutputDirectory();
245 siteDir.mkdirs();
246 FileUtils.copyFile( targetFile, new File( siteDir, "cpd." + format ) );
247 }
248 }
249 catch ( IOException ioe )
250 {
251 throw new MavenReportException( ioe.getMessage(), ioe );
252 }
253 finally
254 {
255 IOUtil.close( writer );
256 IOUtil.close( tStream );
257 }
258 }
259
260
261
262
263 public String getOutputName()
264 {
265 return "cpd";
266 }
267
268 private static ResourceBundle getBundle( Locale locale )
269 {
270 return ResourceBundle.getBundle( "cpd-report", locale, CpdReport.class.getClassLoader() );
271 }
272
273
274
275
276
277
278
279
280 public Renderer createRenderer()
281 throws MavenReportException
282 {
283 Renderer renderer = null;
284 if ( "xml".equals( format ) )
285 {
286
287 System.setProperty( "file.encoding", getOutputEncoding() );
288 renderer = new XMLRenderer();
289 }
290 else if ( "csv".equals( format ) )
291 {
292 renderer = new CSVRenderer();
293 }
294 else if ( !"".equals( format ) && !"none".equals( format ) )
295 {
296 try
297 {
298 renderer = (Renderer) Class.forName( format ).newInstance();
299 }
300 catch ( Exception e )
301 {
302 throw new MavenReportException(
303 "Can't find CPD custom format " + format + ": " + e.getClass().getName(), e );
304 }
305 }
306
307 return renderer;
308 }
309 }