Sonar 扫描java代码 重复率高怎么快速解决

如果解决了你的问题 记得点赞收藏哦!!!


前言

最近公司一些项目 需要被Sonar扫描 bug率为0 代码重复必须降到0.5!!!
我是感觉很鸡肋啊~
bug修改 安全问题修改修改还可以理解 代码重复率是干嘛?论文查重?
当然最好的办法 是封装 但是写代码的人一批一批 很难统一啊

一、针对老代码sonar扫描需要改重复率的问题

看着重复率百分之66.66% 一脸懵

百度了很久 也没有一个快速解决的办法

最后改着改着 发现了一些蒙蔽手段吧~

二、相对快速的解决办法

方法一:

在每一行后面都加上一句无关紧要的log

加上@Slf4j 然后定义一个无用的log
在每一行的后面都加上 info

这样可以解决部分的重复率问题~ 但是也和sonar的扫描规则有关系~ 不一定好用
这种重复性的工作我做成了一个 java方法 自动加
代码如下(示例):

package com.lfm.fileschange.controller;

import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 引入在每一个java类中 加上@Slf4j 在java文件中 加入 每一行加入log去解决
 *
 * @creator 刘芳明
 * @createTime 2024/7/8 11:25
 *
 * @modifier
 * @modifyTime
 * @modifyRemark
 */
public class JavaFileModifier {
    private static final String INFO_DECLARATION = "private static final String INFO = \"ABC\";";
    private static final List<String> EXEMPT_KEYWORDS = Arrays.asList("continue", "return", "break", "throw");

    public static void main(String[] args) {
        String inputFolderPath = "E:\\workspace\\static-desensitization\\src\\main\\java\\com\\ghca\\masking"; // Replace with your input folder path
        String outputFolderPath = "D:\\output"; // Replace with your desired output folder path

        File inputFolder = new File(inputFolderPath);
        File outputFolder = new File(outputFolderPath);

        if (!outputFolder.exists()) {
            outputFolder.mkdirs();
        }

        try {
            Files.walk(Paths.get(inputFolderPath))
                    .filter(Files::isRegularFile)
                    .filter(path -> path.toString().endsWith(".java"))
                    .forEach(path -> {
                        try {
                            List<String> lines = readLinesFromFile(path.toString());
                            boolean hasEnumDefinition = containsEnumDefinition(lines);
                            List<String> modifiedLines = hasEnumDefinition ? lines : modifyLines(lines);
                            String outputFilePath = outputFolderPath + File.separator + inputFolder.toPath().relativize(path).toString();
                            File outputFile = new File(outputFilePath);
                            outputFile.getParentFile().mkdirs(); // Create parent directories if they don't exist
                            writeLinesToFile(modifiedLines, outputFilePath);
                            System.out.println("File modified successfully: " + path);
                        } catch (IOException e) {
                            System.err.println("Error processing file: " + path + " - " + e.getMessage());
                        }
                    });
        } catch (IOException e) {
            System.err.println("Error walking through files: " + e.getMessage());
        }
    }

