【113】JPlag 重复代码段颜色不一致问题的解决方法。

JPlag 是一个用于检查代码相似性的工具。主要用于教育领域,检测学生的代码作业是否有抄袭行为。假如存在两个学生:student1 和 student2。为这两个学生各自创建一个文件夹并把代码放到文件夹中。文件结构如下:

E:\ws\jplag\exercise1
                |
                ├─ student1
                |      └─src
                |          └─Abc.java 
                | 
                └─ student2
                       └─src
                           └─Abc.java 

student1 的 Abc.java 文件内容如下:


public class Abc {
    public static void main(String[] args){
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");

        String a = "asfasdfasfd";
        String b = "klsdjfl";
        System.out.println(a + b);
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
    }

    private void m1 () {
        int[] arr = new int[]{1,3,2,3};
        int max = arr[0];
        for (int i : arr) {
            if (max < i){
                max = i;
            }
        }

    }
}

student2 的 java 文件内容如下:

public class Abc {
    public static void main(String[] args){
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
        int a = 1;
        for (int  i = 0; i < 10; i++) {
            a ++;
        }


        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
        System.out.println("hello");
    }

    private void m1 () {
        int[] arr = new int[]{1,3,2,3};
        int max = arr[0];
        for (int i : arr) {
            if (max < i){
                max = i;
            }
        }

    }
}

要使用 JPlag ,你需要去https://github.com/jplag/jplag/releases下载 JPlag 的 jar 文件。

我现在假设你已经下载好jar包,并把jar文件放到 E:\ws\jplag 。打开命令行,cd命令进入jar文件的目录,然后运行如下命令:

java -jar jplag-2.11.9-SNAPSHOT-jar-with-dependencies.jar -l java17 -r tmp -s exercise1

如果存在相似代码块,就会生成报告。当然你也可以利用程序执行上面的命令,这样可以方便的嵌入你的系统之中。

下面简单解释一下参数:

  • -l: 使用的语言,这里的java17 表示 java 1.7 版本。
  • -r:结果报告存放的目录地址。这里表示当前目录的tmp文件夹。
  • -s:要进行检查的学生作业目录。

最后生成的结果以HTML文件的形式存放。HTML展示效果如下图所示:

【113】JPlag 重复代码段颜色不一致问题的解决方法。_第1张图片

看到这幅图,想必你也能发现问题了。JPlag生成的结果,不同的代码片段使用不同的颜色表示。用户希望用同一个颜色展示,此时我们需要对生成的结果进行处理。

进行处理的代码分成两个文件,分别是 Main.java 、 ColorUtils.java 和 TagUtils.java。其中 Main.java 包含主方法,ColorUtils.java 主要改变代码块的颜色,TagUtils.java 处理HTML标签。这三个文件在同一个包内,包名是zhangchao。

Main.java

package zhangchao;

import java.io.File;

public class Main {
    public static void main(String[] args) {
        ColorUtils.changeColor(new File("E:/ws/jplag/tmp/match0-0.html"));
        ColorUtils.changeColor(new File("E:/ws/jplag/tmp/match0-1.html"));
    }

}

ColorUtils.java

package zhangchao;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;

/**
 * 处理代码片段的颜色。
 * @author 张超
 *
 */
public final class ColorUtils {

    /**
     * 代码片段的颜色值。
     */
    private final static String COLOR = "#ff0000";

