View Javadoc
1   package org.apache.maven.plugins.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.List;
24  import java.util.Map;
25  import java.util.ResourceBundle;
26  
27  import org.apache.maven.doxia.sink.Sink;
28  import org.apache.maven.plugins.pmd.model.CpdFile;
29  import org.apache.maven.plugins.pmd.model.Duplication;
30  import org.apache.maven.project.MavenProject;
31  import org.codehaus.plexus.util.StringUtils;
32  
33  /**
34   * Class that generated the CPD report.
35   *
36   * @author mperham
37   * @version $Id$
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( "https://pmd.github.io/latest/pmd_userdocs_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( CpdFile duplicationMark )
109     {
110         // Get information for report generation
111         String filename = duplicationMark.getPath();
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 = duplicationMark.getLine();
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( xrefLocation + "/" + filename.replaceAll( "\\.java$", ".html" ).replace( '\\', '/' ) + "#L"
135                 + 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 duplications the found duplications
151      */
152     public void generate( List<Duplication> duplications )
153     {
154         beginDocument();
155 
156         if ( duplications.isEmpty() )
157         {
158             sink.paragraph();
159             sink.text( bundle.getString( "report.cpd.noProblems" ) );
160             sink.paragraph_();
161         }
162 
163         for ( Duplication duplication : duplications )
164         {
165             String code = duplication.getCodefragment();
166 
167             sink.table();
168             sink.tableRow();
169             sink.tableHeaderCell();
170             sink.text( bundle.getString( "report.cpd.column.file" ) );
171             sink.tableHeaderCell_();
172             if ( aggregate )
173             {
174                 sink.tableHeaderCell();
175                 sink.text( bundle.getString( "report.cpd.column.project" ) );
176                 sink.tableHeaderCell_();
177             }
178             sink.tableHeaderCell();
179             sink.text( bundle.getString( "report.cpd.column.line" ) );
180             sink.tableHeaderCell_();
181             sink.tableRow_();
182 
183             // Iterating on every token entry
184             for ( CpdFile mark : duplication.getFiles() )
185             {
186                 generateFileLine( mark );
187             }
188 
189             // Source snippet
190             sink.tableRow();
191 
192             int colspan = 2;
193             if ( aggregate )
194             {
195                 ++colspan;
196             }
197             // TODO Cleaner way to do this?
198             sink.rawText( "<td colspan='" + colspan + "'>" );
199             sink.verbatim( null );
200             sink.text( code );
201             sink.verbatim_();
202             sink.rawText( "</td>" );
203             sink.tableRow_();
204             sink.table_();
205         }
206 
207         sink.section1_();
208         sink.body_();
209         sink.flush();
210         sink.close();
211     }
212 }