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