Miaomz 6 лет назад
Родитель
Сommit
c080db348e

+ 10 - 6
src/main/java/com/moekr/moocoder/logic/api/PMDController.java

@@ -3,6 +3,7 @@ package com.moekr.moocoder.logic.api;
 import com.moekr.moocoder.logic.vo.MaintainMetricVO;
 import com.moekr.moocoder.logic.vo.PMDRelatedVO;
 import com.moekr.moocoder.logic.vo.ErrorProneMetricVO;
+import lombok.extern.apachecommons.CommonsLog;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.w3c.dom.*;
@@ -20,8 +21,11 @@ import java.util.Map;
  * @since 2020/5/26
  */
 @Component
+@CommonsLog
 public class PMDController {
     public static final String RULE_SET = "rulesets/internal/all-java.xml";
+    public static final String PMD_REPORT_SUFFIX = "-pmd-report";
+    public static final String CPD_REPORT_SUFFIX = "-cpd-report";
 
     private PMDWrapper wrapper;
 
@@ -37,8 +41,8 @@ public class PMDController {
      * @param project project id
      */
     public void makeReport(String srcDir, String dstDir, String project) {
-        String pmdPrefix = dstDir + File.separator + project + "-pmd-report";
-        String cpdPrefix = dstDir + File.separator + project + "-cpd-report";
+        String pmdPrefix = dstDir + File.separator + project + PMD_REPORT_SUFFIX;
+        String cpdPrefix = dstDir + File.separator + project + CPD_REPORT_SUFFIX;
         wrapper.runPMD(srcDir, "xml", RULE_SET, pmdPrefix+".xml", null);
         wrapper.runPMD(srcDir, "html", RULE_SET, pmdPrefix+".html", null);
         wrapper.runCPD(srcDir, "xml", cpdPrefix+".xml");
@@ -54,7 +58,7 @@ public class PMDController {
      */
     public PMDRelatedVO readXMLReport(String dstDir, String project) {
         try {
-            String pmdReportPath = dstDir + File.separator + project + "-pmd-report.xml";
+            String pmdReportPath = dstDir + File.separator + project + PMD_REPORT_SUFFIX + ".xml";
             File pmdFile = new File(pmdReportPath);
             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
             DocumentBuilder db = dbf.newDocumentBuilder();
@@ -64,13 +68,13 @@ public class PMDController {
             PMDRelatedVO ret = new PMDRelatedVO();
             readPMDXMLReport(ret, doc);
 
-            String cpdReportPath = dstDir + File.separator + project + "-cpd-report.xml";
+            String cpdReportPath = dstDir + File.separator + project + CPD_REPORT_SUFFIX + ".xml";
             File cpdFile = new File(cpdReportPath);
             Document cpdDoc = db.parse(cpdFile);
             readCPDXMLReport(ret, cpdDoc);
             return ret;
         } catch (IOException|SAXException|ParserConfigurationException e){
-            e.printStackTrace();
+            log.warn(e);
             return null;
         }
     }
@@ -82,7 +86,7 @@ public class PMDController {
             return true;
         }
 
-        String pmdReport = dstDir + File.separator + project + "-pmd-report.xml";  // one probe is enough
+        String pmdReport = dstDir + File.separator + project + PMD_REPORT_SUFFIX + ".xml";  // one probe is enough
         File dst = new File(pmdReport);
         if (!dst.exists()){
             return true;

+ 5 - 0
src/main/java/com/moekr/moocoder/logic/api/PMDWrapper.java

@@ -31,6 +31,11 @@ public class PMDWrapper {
         configuration.setReportFile(outputFile);
         configuration.setFailOnViolation(false);  // Otherwise, the process may terminate with code '4'
 
+        // don't show the full path of project files
+        if (outputFormat.equals("html")) {
+            configuration.setReportShortNames(true);
+        }
+
         if (cache != null) {
             configuration.setAnalysisCacheLocation(cache);
         }

+ 2 - 0
src/main/java/com/moekr/moocoder/logic/service/CodeMetricService.java

@@ -20,4 +20,6 @@ public interface CodeMetricService {
     ErrorProneMetricVO getPotentialMetric(String project);
 
     PMDRelatedVO getPMDRelatedMetric(String project);
+
+    String getDetailedProject(String project);
 }

+ 12 - 0
src/main/java/com/moekr/moocoder/logic/service/impl/CodeMetricServiceImpl.java

@@ -4,10 +4,12 @@ import com.moekr.moocoder.logic.api.PMDController;
 import com.moekr.moocoder.logic.api.WalaController;
 import com.moekr.moocoder.logic.service.CodeMetricService;
 import com.moekr.moocoder.logic.vo.*;
+import com.moekr.moocoder.util.FileUtil;
 import com.moekr.moocoder.util.enums.Granularity;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import java.io.File;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -110,4 +112,14 @@ public class CodeMetricServiceImpl implements CodeMetricService{
         }
         return pmdController.readXMLReport(dstDir, project);
     }
+
+    @Override
+    public String getDetailedProject(String project) {
+        String srcDir = ProjectPath.getSrcCodeDir(project);
+        String dstDir = ProjectPath.pmdOutputDir;
+        if (pmdController.needAnalysis(srcDir, dstDir, project)){
+            pmdController.makeReport(srcDir, dstDir, project);
+        }
+        return FileUtil.readFile(dstDir + File.separator + project + PMDController.PMD_REPORT_SUFFIX + ".html");
+    }
 }

+ 6 - 3
src/main/java/com/moekr/moocoder/util/FileUtil.java

@@ -1,11 +1,14 @@
 package com.moekr.moocoder.util;
 
+import lombok.extern.apachecommons.CommonsLog;
+
 import java.io.*;
 
 /**
  * @author miaomuzhi
  * @since 2020/5/8
  */
+@CommonsLog
 public class FileUtil {
     private FileUtil() {}
 
@@ -23,7 +26,7 @@ public class FileUtil {
             bufferedWriter.flush();
             return true;
         } catch (IOException e) {
-            e.printStackTrace();
+            log.info(e);
             return false;
         }
     }
@@ -38,12 +41,12 @@ public class FileUtil {
                 content.append(line).append(System.lineSeparator());
             }
         } catch (IOException e) {
-            e.printStackTrace();
+            log.info(e);
         }
         return new String(content);
     }
 
-    public static boolean createDirectory(String path) {
+    public static boolean createDirectoryIfAbsent(String path) {
         File file = new File(path);
         return file.exists() || file.mkdirs();
     }

+ 29 - 8
src/main/java/com/moekr/moocoder/web/CodeMetricController.java

@@ -4,17 +4,21 @@ import com.moekr.moocoder.logic.service.CodeMetricService;
 import com.moekr.moocoder.logic.service.ScoreService;
 import com.moekr.moocoder.logic.vo.*;
 import com.moekr.moocoder.web.dto.CodeMetricDTO;
+import lombok.extern.apachecommons.CommonsLog;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
 
 /**
  * @author miaomuzhi
  * @since 2020/5/26
  */
-@RestController
+@CommonsLog
+@Controller
 @RequestMapping("/metric")
 public class CodeMetricController {
     private CodeMetricService codeMetricService;
@@ -28,7 +32,7 @@ public class CodeMetricController {
 
 
     @GetMapping("/code_metric")
-    public CodeMetricDTO getCodeMetricDTO(@RequestParam String project){
+    public @ResponseBody CodeMetricDTO getCodeMetricDTO(@RequestParam String project){
         if (project == null) {
             return null;
         }
@@ -75,13 +79,30 @@ public class CodeMetricController {
         return codeMetricDTO;
     }
 
+    @GetMapping("/detailed_report")
+    public void getDetailedReport(@RequestParam String project, HttpServletResponse response) {
+        try {
+            String html = codeMetricService.getDetailedProject(project);
+            if (html == null || html.isEmpty()) {
+                html = "<html><head><meta charset=\"UTF-8\"></head><body><p>项目不存在!</p><p><a href='/code_metric'>返回代码度量页面</a></p></body></html>";
+            }
+
+            response.setContentType("text/html");
+            response.setCharacterEncoding("UTF-8");
+            PrintWriter writer = response.getWriter();
+            writer.print(html);
+        } catch (IOException e){
+            log.warn(e);
+        }
+    }
+
     @GetMapping("/pmd_relate")
-    public PMDRelatedVO getPMDRelatedMetric(@RequestParam String project) {
+    public @ResponseBody PMDRelatedVO getPMDRelatedMetric(@RequestParam String project) {
         return codeMetricService.getPMDRelatedMetric(project);
     }
 
     @GetMapping("/extension")
-    public ExtensionMetricVO getExtensionMetric(@RequestParam String project) {
+    public @ResponseBody ExtensionMetricVO getExtensionMetric(@RequestParam String project) {
         return codeMetricService.getExtensionMetric(project);
     }
 }

+ 87 - 5
src/main/resources/templates/views/code_metric.html

@@ -20,7 +20,7 @@
                     </el-form-item>
                 </el-form>
 
-                <el-row>
+                <el-row :gutter="20">
                     <el-col :span="12">
                         <el-card class="box-card">
                             <div slot="header" class="clearfix">
@@ -48,7 +48,7 @@
                     </el-col>
                 </el-row>
 
-                <el-row>
+                <el-row  :gutter="20">
                     <el-col :span="12">
                         <el-card class="box-card">
                             <div slot="header" class="clearfix">
@@ -70,6 +70,8 @@
                         <el-card class="box-card">
                             <div slot="header" class="clearfix">
                                 <span>潜在危险得分</span>
+                                <el-button style="float: right; padding: 3px 0"
+                                           type="text" @click="checkDetailedReport">详细信息</el-button>
                             </div>
                             <div class="text item">
                                 {{'疑似缺陷数: ' + scores.malformedCode }}
@@ -81,11 +83,13 @@
                     </el-col>
                 </el-row>
 
-                <el-row>
+                <el-row  :gutter="20">
                     <el-col :span="12">
                         <el-card class="box-card">
                             <div slot="header" class="clearfix">
                                 <span>编码规范性得分</span>
+                                <el-button style="float: right; padding: 3px 0"
+                                           type="text" @click="checkDetailedReport">详细信息</el-button>
                             </div>
                             <div class="text item">
                                 {{'编程风格问题数: ' + scores.codeStyleIssuesCount }}
@@ -126,6 +130,46 @@
                     </el-col>
                 </el-row>
 
+                <el-row  :gutter="20">
+                    <el-col :span="12">
+                        <el-card class="box-card">
+                            <div slot="header" class="clearfix">
+                                <span>可维护性得分</span>
+                                <el-button style="float: right; padding: 3px 0"
+                                           type="text" @click="checkDetailedReport">详细信息</el-button>
+                            </div>
+                            <div>
+                                {{ '重复代码数: ' + scores.duplicateCount }}
+                            </div>
+                            <div>
+                                {{ '文档问题数: ' + scores.documentationCount }}
+                            </div>
+                            <div>
+                                {{ '设计问题数: ' + scores.designCount }}
+                            </div>
+                            <div>
+                                {{ '圈复杂度超标数: ' + scores.complexityCount }}
+                            </div>
+                            <div>
+                                <el-collapse v-model="activeMaintainableName">
+                                    <el-collapse-item title="文档问题" name="1">
+                                        <el-table :data="scores.documentTableData" style="width: 100%">
+                                            <el-table-column prop="name" label="问题名称" width="180"></el-table-column>
+                                            <el-table-column prop="occurrence" label="出现次数" width="180"></el-table-column>
+                                        </el-table>
+                                    </el-collapse-item>
+                                    <el-collapse-item title="设计问题" name="2">
+                                        <el-table :data="scores.designTableData" style="width: 100%">
+                                            <el-table-column prop="name" label="问题名称" width="180"></el-table-column>
+                                            <el-table-column prop="occurrence" label="出现次数" width="180"></el-table-column>
+                                        </el-table>
+                                    </el-collapse-item>
+                                </el-collapse>
+                            </div>
+                        </el-card>
+                    </el-col>
+                </el-row>
+
             </el-main>
         </el-container>
     </template>
@@ -148,6 +192,7 @@
             },
 
             activeNormativeName: 1,
+            activeMaintainableName: 1,
 
             scores: {
                 scale: 0,
@@ -172,7 +217,16 @@
                 codeStyleIssuesCount: 0,
                 bestPracticesIssuesCount: 0,
 
-                normDistMainSeqMean: 0
+                normDistMainSeqMean: 0,
+
+                duplicateCount: 0,
+                complexityCount: 0,
+                documentationCount: 0,
+                designCount: 0,
+                documentTableData: [],
+                documentationOccurrences: {},
+                designTableData:[],
+                designOccurrences: {}
             }
         },
 
@@ -182,9 +236,10 @@
                     if (response.body){
                         this.scores = response.body;
 
+                        // code style
                         this.scores.codeStyleTableData = [];
                         this.scores.codeStyleIssuesCount = 0;
-                        for (let issue of new Map(Object.entries(this.scores.codeStyleIssues))) {
+                        for (let issue of Object.entries(this.scores.codeStyleIssues)) {
                             this.scores.codeStyleTableData.push({
                                 name: issue[0],
                                 occurrence: issue[1]
@@ -192,6 +247,7 @@
                             this.scores.codeStyleIssuesCount += issue[1]
                         }
 
+                        // best practice
                         this.scores.bestPracticesTableData = [];
                         this.scores.bestPracticesIssuesCount = 0;
                         for (let issue of Object.entries(this.scores.bestPracticesIssues)) {
@@ -201,6 +257,28 @@
                             });
                             this.scores.bestPracticesIssuesCount += issue[1];
                         }
+
+                        // document
+                        this.scores.documentTableData = [];
+                        this.scores.documentationCount = 0;
+                        for (let issue of Object.entries(this.scores.documentationOccurrences)) {
+                            this.scores.documentTableData.push({
+                                name: issue[0],
+                                occurrence: issue[1]
+                            });
+                            this.scores.documentationCount += issue[1];
+                        }
+
+                        // design
+                        this.scores.designTableData = [];
+                        this.scores.designCount = 0;
+                        for (let issue of Object.entries(this.scores.designOccurrences)) {
+                            this.scores.designTableData.push({
+                                name: issue[0],
+                                occurrence: issue[1]
+                            });
+                            this.scores.designCount += issue[1];
+                        }
                     } else {
                         this.$message.warning('该项目ID不存在!')
                     }
@@ -216,6 +294,10 @@
                 } else {
                     return num.toFixed(2)
                 }
+            },
+
+            checkDetailedReport() {
+                window.open('/metric/detailed_report?project=' + this.formInline.project, '_blank')
             }
         }
     })

+ 10 - 0
src/test/java/com/moekr/moocoder/logic/api/PMDControllerTest.java

@@ -29,4 +29,14 @@ class PMDControllerTest {
         assertEquals(3, relatedVO.getMaintainMetric().getComplexityCount());
         assertEquals(8, relatedVO.getNormativenessMetric().getBestPracticesIssues().size());
     }
+
+    @Test
+    void testWithMaven() {
+        PMDController controller = new PMDController(new PMDWrapper());
+        String srcFolder = getClass().getResource("/maven/ComputeCoverage/src/main/java").getFile();
+        String dstFolder = getClass().getResource("/wala/pmd-controller").getFile();
+        controller.makeReport(srcFolder, dstFolder, "ComputeCoverage");
+        PMDRelatedVO relatedVO = controller.readXMLReport(dstFolder, "ComputeCoverage");
+        System.out.println(relatedVO);
+    }
 }

+ 5 - 0
src/test/java/com/moekr/moocoder/util/FileUtilTest.java

@@ -32,4 +32,9 @@ class FileUtilTest {
                 "org\\/omg\\/.*\n" +
                 "org\\/w3c\\/.*\n", content);
     }
+
+    @Test
+    void writeFile() {
+        assertFalse(FileUtil.writeFile("/fa", "hihi"));
+    }
 }

+ 6 - 0
src/test/java/com/moekr/moocoder/util/MavenUtilTest.java

@@ -15,4 +15,10 @@ class MavenUtilTest {
         String srcDir = getClass().getResource("/maven/ComputeCoverage").getFile();
         assertTrue(MavenUtil.buildMavenProject(srcDir));
     }
+
+    @Test
+    void buildAnotherMavenProject() {
+        String srcDir = getClass().getResource("/maven/1245214").getFile();
+        assertTrue(MavenUtil.buildMavenProject(srcDir));
+    }
 }

+ 1 - 0
src/test/resources/maven/1245214

@@ -0,0 +1 @@
+Subproject commit 33e9034ef8d6cbf122da19dc66fdf401784ec420