package li.lang;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.DecimalFormat;
/**
* 一个统计代码的工具
*
* @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;
}
}
/**
* 代码统计结果。
*
* @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;
}
public String toString() {
long total = importLines + whiteLines + commentLines + normalLines;
double _fileCount = new Integer(fileCount).doubleValue();
DecimalFormat decimalFormat = new DecimalFormat("#0.000");
return "统计路径\t" + src //
+ "\n文件个数\t" + fileCount //
+ "\n导入行数\t" + importLines + "\t平均\t" + decimalFormat.format((importLines / _fileCount))//
+ "\n空行行数\t" + whiteLines + "\t平均\t" + decimalFormat.format((whiteLines / _fileCount))//
+ "\n注释行数\t" + commentLines + "\t平均\t" + decimalFormat.format((commentLines / _fileCount))//
+ "\n代码行数\t" + normalLines + "\t平均\t" + decimalFormat.format((normalLines / _fileCount))//
+ "\n总计行数\t" + total + "\t平均\t" + decimalFormat.format((total / _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]]*$";
}
public static boolean isFile(File f) {
return null != f && f.exists() && f.isFile();
}
/**
* 统计某个文件的信息。
*
* @param file 被分析的文件
* @param conf 代码分析配置项(为空的话,则按照JAVA代码来进行分析统计)
* @return 分析结果
*/
public static CodeAnalysisResult countingCode(File file, CodeAnalysisConf conf) {
if (!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 new RuntimeException(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 new RuntimeException(e);
}
if (br != null) {
try {
br.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 记录并返回统计结果
return new CodeAnalysisResult(normalLines, commentLines, whiteLines, importLines);
}
public static boolean isDirectory(File f) {
if (null == f)
return false;
if (!f.exists())
return false;
if (!f.isDirectory())
return false;
return true;
}
public static boolean isBlank(CharSequence cs) {
if (null == cs)
return true;
int length = cs.length();
for (int i = 0; i < length; i++) {
if (!(Character.isWhitespace(cs.charAt(i))))
return false;
}
return true;
}
/**
* 统计某个目录下,以特定后缀名结尾的源码信息。
*
* @param src 源代码目录
* @param suffix 文件后缀(为空的话,则统计所有类型文件)
* @param countSubFolder 是否统计子文件夹(true的话,将递归统计所有子文件夹)
* @param conf 代码分析配置项(为空的话,则按照JAVA代码来进行分析统计)
*/
public static CodeStatisticsResult countingCode(File src, String suffix, boolean countSubFolder, CodeAnalysisConf conf) {
if (!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 = !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 && 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));
}
}
}
}