最近,我打算對我們項目的代碼進行清理,準備把一些沒有被使用到的公用方法清理掉,但是我在網路找了一遍,像PMD,Findbugs等靜態工具,都只能找到沒有被使用的私人方法。
無奈之下,只能直接寫代碼來實現這個功能,具體代碼如下:
package com;
import java.io.File;
import java.io.FileReader;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
public class FindUnUsedCode {
public static final int TYPE_METHOD = 0; //方法
public static final int TYPE_ATTRIBUTE = 1; //屬性
/**
* 類對象
*/
class ClassObject{
public List<String> methods = new ArrayList<String>(); //公用方法集合
public String className; //類名
public List<String> attributes = new ArrayList<String>(); //屬性名稱
}
/**
* 熟悉對象
*/
class MethodObject{
public String methodName; //方法名
}
class UnUsedCode{
public String className; //類名
public String codeName; //方法名或者屬性名稱
public int type; //0:方法名,1:屬性名稱
public String descript; //描述
}
//類集合
private List<ClassObject> classList = new ArrayList<ClassObject>();
//java定義的資料類型
private String[] dataTypes = {"void", "int", "boolean", "long", "char", "float", "double"};
//java定義的存取權限
private String[] callAuths = {"public", "friendly", "protected", "private"};
//java的關鍵字
private String[] keyWords = {"public", "friendly", "protected", "private", "void", "int", "boolean", "long", "char", "float", "double"
, "{", "}"};
/**
* 檢查一個檔案名稱是否java檔案
* @param fileName
* @return
*/
private boolean checkIsJava(String fileName)
{
boolean result = false;
if (fileName.lastIndexOf(".java") != -1)
{
result = true;
}
return result;
}
/**
* 分析java檔案,返回類對象
* @param fullName
* @return
*/
public ClassObject analyseJavaFile(String path, String fileName) throws Exception
{
//全路徑
String fullName = path + "/" + fileName;
//檔案對象
File file = new File(fullName);
//建立對象
ClassObject classObject = new ClassObject();
//設定類名
classObject.className = fileName.substring(0, fileName.length()-5);
//得到檔案讀對象
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
//讀取一行資料
String line = "";
do
{
line = randomAccessFile.readLine();
if (line == null)
{
break;
}
else
{
//去掉前後空格
line = line.trim();
}
//System.out.println("line:" + line);
if (line.indexOf(" class ") != -1)
{
//為類定義行,則不處理該行
continue;
}
//判斷是否公用對象
if (line.startsWith("public"))
{
//把資料拆分出來
String[] strs = line.split(" ");
//順序擷取資料,從第三個開始讀,因為第一個為public,第二個為資料類型,第三個才可能是名字
for (int i = 2; i < strs.length; i++)
{
//判斷是否java的關鍵字
if (isKeyWords(strs[i]))
{
continue;
}
//判斷是否以小寫字元開始
if (isStartWithLower(strs[i]))
{
//找到是否包含有括弧
int loc = strs[i].indexOf("(");
if (loc == -1)
{//不包含擴充
if (strs[i].indexOf(";") == -1)
{//不包含分號
if ((strs.length > (i+1)) && (strs[i+1].indexOf("(") == -1))
{
//下一個資料也不包含括弧,那麼則是屬性
classObject.attributes.add(strs[i]);
break;
}
else
{
//下個資料包含括弧,則是方法
classObject.methods.add(strs[i]);
//System.out.println("22222222222222222:" + line);
break;
}
}
else
{
//沒有括弧,而且已經分號結束了,那麼則是屬性名稱
classObject.attributes.add(strs[i].substring(0, strs[i].length() - 1 ));
break;
}
}
else
{
//包含了括弧,則是方法
classObject.methods.add(strs[i].substring(0, loc));
//System.out.println("1111111111111111:" + line);
break;
}
}
}
}
}while (line != null);
return classObject;
}
/**
* 判斷字串是否以小寫字元開始
* @param str
* @return
*/
public boolean isStartWithLower(String str)
{
return str.startsWith(str.toLowerCase().substring(0, 1));
}
/**
* 判斷是否java的關鍵字
* @param str
* @return
*/
public boolean isKeyWords(String str)
{
return isInculeList(str, keyWords);
}
/**
* 判斷是否java的資料類型
* @param str
* @return
*/
public boolean isDataType(String str)
{
return isInculeList(str, dataTypes);
}
/**
* 判斷是否java的存取權限類型
* @param str
* @return
*/
public boolean isCallAuth(String str)
{
return isInculeList(str, callAuths);
}
/**
* 判斷一個字串是否在一個資料列表中
* @param str
* @param lists
* @return
*/
public boolean isInculeList(String str, String[] lists)
{
boolean result = false;
//擷取java的所有類型
for (int i = 0; i < lists.length; i++)
{
if (lists[i].equals(str))
{
//包含在列表中
result = true;
break;
}
}
return result;
}
/**
* 去類集合
* @param path
* @return
*/
public List<ClassObject> getClassList(String path) throws Exception
{
File file = new File(path);
String[] list = file.list();
//檔案名稱
String fileName = "";
//類對象
ClassObject classObject;
//迴圈擷取資料
for (int i = 0; i < list.length; i++)
{
//得到檔案的全路徑
fileName = path + "/" + list[i];
//得到檔案對象
file = new File(fileName);
//判斷是否java檔案
if (checkIsJava(list[i]))
{
//得到java資料
classObject = analyseJavaFile(path, list[i]);
if (classObject != null)
{
//增加一個資料
this.classList.add(classObject);
}
}
else if (file.isDirectory())
{//是一個目錄
//遞迴執行
getClassList(fileName);
}
}
return this.classList;
}
/**
* 檔案中是否包含某個字串
* @param fullPath
* @param str
* @return
* @throws Exception
*/
public boolean isIncludeStr(String fullPath, String str) throws Exception
{
List<String> list = new ArrayList<String>();
list.add(str);
return isIncludeStrs(fullPath, list);
}
/**
* 檔案中是否包含了知道的字串
* @param fullPath
* @param strs
* @return
*/
public boolean isIncludeStrs(String fullPath, List<String> strs) throws Exception
{
boolean result = false;
List<String> tempStrs = strs;
//得到檔案讀對象
RandomAccessFile randomAccessFile = new RandomAccessFile(fullPath, "r");
//讀取一行資料
String line = "";
do
{
//讀一行
line = randomAccessFile.readLine();
if (line == null)
{
break;
}
else
{
//去掉前後空格
line = line.trim();
}
if ((tempStrs != null) && (!tempStrs.isEmpty()))
{
for (int i = 0; i < tempStrs.size(); i++)
{
//包含改字串
if (line.indexOf(tempStrs.get(i)) != -1)
{
//去掉該資料
tempStrs.remove(i);
}
}
}
else
{
//已經為空白,則表示已經完全符合了所以字串,跳出
break;
}
}while (line != null);
if ((tempStrs == null) || (tempStrs.isEmpty()))
{
//完全比配
result = true;
}
return result;
}
/**
* 檢查代碼是否被使用,0:被使用了,1:沒有被使用,2:無法確定
* @param path
* @param className
* @param codeName
* @return
*/
public int checkUsed(String path, String className, String codeName) throws Exception
{
//沒有被使用
int result = 1;
File file = new File(path);
//得到列表
String[] list = file.list();
//全路徑
String fullPath = "";
List<String> checkList = new ArrayList<String>();
checkList.add(className);
checkList.add(codeName);
if ((list != null) && (list.length > 0))
{
//迴圈擷取資料
for (int i = 0; i < list.length; i++)
{
//如果為同一個類,則不檢查
if (list[i].equals(className + ".java"))
{
continue;
}
//得到檔案的全路徑
fullPath = path + "/" + list[i];
file = new File(fullPath);
//判斷是檔案還是目錄
if (file.isFile())
{
if (isIncludeStrs(fullPath, checkList))
{
//既包含了類名,又包含方法名,則表示該方法,鐵定被使用了
return 0;
}
else if (isIncludeStr(fullPath, codeName))
{
//調用了方法,且沒有找到類,則無法確定
result = 2;
//繼續判斷下一個
continue;
}
}
else if (file.isDirectory())
{//是一個目錄
//遞迴執行
checkUsed(fullPath, className, codeName);
}
}
}
return result;
}
/**
* 擷取沒有被使用的代碼
* @param path
* @param className
* @param codeNames
* @param type
* @return
* @throws Exception
*/
public List<UnUsedCode> getUnUsedCode(String path, String className, List<String> codeNames, int type) throws Exception
{
List<UnUsedCode> result = new ArrayList<UnUsedCode>();
UnUsedCode unUsedCode;
if ((codeNames != null) && (!codeNames.isEmpty()))
{
//迴圈擷取屬性
for (int j = 0; j < codeNames.size(); j++)
{
String codeName = codeNames.get(j);
//判斷是否被使用
int usedType = checkUsed(path, className, codeName);
//沒有被使用
if (usedType != 0)
{
//建立對象
unUsedCode = new UnUsedCode();
unUsedCode.className = className; //類名
unUsedCode.codeName = codeName; //屬性名稱
unUsedCode.type = type; //類型為屬性
if (usedType == 1)
{
unUsedCode.descript = "沒有被使用";
}
else if (usedType == 2)
{
unUsedCode.descript = "無法確定";
}
//增加一條資料
result.add(unUsedCode);
}
}
}
return result;
}
/**
* 得到未使用的代碼
* @param path
* @return
*/
public List<UnUsedCode> getUnUsedCode(String path) throws Exception
{
//結果
List<UnUsedCode> result = new ArrayList<UnUsedCode>();
//得到類對象
List<ClassObject> classList = getClassList(path);
ClassObject classObject;
List<UnUsedCode> unUsedAttrList;
List<UnUsedCode> unUsedMethodList;
//資料不為空白
if ((classList != null) && (!classList.isEmpty()))
{
//迴圈擷取資料
for (int i = 0; i < classList.size(); i++)
{
//擷取一個資料
classObject = classList.get(i);
//得到一個類中沒有使用的屬性列表
unUsedAttrList = getUnUsedCode(path, classObject.className, classObject.attributes, TYPE_ATTRIBUTE);
if ((unUsedAttrList != null) && (!unUsedAttrList.isEmpty()))
{
//增加資料
result.addAll(unUsedAttrList);
}
//得到一個類中沒有使用的屬性列表
unUsedMethodList = getUnUsedCode(path, classObject.className, classObject.methods, TYPE_METHOD);
if ((unUsedMethodList != null) && (!unUsedMethodList.isEmpty()))
{
//增加資料
result.addAll(unUsedMethodList);
}
}
}
return result;
}
public static void main(String[] args)
{
FindUnUsedCode findUnUsedCode = new FindUnUsedCode();
try
{
List<UnUsedCode> list = findUnUsedCode.getUnUsedCode("C:/com");
if ((list != null) && (!list.isEmpty()))
{
for (int i = 0; i < list.size(); i++)
{
UnUsedCode unUsedCode = list.get(i);
System.out.println(unUsedCode.className + "." + unUsedCode.codeName
+ " " + (unUsedCode.type==TYPE_METHOD?"方法":"屬性") + " " + unUsedCode.descript);
}
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}