
代码质量教育后端【免费下载链接】JPlagState-of-the-Art Source Code Plagiarism Collusion Detection. Check for plagiarism in a set of programs.项目地址https://gitcode.com/gh_mirrors/jp/JPlag点击查看免费下载JPlag 在完成一批提交的抄袭检测后会将所有比较结果、提交文件与运行元数据打包为一个.jplag报告文件供 Report Viewer 打开查看。本文以 报告文件生成文档 为核心结合仓库源码完整讲解报告生成的入口调用链、.jplag包内的文件结构、每个 JSON 文件对应的 Java DTO 与写入器实现以及如何按照既定模式向报告扩展新属性并在 Report Viewer 中展示。读完后你将掌握 JPlag 报告从JPlagResult对象到磁盘 ZIP 归档的每一步实现细节。报告生成的入口ReportObjectFactoryReportObjectFactory是报告流程的起点与锚点见 ReportObjectFactory.java。它负责把一次运行的JPlagResult转换为一组 DTO 并写入磁盘最终打包成带.jplag扩展名的 ZIP 文件。文档中描述的流程为CLI 调用ReportObjectFactory#createAndSaveReport传入 JPlag 运行结果与目标路径工厂随后委派创建所有必要的文件和目录并打包报告。从 CLI 进入报告生成在命令行模式下实际调用链位于 OutputFileGenerator.javapublic static void generateJPlagResultFile(JPlagResult result, File outputFile) throws FileNotFoundException { ReportObjectFactory reportObjectFactory new ReportObjectFactory(outputFile); reportObjectFactory.createAndSaveReport(result); logger.info(Successfully written the result: {}, outputFile.getPath()); logger.info(View the result using --mode); }可以看到当前实现中目标路径通过构造函数传入new ReportObjectFactory(File targetFile)内部包装为ZipWriter而createAndSaveReport(JPlagResult result)只接收结果对象。输出文件名由 CLI 的--result-file选项控制默认值为results缺少.jplag扩展名时会自动补全见 CliOptions.java。从 Java API 进入报告生成以编程方式集成时README 给出的标准用法该示例由 ReadmeCodeExampleTest 保证与文档同步、始终可编译Language language new JavaLanguage(); SetFile submissionDirectories Set.of(new File(/path/to/rootDir)); File baseCode new File(/path/to/baseCode); JPlagOptions options new JPlagOptions(language, submissionDirectories, Set.of()).withBaseCodeSubmissionDirectory(baseCode); try { JPlagResult result JPlag.run(options); // Optional ReportObjectFactory reportObjectFactory new ReportObjectFactory(new File(/path/to/output)); reportObjectFactory.createAndSaveReport(result); } catch (ExitException e) { // error handling here } catch (FileNotFoundException e) { // handle IO exception here }createAndSaveReport 的执行顺序createAndSaveReport是整个报告流程的总调度见 ReportObjectFactory.java当前实现按如下顺序依次执行buildSubmissionToIdMap(result)—— 建立“提交名 → 报告 viewer id”的映射后文详述copySubmissionFilesToReport(result)—— 将各提交的文件复制进报告包writeComparisons(result)—— 委托ComparisonReportWriter写出一对提交的详细比较文件writeRunInformation(result)—— 写出runInformation.jsonwriteDistribution(result)—— 写出distribution.jsonwriteTopComparisons(result)—— 写出topComparisons.jsonwriteCluster(result)—— 写出cluster.jsonwriteSubmissionMappings()—— 写出submissionMappings.jsonwriteSubmissionIndexFile(result)—— 写出submissionFileIndex.jsonwriteReadMeFile()—— 写出一段提示如何用java -jar查看报告的README.txtwriteOptionsFiles(result.getOptions())—— 序列化JPlagOptions为options.jsonwriteBaseCodeReport(result)—— 委托BaseCodeReportWriter写出 basecode 匹配。值得注意的设计是写入后端可插拔工厂构造时接收JPlagResultWriter接口见 JPlagResultWriter.java它定义了addJsonEntryJSON DTO 序列化、addFileContentEntry原始文件复制、writeStringEntry纯文本与close四个操作。默认实现 ZipWriter.java 使用 JacksonObjectMapper将 DTO 序列化为 JSON 字节流逐个写入ZipEntry测试 ReportObjectFactoryTest 还会校验生成文件具有合法的 ZIP 魔数0x504B0304等确保产物是可打开的归档。.jplag报告文件整体结构报告是一个 ZIP 归档内部结构如下继承自文档中的目录树README.txt为当前源码额外写入的说明文件result.jplag | cluster.json | distribution.json | options.json | runInformation.json | submissionFileIndex.json | submissionMappings.json | topComparisons.json | README.txt │ └───comparisons | └───submission1-submission2.json | submission1-submission3.json | ... | submissionN-1-submissionN.json │ └───files │ └───submissionId1 │ │ submission1File1 │ │ submission1File2 │ │ ... │ └───submissionId2 │ │ ... │ └───submissionIdN │ │ ... │ └───basecode │ └───submissionId1.json │ └───submissionId2.json │ ...各文件的作用及其对应的 Java 记录类型汇总如下DTO 均位于 reportobject/model 包文件内容对应 DTO / 写入者cluster.jsonJPlag 发现的所有聚类数组元素为ClusterClusteringResultMapper → Clusterdistribution.json各指标的相似度分布每个分布 100 个桶按百分位Map指标名, int[]MetricMapper.getDistributionsoptions.json本次运行给出的全部选项CLI 或编程方式直接序列化 JPlagOptionsrunInformation.json运行耗时、比较次数、失败提交等RunInformationsubmissionFileIndex.json每个 submission id 的文件清单及每文件 token 数MapsubmissionId, SubmissionFileSubmissionFileIndexsubmissionMappings.json提交 id → 显示名映射 提交对 → 比较文件名映射SubmissionMappingstopComparisons.json导出的比较列表每条含两个提交与多维相似度TopComparisoncomparisons/每个导出比较对的详细报告相似度 具体匹配ComparisonReportfiles/被检测的所有提交文件按 submission id 分子目录copySubmissionFilesToReportbasecode/每个提交与所提供 basecode 的匹配结果数组BaseCodeMatch下面结合源码逐一拆解关键文件。核心文件逐项解析comparisons/ComparisonReportWriter 与匹配的定位信息comparisons目录中每个 JSON 文件对应一个导出比较对。对 submission id 为submissionId1、submissionId2的一对提交文件名形如submissionId1-submissionId2.json或submissionId2-submissionId1.json。实现位于 ComparisonReportWriter.java几个值得注意的细节只导出 top-NwriteComparisonReports先取jPlagResult.getOptions().maximumNumberOfComparisons()再对该范围内的比较对逐一写文件L60-L65因此报告体积受--top-comparisons类选项约束。每个比较记录 4 个相似度指标AVG、MAX、LONGEST_MATCH、MAXIMUM_LENGTHL37-L38这些指标定义于 SimilarityMetric 枚举MIN、INTERSECTION已被标记Deprecated不再导出。文件名冲突处理generateComparisonName用ConcurrentHashMap检测同名文件名冲突时追加递增序号id1-id2-2.json并在submissionIdToComparisonFileName双向查找表中登记L97-L112这份查找表随后写入submissionMappings.json。匹配位置如何换算convertMatchToReportMatch从比较的 token 列表中切出匹配区间subList(start, end 1)分别取区间的起始 token 最小行/列与结束 token 最大行/列构造CodePositionL118-L143。CodePosition 记录line1 基、column0 基、tokenListIndex0 基。对应的 ComparisonReport 记录为public record ComparisonReport(String firstSubmissionId, String secondSubmissionId, MapString, Double similarities, ListMatch matches, double firstSimilarity, double secondSimilarity) {}其中matches的每个元素是 Match两个文件路径、两侧起止CodePosition以及lengthOfFirst/lengthOfSecond匹配段在两侧文件中的 token 数。basecode/BaseCodeReportWriterBaseCodeReportWriter 为每个参与比较的提交写出一个basecode/submissionId.json若运行未提供 basecodesubmission.getBaseCodeComparison()为null写出的是空数组与文档描述一致若提供了 basecode则把该提交与 basecode 的匹配逐个转换为 BaseCodeMatch文件路径、起止CodePosition、匹配 token 数。这里同样通过baseCodeComparison.firstSubmission().equals(submission)判断“取左侧还是右侧 token 区间”来还原提交侧的位置L55-L63。runInformation.jsonRunInformationwriteRunInformation 将结果中所有无效提交转换为FailedSubmission名称 状态与版本号、执行日期dd/MM/yy格式、总耗时毫秒和总比较次数一起组装为 RunInformation。版本字段version取自ReportObjectFactory.REPORT_VIEWER_VERSION其值即 JPlag.JPLAG_VERSION从de.jplag.version资源文件解析解析失败时回退为Version.DEVELOPMENT 0.0.0。Version 遵循 semver用于 Report Viewer 比较报告文件与查看器版本的兼容性。distribution.jsonMetricMapper 的百分位分布MetricMapper.getDistributions 对AVG、MAX两个指标各调用一次result.calculateDistributionFor(metric)返回Map指标名, ListInteger——即文档所说的“Map from the metric name to an array of integers”。分布计算在 JPlagResult.calculateDistributionFor 中完成产出 100 个百分位桶。topComparisons.json则由 MetricMapper.getTopComparisons 生成列表元素为TopComparison(firstSubmission, secondSubmission, similarities)其中similarities覆盖 4 个导出指标。cluster.jsonClusteringResultMapperClusteringResultMapper 从result.getClusteringResult()中提取所有聚类聚类在 JPlag.run 中由ClusteringFactory.getClusterings计算并挂到结果上每个 Cluster 包含averageSimilarity簇内平均相似度、strength社区强度与成员提交 id 列表。submissionFileIndex.json 与 files/提交文件与 token 统计copySubmissionFilesToReport遍历导出比较涉及的所有提交将每个源文件复制到files/submissionId/...相对路径下ReportObjectFactory.java。一个细节若语言声明了useViewFiles()会改存file language.viewFileExtension()生成的“展示文件”而非原始文件——这是为某些语言预处理出适合高亮展示的版本。writeSubmissionIndexFile则把submission.getTokenCountPerFile()的每文件 token 数写进submissionFileIndex.json供 Report Viewer 计算匹配密度ComparisonFactory.loadSubmissionFiles中会用tokenCount初始化每个文件并用lengthOfFirst/lengthOfSecond累加matchedTokenCount见 ComparisonFactory.ts。options.json 与 README.txtwriteOptionsFiles直接将JPlagOptions整体序列化为options.json即文档所说“directly represented by the JPlagOptions class”。writeReadMeFile额外写入README.txt内容为固定三行提示这是 JPlag 生成的报告请获取最新版本 JPlag并用java -jar启动后将报告拖入页面查看ReportObjectFactory.java。Submission id 体系JPlag 侧与 Report Viewer 侧文档专节强调了 id 约定这是理解整个报告结构的前提。JPlag 侧建立 Submission → id 映射报告生成一开始buildSubmissionToIdMap会调用 SubmissionNameToIdMapper.buildSubmissionNameToIdMap把Submission#getName()JPlag 内部提交名映射为“报告 viewer id”。当前的具体实现是把提交名中的文件分隔符替换为下划线File.separator → _得到可安全用作 ZIP 内目录/文件名的 id同时构造submissionToIdFunction一个FunctionSubmission, String注入给各 Writer/Mapper。文档的原则在源码中依然成立凡是在报告 DTO 中引用提交的地方一律通过这个映射/函数解析为 id。Report Viewer 侧内部只认 id显示时再解析文档原话报告 viewer 内部“For internal use ... use only (!) the submission id”只使用 submission id需要展示名称时先解析为显示名。文档写作时期该映射存放在overview.json在当前实现中这一职责由submissionMappings.json承担——SubmissionMappings 的submissionIds字段即“提交 id → 显示名”的映射第二个字段是“提交 id 对 → 比较文件名”的嵌套查找表。Report Viewer 侧的 SubmissionMappingsFactory 将其解析为interface SubmissionMappings { idToDisplayNameMap: Mapstring, string comparisonFilesLookup: Mapstring, Mapstring, string }Viewer 的 store 层提供 getter 完成 id → 显示名解析比较文件名则通过comparisonFilesLookup.get(id1).get(id2)双向查找与ComparisonReportWriter写入的双向查找表严格对应。扩展指南为 JPlagResult 添加并展示新属性报告体系的设计目标之一就是让新属性易于加入。文档给出的 5 步模式JPlag 侧 2 步 Report Viewer 侧 3 步至今有效在 JPlagJava侧在代表目标 JSON 文件的 Java DTOreportobject/model包中的 record上新增属性定义该属性如何从JPlagResult提取——新增一个从JPlagResult抽取属性的组件或修改现有组件如ComparisonReportWriter、MetricMapper然后由ReportObjectFactory调用该组件并把结果赋值给第 1 步定义的属性。在 Report ViewerTypeScript/Vue侧在 TypeScript DTOreport-viewer/model包中引入新属性在对应的 factoryreport-viewer/parser/src/factories中定义如何从 JSON 文件提取该属性在目标 Vue 组件中展示该属性。文档中的示例把“每个匹配的 token 数”加入报告文档给出的示例任务是把匹配中的 token 数加入 JPlagReport并显示在 ComparisonView 的 MatchesTable 中五步落地如下给 Match.javaComparisonReport的子模型添加属性修改现有组件ComparisonReportWriter从JPlagResult中额外提取匹配的 token 数并保存到 Match DTO给 Match.ts 添加对应字段编辑 ComparisonFactory.ts 从 JSON 报告文件中读取该字段编辑 Match 列表组件MatchList.vue在 ComparisonView 中显示 token 数。对照当前代码可以看到这个示例实际上已经被实现——只是字段名演化为lengthOfFirst/lengthOfSecondComparisonReportWriter在构造MatchDTO 时写入match.lengthOfFirst()、match.lengthOfSecond()L141-L142ReportFormatMatch接口声明了lengthOfFirst: number; lengthOfSecond: numberMatch.tsComparisonFactory读取后累加到各文件的matchedTokenCount并暴露getMatchLength供排序/着色使用。这是一个“文档模式 → 仓库现实”的完整闭环示例。Report Viewer 如何消费报告包理解生成端的最后一环是消费端。ReportFileHandler 用 jszip 在浏览器内解包.jplag文件凡是路径包含files/的条目被识别为提交源文件记录submissionId与相对路径供比较视图做代码高亮与匹配着色其余条目作为 JSON 文件交给对应的 factoryBaseCodeReportFactory、ClusterFactory、ComparisonFactory、DistributionFactory、OptionsFactory、RunInformationFactory、SubmissionMappingsFactory、TopComparisonFactory导出见 parser/src/index.ts解析为模型对象。生成端files/submissionId/...的目录约定正是该解析逻辑的前置依赖。测试与验证仓库中与该流程直接相关的验证包括ReportObjectFactoryTesttestCreateAndSaveReportWithBasecode带 basecode 运行一次完整检测并生成报告断言产物是合法 ZIPtestVersionLoading断言REPORT_VIEWER_VERSION非空且不是开发版0.0.0ReadmeCodeExampleTest保证 README 中“ReportObjectFactory(new File(/path/to/output))createAndSaveReport(result)”的 API 示例始终可编译防止文档漂移ComparisonFactory.test.ts 等 parser 测试以report-viewer/parser/tests/factories/assets/下的ValidComparison.json、ValidSubmissionMappings.json等真实 JSON 样例验证解析逻辑与生成端 DTO 结构形成对照。小结JPlag 报告生成的核心事实可以归纳为三点其一ReportObjectFactory是唯一入口它按固定顺序把JPlagResult拆解为 8 个 JSON 文件、比较目录、提交文件目录与 basecode 目录并通过可插拔的JPlagResultWriter默认ZipWriter落盘为 ZIP其二全部提交引用一律通过SubmissionNameToIdMapper建立的 id 映射表达显示名解析被完全交给 Report Viewer 一侧的submissionMappings.json其三报告结构是刻意设计为可扩展的——Java DTO、提取组件、TypeScript 模型、解析 factory 与 Vue 组件五处按固定模式联动即可向报告中加入任意新维度如匹配 token 数并展示出来。这套“生成端 DTO 定契约、解析端 factory 守契约”的结构也是自定义 JPlag 输出格式时最值得参考的实现范式。赞分享代码质量教育后端【免费下载链接】JPlagState-of-the-Art Source Code Plagiarism Collusion Detection. Check for plagiarism in a set of programs.项目地址https://gitcode.com/gh_mirrors/jp/JPlag点击查看免费下载相关推荐JPlag 完全使用指南CLI 命令详解、Java API 集成与报告文件生成机制JPlag 完全使用指南CLI 命令详解、Java API 集成与报告文件生成机制 本文基于 JPlag 官方文档 1. How to Use JPlag.m代码质量教育后端PixiEditor 动画导出如何把 GIF 压小而不牺牲画质PixiEditor 动画导出如何把 GIF 压小而不牺牲画质 PixiEditor 是一款基于 .NET 的像素艺术编辑器动画导出时它会把文档里的每一帧逐桌面应用图像处理EagleEye报告生成指南从数据到PDF/JSON完整报告EagleEye报告生成指南从数据到PDF/JSON完整报告 想要将EagleEye的强大社交媒体追踪能力转化为专业的报告文档吗 本指南将带你深入了解如计算机视觉AI 应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考