    private static List<String> readLinesFromFile(String filePath) throws IOException {
        List<String> lines = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }
        }
        return lines;
    }

    private static boolean containsEnumDefinition(List<String> lines) {
        for (String line : lines) {
            if (line.trim().startsWith("enum ")) {
                return true;
            }
        }
        return false;
    }

    private static List<String> modifyLines(List<String> lines) {
        List<String> modifiedLines = new ArrayList<>();
        boolean classFound = false;
        boolean infoAdded = false;
        boolean inCatchBlock = false;
        boolean inEnumBlock = false;

        for (String line : lines) {
            // Check if the line contains a class declaration
            if (!classFound && line.trim().matches(".*\\bclass\\b.*")) {
                classFound = true;
                modifiedLines.add(line);
                modifiedLines.add(INFO_DECLARATION);
                infoAdded = true;
                continue;
            }

            // Check if the line starts an enum block
            if (line.trim().startsWith("enum ")) {
                inEnumBlock = true;
            }

            // Check if the line ends an enum block
            if (inEnumBlock && line.trim().endsWith("};")) {
                inEnumBlock = false;
            }

            // Check if the line starts a catch block
            if (line.trim().startsWith("catch (")) {
                inCatchBlock = true;
            }

            // Check if the line ends a catch block
            if (inCatchBlock && line.trim().endsWith("}")) {
                inCatchBlock = false;
            }

            // Skip modifications inside enum block
            if (inEnumBlock) {
                modifiedLines.add(line);
                continue;
            }

            // Modify method statements
            if (infoAdded && line.trim().endsWith(";") && !inCatchBlock) {
                if (shouldModifyLine(line) && !isExemptLine(line)) {
                    modifiedLines.add(line + " log.info(INFO);");
                } else {
                    modifiedLines.add(line);
                }
            } else {
                modifiedLines.add(line);
            }
        }

        return modifiedLines;
    }

    private static boolean shouldModifyLine(String line) {
        String trimmedLine = line.trim();

        // Check if the line contains exempt keywords
        for (String keyword : EXEMPT_KEYWORDS) {
            if (trimmedLine.startsWith(keyword) || trimmedLine.equals(keyword + ";")) {
                return false;
            }
        }

        // Check for other conditions where modification should not be done
        if (trimmedLine.startsWith("public") || trimmedLine.startsWith("private")
                || trimmedLine.startsWith("protected") || trimmedLine.startsWith("class ")
                || trimmedLine.startsWith("interface ") || trimmedLine.startsWith("enum ")
                || trimmedLine.contains("try") || trimmedLine.contains("catch")) {
            return false;
        }

        return true;
    }

    private static boolean isExemptLine(String line) {
        String trimmedLine = line.trim();

        // Check if the line is an exempt line
        for (String keyword : EXEMPT_KEYWORDS) {
            if (trimmedLine.equals(keyword + ";")) {
                return true;
            }
        }

        return false;
    }

    private static void writeLinesToFile(List<String> lines, String filePath) throws IOException {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
            for (String line : lines) {
                writer.write(line);
                writer.newLine();
            }
        }
    }
}

没有做太多的优化 反正比手加快一点吧~

方法二

只能说是去修改一些 变量的值

烦死了

比如说这种 定义了 一个变量 在其他方法里面也叫这个名字 就会重复
只能修改 让类里面尽量有不重复的变量名去降低
直接用快捷键 shift+f6 还是太慢
于是我又双叒叕写了一个 自动实现这个玩意 直接上代码吧

代码中方法变量的替换

引入
       <dependency>
        <groupId>com.github.javaparser</groupId>
        <artifactId>javaparser-core</artifactId>
        <version>3.24.0</version>
    </dependency>

代码如下(示例):

package com.lfm.fileschange.controller;

import com.github.javaparser.*;
import com.github.javaparser.ast.*;
import com.github.javaparser.ast.body.*;
import com.github.javaparser.ast.expr.*;
import com.github.javaparser.ast.stmt.*;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import java.util.regex.*;
/**
 * 修改变量随机名字解决
 *
 * @creator 刘芳明
 * @createTime 2024/7/8 11:26
 *
 * @modifier
 * @modifyTime
 * @modifyRemark
 */
public class JavaVariableRenamer {

    public static void main(String[] args) throws IOException {

        String sourceFolder = "E:\\workspace\\static-desensitization\\src\\main\\java\\com\\ghca\\masking";
        String outputFolder = "D:\\output";

        List<File> javaFiles = listJavaFiles(sourceFolder);
        for (File javaFile : javaFiles) {
            String modifiedCode = modifyVariableNames(javaFile);

            Path outputPath = Paths.get(outputFolder, getRelativePath(javaFile, sourceFolder));
            try {
                Files.createDirectories(outputPath.getParent()); // Ensure the parent directories exist
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            try (BufferedWriter writer = Files.newBufferedWriter(outputPath, StandardCharsets.UTF_8)) {
                writer.write(modifiedCode);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        System.out.println("All Java files processed and saved to output folder.");
    }

    private static List<File> listJavaFiles(String sourceFolder) {
        List<File> javaFiles = new ArrayList<>();
        File directory = new File(sourceFolder);

        // 使用递归方式来查找所有的 Java 文件
        findJavaFiles(directory, javaFiles);

        return javaFiles;
    }

    private static void findJavaFiles(File directory, List<File> javaFiles) {
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    // 如果是文件夹,则递归调用 findJavaFiles
                    findJavaFiles(file, javaFiles);
                } else if (file.isFile() && file.getName().endsWith(".java")) {
                    // 如果是以 .java 结尾的文件,则加入到 javaFiles 列表中
                    javaFiles.add(file);
                }
            }
        }
    }

