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.IOException;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.Comparator;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.ResourceBundle;
32  
33  import net.sourceforge.pmd.RuleViolation;
34  
35  import org.apache.maven.doxia.sink.Sink;
36  import org.apache.maven.plugin.logging.Log;
37  import org.codehaus.plexus.util.StringUtils;
38  
39  
40  
41  
42  
43  
44  
45  public class PmdReportGenerator
46  {
47      private Log log;
48  
49      private Sink sink;
50  
51      private String currentFilename;
52  
53      private ResourceBundle bundle;
54  
55      private HashSet<RuleViolation> violations = new HashSet<RuleViolation>();
56  
57      private boolean aggregate;
58  
59      
60      private int fileCount = 0;
61  
62      private Map<File, PmdFileInfo> files;
63  
64      
65  
66      public PmdReportGenerator( Log log, Sink sink, ResourceBundle bundle, boolean aggregate )
67      {
68          this.log = log;
69          this.sink = sink;
70          this.bundle = bundle;
71          this.aggregate = aggregate;
72      }
73  
74      private String getTitle()
75      {
76          return bundle.getString( "report.pmd.title" );
77      }
78  
79      public void setViolations( Collection<RuleViolation> violations )
80      {
81          this.violations = new HashSet<RuleViolation>( violations );
82      }
83  
84      public List<RuleViolation> getViolations()
85      {
86          return new ArrayList<RuleViolation>( violations );
87      }
88  
89      
90      
91      
92      
93      
94      
95      
96      
97      
98  
99      private void startFileSection( String currentFilename, PmdFileInfo fileInfo )
100     {
101         sink.section2();
102         sink.sectionTitle2();
103 
104         
105         this.currentFilename = currentFilename;
106         if ( fileInfo != null && fileInfo.getSourceDirectory() != null )
107         {
108             this.currentFilename =
109                 StringUtils.substring( currentFilename, fileInfo.getSourceDirectory().getAbsolutePath().length() + 1 );
110         }
111         this.currentFilename = StringUtils.replace( this.currentFilename, "\\", "/" );
112 
113         String title = this.currentFilename;
114         if ( aggregate && fileInfo != null && fileInfo.getProject() != null )
115         {
116             title = fileInfo.getProject().getName() + " - " + this.currentFilename;
117         }
118         sink.text( title );
119         sink.sectionTitle2_();
120 
121         sink.table();
122         sink.tableRow();
123         sink.tableHeaderCell();
124         sink.text( bundle.getString( "report.pmd.column.violation" ) );
125         sink.tableHeaderCell_();
126         sink.tableHeaderCell();
127         sink.text( bundle.getString( "report.pmd.column.line" ) );
128         sink.tableHeaderCell_();
129         sink.tableRow_();
130     }
131 
132     private void endFileSection()
133     {
134         sink.table_();
135         sink.section2_();
136     }
137 
138     private void processSingleRuleViolation( RuleViolation ruleViolation, PmdFileInfo fileInfo )
139     {
140         sink.tableRow();
141         sink.tableCell();
142         sink.text( ruleViolation.getDescription() );
143         sink.tableCell_();
144         sink.tableCell();
145 
146         int beginLine = ruleViolation.getBeginLine();
147         outputLineLink( beginLine, fileInfo );
148         int endLine = ruleViolation.getEndLine();
149         if ( endLine != beginLine )
150         {
151             sink.text( "–" );
152             outputLineLink( endLine, fileInfo );
153         }
154 
155         sink.tableCell_();
156         sink.tableRow_();
157     }
158 
159     
160     
161     
162     private void processViolations()
163         throws IOException
164     {
165         fileCount = files.size();
166         ArrayList<RuleViolation> violations2 = new ArrayList<RuleViolation>( violations );
167         Collections.sort( violations2, new Comparator<RuleViolation>()
168         {
169             
170             public int compare( RuleViolation o1, RuleViolation o2 )
171             {
172                 int filenames = o1.getFilename().compareTo( o2.getFilename() );
173                 if ( filenames == 0 )
174                 {
175                     return o1.getBeginLine() - o2.getBeginLine();
176                 }
177                 else
178                 {
179                     return filenames;
180                 }
181             }
182         } );
183 
184         boolean fileSectionStarted = false;
185         String previousFilename = null;
186         for ( RuleViolation ruleViolation : violations2 )
187         {
188             String currentFn = ruleViolation.getFilename();
189             File canonicalFilename = new File( currentFn ).getCanonicalFile();
190             PmdFileInfo fileInfo = files.get( canonicalFilename );
191             if ( fileInfo == null )
192             {
193                 log.warn( "Couldn't determine PmdFileInfo for file " + currentFn + " (canonical: " + canonicalFilename
194                     + "). XRef links won't be available." );
195             }
196 
197             if ( !currentFn.equalsIgnoreCase( previousFilename ) && fileSectionStarted )
198             {
199                 endFileSection();
200                 fileSectionStarted = false;
201             }
202             if ( !fileSectionStarted )
203             {
204                 startFileSection( currentFn, fileInfo );
205                 fileSectionStarted = true;
206             }
207 
208             processSingleRuleViolation( ruleViolation, fileInfo );
209 
210             previousFilename = currentFn;
211         }
212 
213         if ( fileSectionStarted )
214         {
215             endFileSection();
216         }
217     }
218 
219     private void outputLineLink( int line, PmdFileInfo fileInfo )
220     {
221         String xrefLocation = null;
222         if ( fileInfo != null )
223         {
224             xrefLocation = fileInfo.getXrefLocation();
225         }
226 
227         if ( xrefLocation != null )
228         {
229             sink.link( xrefLocation + "/" + currentFilename.replaceAll( "\\.java$", ".html" ) + "#L" + line );
230         }
231         sink.text( String.valueOf( line ) );
232         if ( xrefLocation != null )
233         {
234             sink.link_();
235         }
236     }
237 
238     public void beginDocument()
239     {
240         sink.head();
241         sink.title();
242         sink.text( getTitle() );
243         sink.title_();
244         sink.head_();
245 
246         sink.body();
247 
248         sink.section1();
249         sink.sectionTitle1();
250         sink.text( getTitle() );
251         sink.sectionTitle1_();
252 
253         sink.paragraph();
254         sink.text( bundle.getString( "report.pmd.pmdlink" ) + " " );
255         sink.link( "http://pmd.sourceforge.net/" );
256         sink.text( "PMD" );
257         sink.link_();
258         sink.text( " " + AbstractPmdReport.getPmdVersion() + "." );
259         sink.paragraph_();
260 
261         sink.section1_();
262 
263         
264 
265         sink.section1();
266         sink.sectionTitle1();
267         sink.text( bundle.getString( "report.pmd.files" ) );
268         sink.sectionTitle1_();
269 
270         
271     }
272 
273     
274 
275 
276 
277 
278 
279 
280 
281 
282 
283 
284 
285 
286 
287     public void render()
288         throws IOException
289     {
290         processViolations();
291     }
292 
293     public void endDocument()
294         throws IOException
295     {
296         if ( fileCount == 0 )
297         {
298             sink.paragraph();
299             sink.text( bundle.getString( "report.pmd.noProblems" ) );
300             sink.paragraph_();
301         }
302 
303         sink.section1_();
304 
305         
306         
307         
308         
309 
310         
311 
312         sink.body_();
313 
314         sink.flush();
315 
316         sink.close();
317     }
318 
319     public void setFiles( Map<File, PmdFileInfo> files )
320     {
321         this.files = files;
322     }
323 }