功能:统计指定文件夹中及其子文件夹中所有Java/C++代码的有效代码行数。在统计过程中,注释行或注释块已经去处。
已知缺陷:1.对Java中的annotation算作代码行
2.字符串中如存在"//",“/*” 或"*/" 将影响程序判断代码中注释部分,从而导致统计的精确性。由于这中情况在实际代码中很少见,故忽略不计。
今后改进:在统计代码行时,本程序时按行读文件的。如果时按字符读入,那么也许处理扩展性功能将更方便。总体来说,对于一般情况,本程序已经能够cover。还望网友对本程序改进之处提出建议。
直接贴程序:
package myTest.util;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class SourceLineCounter {
//指定文件类型
final public static String suffix = "java";
//指定源代码根文件夹路径
final public static String rootPath = "/Users/jerry/Downloads/apache-tomcat-7.0.37-src";
//指定统计Log文件的路径
final public static String logFile = "/Users/jerry/Downloads/log.txt";
public static void openLog() {
sb = null;
try {
sb = new BufferedWriter(new FileWriter(logFile));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public synchronized static String getLog() {
if (sb != null)
return sb.toString();
else
return null;
}
public synchronized static void appendLog(String s) {
if (sb != null)
try {
sb.write(s + "\n");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void closeLog() {
if (sb != null) {
try {
sb.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String args[]) {
openLog();
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
DirectoryUtil du = new DirectoryUtil(rootPath);
appendLog("Total Files:" + du.totalFiles);
Date startDate = new Date();
appendLog("Start counting " + formatter.format(new Date()));
ExecutorService executor = Executors.newFixedThreadPool(1);
String string = du.getNextFileName();
while (string != null) {
// System.out.println(string);
executor.execute(new LineCounter(string));
string = du.getNextFileName();
}
executor.shutdown();
try {
executor.awaitTermination(Integer.MAX_VALUE, TimeUnit.MICROSECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
appendLog("Total Lines:" + SourceLineCounter.lineCount);
Date endDate = new Date();
appendLog("End counting " + formatter.format(new Date()));
long milliseconds = endDate.getTime() - startDate.getTime();
appendLog("It takes " + milliseconds/1000 + "." + milliseconds%1000 + "s");
closeLog();
}
public static AtomicInteger lineCount = new AtomicInteger(0);
private static BufferedWriter sb;
private static class LineCounter implements Runnable {
public LineCounter(String fileName) {
this.fileName = fileName;
}
public void run() {
boolean findEndCommentFlag = false;
int lineCount = 0;
String preFlag = "/*";
// String regPreFlag = "";
String postFlag = "*/";
String regPostFlag = "\\*/";
// Reading input by lines:
BufferedReader in = null;
try {
in = new BufferedReader(new FileReader(fileName));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
System.err.println(e.toString());
return;
} catch (Exception e) {
e.printStackTrace();
}
String s;
try {
s = in.readLine();
// Consider we have no syntax error in source files
while (s != null) {
s = s.trim();
if (s.isEmpty()) {
s = in.readLine();
continue;
}
if (s.startsWith("//")) {
s = in.readLine();
continue;
}
if (!findEndCommentFlag) {
if (!s.startsWith(preFlag)) {
lineCount++;
}
if (s.lastIndexOf(preFlag) != -1) {
findEndCommentFlag = true;
continue;
}
s = in.readLine();
} else {
if (s.endsWith(postFlag)) {
findEndCommentFlag = false;
} else {
String strings[] = s.split(regPostFlag);
if (strings.length > 1) {
// check if there is preFlag
if (strings[strings.length - 1]
.lastIndexOf(preFlag) == -1) {
// preFlag not found, current sentence will
// be
// out of current comments block
findEndCommentFlag = false;
lineCount++;
}
}
}
s = in.readLine();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
}
}
SourceLineCounter.appendLog("#" + lineCount + "# " + fileName);
SourceLineCounter.lineCount.addAndGet(lineCount);
//System.out.print('+');
}
private String fileName;
}
private static class DirectoryUtil {
private int totalFiles;
public DirectoryUtil(String filePath) {
currentFolder = new DirectoryTree(filePath);
totalFiles = 0;
currentFolder.propagate();
}
synchronized public String getNextFileName() {
return _getNextFileName();
}
private String _getNextFileName() {
if (null == this.currentFolder) {
return null;
}
LinkedList<String> files = currentFolder.getSubFiles();
if (!files.isEmpty()) {
return files.pop();
} else {
LinkedList<DirectoryTree> folders = currentFolder
.getSubFolders();
if (folders.isEmpty()) {
this.currentFolder = this.currentFolder.getParent();
return _getNextFileName();
} else {
this.currentFolder = folders.pop();
return _getNextFileName();
}
}
}
private DirectoryTree currentFolder;
private class DirectoryTree {
public DirectoryTree(String folderName) {
this.folderName = folderName;
couldPropagate = true;
parent = null;
}
public void setParent(DirectoryTree parent) {
if (couldPropagate) {
this.parent = parent;
}
}
public DirectoryTree getParent() {
return this.parent;
}
public String getFolderName() {
return folderName;
}
public void propagate() {
if (this.couldPropagate) {
propagateFolderTree(this);
}
}
public LinkedList<DirectoryTree> getSubFolders() {
return this.subFolders;
}
public LinkedList<String> getSubFiles() {
return this.files;
}
private void propagateFolderTree(DirectoryTree current) {
File currentFolder = new File(current.getFolderName());
File[] children = currentFolder.listFiles();
for (File child : children) {
if (child.isFile()) {
String fileName = child.getAbsolutePath();
if (fileName.trim().toLowerCase()
.endsWith(SourceLineCounter.suffix)) {
DirectoryUtil.this.totalFiles++;
files.push(child.getAbsolutePath());
}
} else {
DirectoryTree folder = new DirectoryTree(
child.getAbsolutePath());
folder.setParent(current);
folder.propagate();
subFolders.push(folder);
}
}
this.couldPropagate = false;
}
private LinkedList<DirectoryTree> subFolders = new LinkedList<DirectoryTree>();
private LinkedList<String> files = new LinkedList<String>();
private DirectoryTree parent;
private String folderName;
private boolean couldPropagate;
}
}
}