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 java.io.File;
23  import java.util.Iterator;
24  import java.util.Map;
25  import java.util.ResourceBundle;
26  
27  import net.sourceforge.pmd.cpd.Mark;
28  import net.sourceforge.pmd.cpd.Match;
29  import net.sourceforge.pmd.cpd.TokenEntry;
30  
31  import org.apache.maven.doxia.sink.Sink;
32  import org.apache.maven.project.MavenProject;
33  import org.codehaus.plexus.util.StringUtils;
34  
35  /**
36   * Class that generated the CPD report.
37   *
38   * @author mperham
39   * @version $Id: CpdReportGenerator.html 999063 2016-10-08 16:53:12Z adangel $
40   */
41  public class CpdReportGenerator
42  {
43      private Sink sink;
44  
45      private Map<File, PmdFileInfo> fileMap;
46  
47      private ResourceBundle bundle;
48  
49      private boolean aggregate;
50  
51      public CpdReportGenerator( Sink sink, Map<File, PmdFileInfo> fileMap, ResourceBundle bundle, boolean aggregate )
52      {
53          this.sink = sink;
54          this.fileMap = fileMap;
55          this.bundle = bundle;
56          this.aggregate = aggregate;
57      }
58  
59      /**
60       * Method that returns the title of the CPD Report
61       *
62       * @return a String that contains the title
63       */
64      private String getTitle()
65      {
66          return bundle.getString( "report.cpd.title" );
67      }
68  
69      /**
70       * Method that generates the start of the CPD report.
71       */
72      public void beginDocument()
73      {
74          sink.head();
75          sink.title();
76          sink.text( getTitle() );
77          sink.title_();
78          sink.head_();
79  
80          sink.body();
81  
82          sink.section1();
83          sink.sectionTitle1();
84          sink.text( getTitle() );
85          sink.sectionTitle1_();
86  
87          sink.paragraph();
88          sink.text( bundle.getString( "report.cpd.cpdlink" ) + " " );
89          sink.link( "http://pmd.sourceforge.net/cpd.html" );
90          sink.text( "CPD" );
91          sink.link_();
92          sink.text( " " + AbstractPmdReport.getPmdVersion() + "." );
93          sink.paragraph_();
94  
95          sink.section1_();
96  
97          // TODO overall summary
98  
99          sink.section1();
100         sink.sectionTitle1();
101         sink.text( bundle.getString( "report.cpd.dupes" ) );
102         sink.sectionTitle1_();
103 
104         // TODO files summary
105     }
106 
107     /**
108      * Method that generates a line of CPD report according to a TokenEntry.
109      */
110     private void generateFileLine( TokenEntry tokenEntry )
111     {
112         // Get information for report generation
113         String filename = tokenEntry.getTokenSrcID();
114         File file = new File( filename );
115         PmdFileInfo fileInfo = fileMap.get( file );
116         File sourceDirectory = fileInfo.getSourceDirectory();
117         filename = StringUtils.substring( filename, sourceDirectory.getAbsolutePath().length() + 1 );
118         String xrefLocation = fileInfo.getXrefLocation();
119         MavenProject projectFile = fileInfo.getProject();
120         int line = tokenEntry.getBeginLine();
121 
122         sink.tableRow();
123         sink.tableCell();
124         sink.text( filename );
125         sink.tableCell_();
126         if ( aggregate )
127         {
128             sink.tableCell();
129             sink.text( projectFile.getName() );
130             sink.tableCell_();
131         }
132         sink.tableCell();
133 
134         if ( xrefLocation != null )
135         {
136             sink.link( xrefLocation + "/" + filename.replaceAll( "\\.java$", ".html" ).replace( '\\', '/' ) + "#L"
137                 + line );
138         }
139         sink.text( String.valueOf( line ) );
140         if ( xrefLocation != null )
141         {
142             sink.link_();
143         }
144 
145         sink.tableCell_();
146         sink.tableRow_();
147     }
148 
149     /**
150      * Method that generates the contents of the CPD report
151      *
152      * @param matches
153      */
154     @SuppressWarnings( "deprecation" )
155     public void generate( Iterator<Match> matches )
156     {
157         beginDocument();
158 
159         if ( !matches.hasNext() )
160         {
161             sink.paragraph();
162             sink.text( bundle.getString( "report.cpd.noProblems" ) );
163             sink.paragraph_();
164         }
165 
166         while ( matches.hasNext() )
167         {
168             Match match = matches.next();
169 
170             String code = match.getSourceCodeSlice();
171 
172             sink.table();
173             sink.tableRow();
174             sink.tableHeaderCell();
175             sink.text( bundle.getString( "report.cpd.column.file" ) );
176             sink.tableHeaderCell_();
177             if ( aggregate )
178             {
179                 sink.tableHeaderCell();
180                 sink.text( bundle.getString( "report.cpd.column.project" ) );
181                 sink.tableHeaderCell_();
182             }
183             sink.tableHeaderCell();
184             sink.text( bundle.getString( "report.cpd.column.line" ) );
185             sink.tableHeaderCell_();
186             sink.tableRow_();
187 
188             // Iterating on every token entry
189             for ( Iterator<Mark> occurrences = match.iterator(); occurrences.hasNext(); )
190             {
191 
192                 TokenEntry mark = occurrences.next().getToken();
193                 generateFileLine( mark );
194             }
195 
196             // Source snippet
197             sink.tableRow();
198 
199             int colspan = 2;
200             if ( aggregate )
201             {
202                 ++colspan;
203             }
204             // TODO Cleaner way to do this?
205             sink.rawText( "<td colspan='" + colspan + "'>" );
206             sink.verbatim( false );
207             sink.text( code );
208             sink.verbatim_();
209             sink.rawText( "</td>" );
210             sink.tableRow_();
211             sink.table_();
212         }
213 
214         sink.section1_();
215         sink.body_();
216         sink.flush();
217         sink.close();
218     }
219 }