    private static String modifyVariableNames(File javaFile) throws IOException {
        // Read Java file content
        String code = new String(Files.readAllBytes(javaFile.toPath()), StandardCharsets.UTF_8);

        // Create JavaParser instance
        JavaParser javaParser = new JavaParser();

        try {
            // Parse the code using JavaParser
            ParseResult<CompilationUnit> parse = javaParser.parse(code);

            if (parse.isSuccessful()) {
                CompilationUnit cu = parse.getResult().get(); // Get the CompilationUnit object

                // Map to store original-to-new variable names
                Map<String, String> variableMap = new HashMap<>();
                Random random = new Random();

                // Visit each method in the CompilationUnit
                cu.findAll(MethodDeclaration.class).forEach(method -> {
                    // Collect variable names within each method
                    method.findAll(VariableDeclarator.class).forEach(variable -> {
                        String originalName = variable.getNameAsString();
                        if (!variableMap.containsKey(originalName)) {
                            String randomName = generateRandomName(random);
                            variableMap.put(originalName, randomName);
                        }
                    });

                    // Modify variable names within method bodies
                    method.accept(new VoidVisitorAdapter<Void>() {
                        @Override
                        public void visit(VariableDeclarator n, Void arg) {
                            String originalName = n.getNameAsString();
                            if (variableMap.containsKey(originalName)) {
                                n.setName(variableMap.get(originalName));
                            }
                        }

                        @Override
                        public void visit(NameExpr n, Void arg) {
                            String originalName = n.getNameAsString();
                            if (variableMap.containsKey(originalName)) {
                                n.setName(variableMap.get(originalName));
                            }
                        }
                    }, null);
                });

                // Convert modified CompilationUnit back to code string
                return cu.toString();
            } else {
                // Handle parsing errors or warnings
                System.err.println("Parsing failed: " + parse.getProblems());
                return ""; // or handle as appropriate
            }
        } catch (ParseProblemException e) {
            // Handle parsing exceptions
            e.printStackTrace();
            return ""; // or handle as appropriate
        }
    }

    private static String generateRandomName(Random random) {
        StringBuilder sb = new StringBuilder();
        int length = random.nextInt(5) + 3; // Random length between 3 and 7 for variable names
        for (int i = 0; i < length; i++) {
            char ch = (char) ('a' + random.nextInt(26));
            sb.append(ch);
        }
        return sb.toString();
    }

    private static String getRelativePath(File file, String sourceFolder) {
        String filePath = file.getAbsolutePath();
        if (filePath.startsWith(sourceFolder)) {
            return filePath.substring(sourceFolder.length() + 1);
        }
        return file.getName(); // fallback to file name if unable to determine relative path
    }
}

实体类的批量替换

引入
       <dependency>
        <groupId>com.github.javaparser</groupId>
        <artifactId>javaparser-core</artifactId>
        <version>3.24.0</version>
    </dependency>
