View Javadoc

1   package org.apache.maven.plugin.pmd;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import net.sourceforge.pmd.cpd.Match;
23  import net.sourceforge.pmd.cpd.TokenEntry;
24  import org.apache.maven.doxia.sink.Sink;
25  import org.apache.maven.project.MavenProject;
26  import org.codehaus.plexus.util.StringUtils;
27  
28  import java.io.File;
29  import java.util.Iterator;
30  import java.util.Map;
31  import java.util.ResourceBundle;
32  
33  /**
34   * Class that generated the CPD report.
35   *
36   * @author mperham
37   * @version $Id: CpdReportGenerator.html 853015 2013-03-04 21:10:54Z olamy $
38   */
39  public class CpdReportGenerator
40  {
41      private Sink sink;
42  
43      private Map<File, PmdFileInfo> fileMap;
44  
45      private ResourceBundle bundle;
46  
47      private boolean aggregate;
48  
49      public CpdReportGenerator( Sink sink, Map<File, PmdFileInfo> fileMap, ResourceBundle bundle, boolean aggregate )
50      {
51          this.sink = sink;
52          this.fileMap = fileMap;
53          this.bundle = bundle;
54          this.aggregate = aggregate;
55      }
56  
57      /**
58       * Method that returns the title of the CPD Report
59       *
60       * @return a String that contains the title
61       */
62      private String getTitle()
63      {
64          return bundle.getString( "report.cpd.title" );
65      }
66  
67      /**
68       * Method that generates the start of the CPD report.
69       */
70      public void beginDocument()
71      {
72          sink.head();
73          sink.title();
74          sink.text( getTitle() );
75          sink.title_();
76          sink.head_();
77  
78          sink.body();
79  
80          sink.section1();
81          sink.sectionTitle1();
82          sink.text( getTitle() );
83          sink.sectionTitle1_();
84  
85          sink.paragraph();
86          sink.text( bundle.getString( "report.cpd.cpdlink" ) + " " );
87          sink.link( "http://pmd.sourceforge.net/cpd.html" );
88          sink.text( "CPD" );
89          sink.link_();
90          sink.text( " " + AbstractPmdReport.getPmdVersion() + "." );
91          sink.paragraph_();
92  
93          sink.section1_();
94  
95          // TODO overall summary
96  
97          sink.section1();
98          sink.sectionTitle1();
99          sink.text( bundle.getString( "report.cpd.dupes" ) );
100         sink.sectionTitle1_();
101 
102         // TODO files summary
103     }
104 
105     /**
106      * Method that generates a line of CPD report according to a TokenEntry.
107      */
108     private void generateFileLine( TokenEntry tokenEntry )
109     {
110         // Get information for report generation
111         String filename = tokenEntry.getTokenSrcID();
112         File file = new File( filename );
113         PmdFileInfo fileInfo = fileMap.get( file );
114         File sourceDirectory = fileInfo.getSourceDirectory();
115         filename = StringUtils.substring( filename, sourceDirectory.getAbsolutePath().length() + 1 );
116         String xrefLocation = fileInfo.getXrefLocation();
117         MavenProject projectFile = fileInfo.getProject();
118         int line = tokenEntry.getBeginLine();
119 
120         sink.tableRow();
121         sink.tableCell();
122         sink.text( filename );
123         sink.tableCell_();
124         if ( aggregate )
125         {
126             sink.tableCell();
127             sink.text( projectFile.getName() );
128             sink.tableCell_();
129         }
130         sink.tableCell();
131 
132         if ( xrefLocation != null )
133         {
134             sink.link(
135                 xrefLocation + "/" + filename.replaceAll( "\\.java$", ".html" ).replace( '\\', '/' ) + "#" + line );
136         }
137         sink.text( String.valueOf( line ) );
138         if ( xrefLocation != null )
139         {
140             sink.link_();
141         }
142 
143         sink.tableCell_();
144         sink.tableRow_();
145     }
146 
147     /**
148      * Method that generates the contents of the CPD report
149      *
150      * @param matches
151      */
152     @SuppressWarnings( "deprecation" )
153     public void generate( Iterator<Match> matches )
154     {
155         beginDocument();
156 
157         if ( !matches.hasNext() )
158         {
159             sink.paragraph();
160             sink.text( bundle.getString( "report.cpd.noProblems" ) );
161             sink.paragraph_();
162         }
163 
164         while ( matches.hasNext() )
165         {
166             Match match = matches.next();
167 
168             String code = match.getSourceCodeSlice();
169 
170             sink.table();
171             sink.tableRow();
172             sink.tableHeaderCell();
173             sink.text( bundle.getString( "report.cpd.column.file" ) );
174             sink.tableHeaderCell_();
175             if ( aggregate )
176             {
177                 sink.tableHeaderCell();
178                 sink.text( bundle.getString( "report.cpd.column.project" ) );
179                 sink.tableHeaderCell_();
180             }
181             sink.tableHeaderCell();
182             sink.text( bundle.getString( "report.cpd.column.line" ) );
183             sink.tableHeaderCell_();
184             sink.tableRow_();
185 
186             // Iterating on every token entry
187             for ( Iterator<TokenEntry> occurrences = match.iterator(); occurrences.hasNext(); )
188             {
189 
190                 TokenEntry mark = occurrences.next();
191                 generateFileLine( mark );
192             }
193 
194             // Source snippet
195             sink.tableRow();
196 
197             int colspan = 2;
198             if ( aggregate )
199             {
200                 ++colspan;
201             }
202             // TODO Cleaner way to do this?
203             sink.rawText( "<td colspan='" + colspan + "'>" );
204             sink.verbatim( false );
205             sink.text( code );
206             sink.verbatim_();
207             sink.rawText( "</td>" );
208             sink.tableRow_();
209             sink.table_();
210         }
211 
212         sink.section1_();
213         sink.body_();
214         sink.flush();
215         sink.close();
216     }
217 }