    /**
     * 统一更改颜色
     * @param file
     */
    public static final void changeColor(final File file) {
        FileInputStream fis = null;
        BufferedReader br = null;
        FileOutputStream fos = null;
        BufferedWriter bw = null;
        try {
            fis = new FileInputStream(file);
            br = new BufferedReader(new InputStreamReader(fis, "UTF8"));
            String str = null;
            StringBuilder htmlSb = new StringBuilder();
            while(null != (str = br.readLine())){
                htmlSb.append(str).append("\n");
            }
            String originHtml = htmlSb.toString(); // 读取的HTML
            ArrayList strList = new ArrayList();
            int scanIndex = 0;
            while ( scanIndex < originHtml.length()) {
                // 如果存在标签,就替换成统一的颜色。
                if (TagUtils.isTag(originHtml, scanIndex)) {
                    scanIndex = TagUtils.increament(originHtml, scanIndex);
                    strList.add(" + COLOR + "\">");
                } else {
                    strList.add(originHtml.substring(scanIndex, scanIndex + 1));
                    scanIndex++;
                }
            }

            // 先关闭读取的流,再打开写入的流。
            br.close();
            br = null;
            fis.close();
            fis = null;

            StringBuilder newHtmlSb = new StringBuilder();
            for (String s : strList) {
                newHtmlSb.append(s);
            }
            fos = new FileOutputStream(file);
            bw = new BufferedWriter(new OutputStreamWriter(fos, "UTF8"));
            bw.write(newHtmlSb.toString());

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != fis) {
                    fis.close();
                }
                if (null != br) {
                    br.close();
                }
                if (null != bw) {
                    bw.flush();
                    bw.close();
                }
                if (null != fos) {
                    fos.flush();
                    fos.close();
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

TagUtils.java

package zhangchao;

/**
 * 处理HTML标签
 * @author 张超
 *
 */
public final class TagUtils {
    static final String TAG_NAME = "font";

    /**
     * 是否有标签
     * @param originHtml
     * @param scanIndex
     * @return
     */
    public static final boolean isTag(final String originHtml, final int scanIndex){
        if (originHtml.length() - scanIndex < TAG_NAME.length() + 2) {
            return false;
        }
        if(("<"+TAG_NAME).equalsIgnoreCase(originHtml.substring(scanIndex, scanIndex + TAG_NAME.length() + 1))){

        } else {
            return false;
        }
        if (false == Character.isWhitespace(originHtml.charAt(scanIndex + TAG_NAME.length() + 1))) {
            return false;
        }



        int tmpIndex = scanIndex + TAG_NAME.length() + 1;
        boolean isInDoubleQuote = false; // 是否在双引号中
        while(tmpIndex < originHtml.length()) {
            char ch = originHtml.charAt(tmpIndex);
            if (Character.isWhitespace(ch)) {
                tmpIndex ++;
            }
            // 标签中出现 ="  表明在双引号中
            else if (!isInDoubleQuote && ch == '=' && tmpIndex + 1 < originHtml.length() && originHtml.charAt(tmpIndex+1)=='"') {
                tmpIndex += 2;
                isInDoubleQuote = true;
            }
            else if (isInDoubleQuote && ch == '>') {
                tmpIndex ++;
            }
            else if (isInDoubleQuote && ch=='"') {
                tmpIndex ++;
                isInDoubleQuote = false;
            }

            else if (!isInDoubleQuote && ch=='>') { // 标签结束 >
                return true;
            }

            else {
                tmpIndex ++;
            }
        }

        return false;
    }

    /**
     * 计算标签要跨过多少字符
     * @param originHtml
     * @param scanIndex
     * @return
     */
    public static final int increament(final String originHtml, final int scanIndex){
        if (("<" + TAG_NAME + ">").equalsIgnoreCase(originHtml.substring(scanIndex, scanIndex) + TAG_NAME.length() + 2)) {
            return TAG_NAME.length() + 2;
        }

        int tmpIndex = scanIndex + TAG_NAME.length() + 1;
        boolean isInDoubleQuote = false; // 是否在双引号中
        int originHtmlLength = originHtml.length();
        while (tmpIndex < originHtmlLength) {
            char ch = originHtml.charAt(tmpIndex);
            if (Character.isWhitespace(ch)) {
                tmpIndex ++;
            }
            // 标签中出现 ="  表明在双引号中
            else if (!isInDoubleQuote && ch == '=' && tmpIndex + 1 < originHtmlLength && originHtml.charAt(tmpIndex+1)=='"') {
                tmpIndex += 2;
                isInDoubleQuote = true;
            }
            else if (isInDoubleQuote && ch == '>') { // 双引号中出现 > 不能作为标签结束的标志
                tmpIndex ++;
            }
            else if (isInDoubleQuote && ch=='"') {  // 找到右边的双引号,已经不在双引号里面了。
                tmpIndex ++;
                isInDoubleQuote = false;
            }

            else if (!isInDoubleQuote && ch=='>') { // img 标签结束 >
                tmpIndex ++;
                return tmpIndex;
            }
            // img 标签结束 />
            else if (!isInDoubleQuote && ch=='/' && tmpIndex + 1 < originHtmlLength && originHtml.charAt(tmpIndex+1)=='>') {
                tmpIndex += 2;
                return tmpIndex;
            }
            else {
                tmpIndex ++;
            }
        }
        return tmpIndex;
    }
}

你可能感兴趣的:(JAVA,JAVA,JPlag)