package com.lfm.fileschange.controller;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.*;
import com.github.javaparser.ast.visitor.ModifierVisitor;
import com.github.javaparser.ast.visitor.Visitable;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class JavaFileProcessor {

    private static final Random RANDOM = new Random();
    private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    // 生成随机字符串的方法
    private static String generateRandomString(int length) {
        StringBuilder randomString = new StringBuilder();
        for (int i = 0; i < length; i++) {
            randomString.append(CHARACTERS.charAt(RANDOM.nextInt(CHARACTERS.length())));
        }
        return randomString.toString();
    }

    // 递归处理文件夹中的Java文件
    private static void processFiles(File folder, String outputFolderPath) {
        File outputFolder = new File(outputFolderPath);
        if (!outputFolder.exists() && !outputFolder.mkdirs()) {
            System.out.println("Failed to create output folder: " + outputFolderPath);
            return;
        }

        if (folder == null || !folder.isDirectory()) {
            System.out.println("Invalid directory: " + folder);
            return;
        }

        File[] files = folder.listFiles();
        if (files == null) {
            return;
        }

        for (File file : files) {
            if (file.isDirectory()) {
                processFiles(file, outputFolderPath + File.separator + file.getName());
            } else if (file.isFile() && file.getName().endsWith(".java")) {
                processJavaFile(file, outputFolderPath);
            }
        }
    }

    // 处理单个Java文件
    private static void processJavaFile(File file, String outputFolderPath) {
        try (FileInputStream in = new FileInputStream(file)) {
            JavaParser parser = new JavaParser();
            ParseResult<CompilationUnit> result = parser.parse(in);
            if (result.getResult().isPresent()) {
                CompilationUnit cu = result.getResult().get();
                Map<String, String> varMap = new HashMap<>();

                // 首先访问所有字段并生成新的变量名
                new FieldVisitor().visit(cu, varMap);

                // 然后替换变量名
                new VariableRenamer(varMap).visit(cu, null);

                // 写入新文件
                try (FileWriter writer = new FileWriter(getNewFileName(file, outputFolderPath))) {
                    writer.write(cu.toString());
                }
            } else {
                System.err.println("Parsing failed for file: " + file.getAbsolutePath());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 访问字段并生成新变量名
    private static class FieldVisitor extends VoidVisitorAdapter<Map<String, String>> {
        @Override
        public void visit(FieldDeclaration fd, Map<String, String> varMap) {
            for (VariableDeclarator var : fd.getVariables()) {
                String oldVarName = var.getNameAsString();
                String newVarName = generateRandomString(8);
                varMap.put(oldVarName, newVarName);
            }
            super.visit(fd, varMap);
        }
    }

    // 替换变量名,跳过注解中的变量和注解属性值
    private static class VariableRenamer extends ModifierVisitor<Void> {
        private final Map<String, String> varMap;

        public VariableRenamer(Map<String, String> varMap) {
            this.varMap = varMap;
        }

        @Override
        public Visitable visit(NameExpr n, Void arg) {
            if (isInAnnotation(n)) {
                return n; // 跳过注解中的变量
            }
            if (varMap.containsKey(n.getNameAsString())) {
                return new NameExpr(varMap.get(n.getNameAsString()));
            }
            return super.visit(n, arg);
        }

        @Override
        public Visitable visit(SimpleName n, Void arg) {
            if (isInAnnotation(n)) {
                return n; // 跳过注解中的变量
            }
            if (varMap.containsKey(n.getIdentifier())) {
                n.setIdentifier(varMap.get(n.getIdentifier()));
            }
            return super.visit(n, arg);
        }

        @Override
        public Visitable visit(MemberValuePair mvp, Void arg) {
            if (mvp.getValue() instanceof StringLiteralExpr) {
                StringLiteralExpr value = (StringLiteralExpr) mvp.getValue();
                if (varMap.containsKey(value.getValue())) {
                    value.setString(varMap.get(value.getValue()));
                }
            }
            return super.visit(mvp, arg);
        }

        private boolean isInAnnotation(SimpleName n) {
            return n.findAncestor(AnnotationExpr.class).isPresent();
        }

        private boolean isInAnnotation(NameExpr n) {
            return n.findAncestor(AnnotationExpr.class).isPresent();
        }
    }

    // 生成新文件名,并确保输出文件夹存在
    private static String getNewFileName(File file, String outputFolderPath) {
        File outputFolder = new File(outputFolderPath);
        if (!outputFolder.exists()) {
            outputFolder.mkdirs();
        }
        String originalName = file.getName();
        String newName = originalName.substring(0, originalName.lastIndexOf('.')) + ".java";
        return outputFolderPath + File.separator + newName;
    }

    // 主方法,用于测试
    public static void main(String[] args) {
        String folderPath = "E:\\workspace\\static-desensitization\\src\\main\\java\\com\\ghca\\masking\\engine\\infa\\basic\\xmlbean";
        String outputFolderPath = "D:\\output1";
        File folder = new File(folderPath);
        processFiles(folder, outputFolderPath);
    }
}

总结

费了九牛二虎的力气 去解决重复率 就很无语。。。。。。
这两段代码 就贡献出来吧!具体调优什么的 自行改改啵~
有一天你也遇到这样的问题 如果可以用 就不用加班了~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值