package org.nutz.lang; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; /** * 一个统计代码的工具 * * @author pangwu86(pangwu86@gmail.com) */ public class Code { private Code() {} /** * 代码分析结果。 * * @author pangwu86(pangwu86@gmail.com) */ public static class CodeAnalysisResult { // 代码行 protected long normalLines; // 注释行 protected long commentLines; // 空行 protected long whiteLines; // 导入行 protected long importLines; public CodeAnalysisResult() {} public CodeAnalysisResult(long normalLines, long commentLines, long whiteLines, long importLines) { this.normalLines = normalLines; this.commentLines = commentLines; this.whiteLines = whiteLines; this.importLines = importLines; } public long getNormalLines() { return normalLines; } public long getCommentLines() { return commentLines; } public long getWhiteLines() { return whiteLines; } public long getImportLines() { return importLines; } public long getTotalLines() { return normalLines + commentLines + whiteLines + importLines; } public String toString() { return String.format("All : %d lines\n" + "comments : %d lines\n" + "blank : %d lines\n" + "imports : %d lines\n", normalLines, commentLines, whiteLines, importLines); } } /** * 代码统计结果。 * * @author pangwu86(pangwu86@gmail.com) */ public static class CodeStatisticsResult extends CodeAnalysisResult { private File src; private int fileCount; public CodeStatisticsResult(File src) { this.src = src; } public void addCodeAnalysisResult(CodeAnalysisResult analysisResult) { this.normalLines += analysisResult.getNormalLines(); this.commentLines += analysisResult.getCommentLines(); this.whiteLines += analysisResult.getWhiteLines(); this.importLines += analysisResult.getImportLines(); fileCount++; } public File getSrc() { return src; } public int getFileCount() { return fileCount; } } /** * 代码分析配置信息。 * * @author pangwu86(pangwu86@gmail.com) */ public static class CodeAnalysisConf { /** 包名行开头 */ public String pakStart; /** 导入行开头 */ public String impStart; /** 单行注解开头 */ public String singleLineCommentStart; /** 多行注解开头 */ public String multiLineCommentStart; /** 多行注解结尾 */ public String multiLineCommentEnd; /** 空行 */ public String emptyLinePattern; } /** 分析JAVA代码的配置项 */ private static CodeAnalysisConf CODE_INFO_JAVA = new CodeAnalysisConf(); static { CODE_INFO_JAVA.pakStart = "package "; CODE_INFO_JAVA.impStart = "import "; CODE_INFO_JAVA.singleLineCommentStart = "//"; CODE_INFO_JAVA.multiLineCommentStart = "/*"; CODE_INFO_JAVA.multiLineCommentEnd = "*/"; CODE_INFO_JAVA.emptyLinePattern = "^[\\s&&[^\\n]]*$"; } /** * 统计某个文件的信息。 * * @param file * 被分析的文件 * @param conf * 代码分析配置项(为空的话,则按照JAVA代码来进行分析统计) * @return 分析结果 */ public static CodeAnalysisResult countingCode(File file, CodeAnalysisConf conf) { if (!Files.isFile(file)) { throw new RuntimeException("file is not a File, can't analysis it."); } if (null == conf) { conf = CODE_INFO_JAVA; } BufferedReader br; try { br = new BufferedReader(new FileReader(file)); } catch (FileNotFoundException e) { throw Lang.wrapThrow(e); } boolean comment = false; long whiteLines = 0; long commentLines = 0; long normalLines = 0; long importLines = 0; String line = ""; try { while ((line = br.readLine()) != null) { line = line.trim(); if (line.startsWith(conf.multiLineCommentStart) && !line.endsWith(conf.multiLineCommentEnd)) { // 多行注释开始 commentLines++; comment = true; } else if (true == comment) { // 多行注释结束 commentLines++; if (line.endsWith(conf.multiLineCommentEnd)) { comment = false; } } else if (line.matches(conf.emptyLinePattern)) { // 空白行(多行注解内的空白行不算在内) whiteLines++; } else if (line.startsWith(conf.singleLineCommentStart) || (line.startsWith(conf.multiLineCommentStart) && line.endsWith(conf.multiLineCommentEnd))) { // 单行注释 commentLines++; } else if (line.startsWith(conf.pakStart) || line.startsWith(conf.impStart)) { // package与import importLines++; } else { // 代码行 normalLines++; } } } catch (IOException e) { throw Lang.wrapThrow(e); } if (br != null) { try { br.close(); } catch (IOException e) { throw Lang.wrapThrow(e); } } // 记录并返回统计结果 return new CodeAnalysisResult(normalLines, commentLines, whiteLines, importLines); } /** * 统计某个目录下,以特定后缀名结尾的源码信息。 * * @param src * 源代码目录 * @param suffix * 文件后缀(为空的话,则统计所有类型文件) * @param countSubFolder * 是否统计子文件夹(true的话,将递归统计所有子文件夹) * @param conf * 代码分析配置项(为空的话,则按照JAVA代码来进行分析统计) */ public static CodeStatisticsResult countingCode(File src, String suffix, boolean countSubFolder, CodeAnalysisConf conf) { if (!Files.isDirectory(src)) { throw new RuntimeException("src is not a File, can't analysis it."); } if (null == conf) { conf = CODE_INFO_JAVA; } CodeStatisticsResult statisticsResult = new CodeStatisticsResult(src); boolean useParticularType = !Strings.isBlank(suffix); folderAnalysis(src, useParticularType, suffix, countSubFolder, conf, statisticsResult); return statisticsResult; } private static void folderAnalysis(File src, boolean useParticularType, String suffix, boolean countSubFolder, CodeAnalysisConf conf, CodeStatisticsResult statisticsResult) { for (File f : src.listFiles()) { if (countSubFolder && Files.isDirectory(f)) { folderAnalysis(f, useParticularType, suffix, countSubFolder, conf, statisticsResult); } else { if (useParticularType && !suffix.equalsIgnoreCase(f.getName() .substring(f.getName() .lastIndexOf('.') + 1))) { continue; } statisticsResult.addCodeAnalysisResult(countingCode(f, conf)); } } } }