看开源项目源代码或者看项目源码的时候,常常觉得代码看起来比较啰嗦,总觉得有些的应该可以简化的。
比如:
1 过多的getter、setter 这些显然是没技术含量的,但是它夹杂在类的其他代码中,影响了阅读。我的做法是把去掉其getter、setter,然后它直接改为public 变量。——虽然这有违java封装原则,但是我想这却是有利于代码阅读的。。
2 过多的注释,一些开源项目往往就是大版大版的注释,注释多是好是坏姑且不论,但是有时候我们只是想看源码,不想看注释,这个时候它就非常讨嫌了!
3 过多的try catch 等异常扑捉。 这显然也是影响阅读的,试想,如果关键代码就那么一行一个方法20个字符之内,外面却包了3、4个try catch,多烦人啊,最后还有finally。。。 当然,如果想详细了解源码,彻底弄明白其功能,仔细阅读try catch是有必要的,但有时候,我们只想快速的、大致的阅读一下源码,这个时候也有必要简化一下那个类了。
4 日志,一般来说,日志只是处理调试信息、警告信息、异常信息等用的, 去掉它并不影响功能
5 注解 有些annotation并不是必须的,去掉完全不影响阅读,比如:@Override、@SuppressWarnings..等
众所周知,看源码并不是一件简单的事,因为,我们常常没有那个对应的UML图,功能说明啊等。(不知道为什么都这样。。。)
要是能简化代码,那么再来阅读就会轻松多了!本想这么个简单的功能,别人应该已经做出来了的。可是,我网上搜索却总也找不到。——难道大家都没有这方面的考虑吗??
困扰了我很久,今天有点时间,想起这事,于是自己写了一个简单实现:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.luobk.po.VarObject;
/**
* 代码阅读优化器 CodeSimplifier或者也可以称为 CodePurifier吧
* goSimplifyProject 只针对 目录
* 若是 想直接的simplify一个java文件,则用simplifyFile即可
*
*/
public class CodeSimplifier {
// public static String SOURCE_DIR = "D:\\WS\ABC\\src";
public static String SOURCE_DIR = "D:\\WS\\CodeSimplifier\\src";
public static String DEST_DIR = "D:\\WS\\CodeSimplifier\\src3";
public static String SOURCE_FOLDER = "src";
public static String LOGGER_NAME = "log";
public static boolean USE_SOURCE_FOLDER = false;
/**
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
goSimplifyProject(SOURCE_DIR,DEST_DIR);
// simplifyFile(new File(SOURCE_DIR), DEST_DIR);
// System.out.println("sfasf".matches("[.]+"));
// System.out.println("asf " +
// "a\" f艾丝凡 f\nasf\nnnbbsa;\"aa".matches("a\"(.|\\s)*;\"aa"));
// System.out.println("\n".matches("(\\s+)"));
// System.out.println("sfasf".matches(".*asf")); // .+ 。* 是可以自动伸缩的 , 默认的 。。。 非贪婪模式??
// String ss = "System.out. println(\"sfas r asfa fas \");\n" +
// "System.out. println(\"sfas + \n asfa fas \");";
// System.out.println("ss . replaceAll : "+ss.
// replaceAll("\\s+System\\s*\\.\\s*out\\s*\\.\\s*print(ln)?\\s*\\(\\s*\"(.|\\s)*\"\\s*\\)\\s*;",""));
// StringBuffer sb = new StringBuffer("abcdef\nxyz\n>");
// sb.deleteCharAt(sb.length()-1);
// System.out.println("matches++ = "+"sfaaf".matches(".{6}"));// + 后面的 {n} 失效
// System.out.println("matches++ = "+"fasf \n}".matches(".+")); // 匹配任何字符包括' ',但是不包括 \n等
// System.out.println("sfaf>s]f[s.".replaceAll("]","A"));// 到底要不要转义 [] 啊!!
// sb.reverse 有用的方法!是String 没有提供的!
// String filePath = "D:\\iEMPWorkspaces\\CodePurifier\\src\\po\\";
// String projectPath = "D:\\iEMPWorkspaces\\CodePurifier\\src2\\Bootstrap.java";
// String destDir = "D:\\iEMPWorkspaces\\CodePurifier\\src3\\";
// System.out.println("ddasfasf aa\n = asf qwf".replaceAll("\\s{2}", "X"));
// System.out.println("ddasfasf aa\n = 2asf qwf".replaceAll("\\s[2]", "X"));
// System.out.println(capitalize("asfaf"));
// File f = new File("D:\\iEMPWorkspaces\\CodePurifier\\src2\\sdaf.java");
// f.mkdir();
// FileOutputStream fos = new FileOutputStream(f);
// BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos));
// bw.write(destDir);// if we don't write sth , the file will never be created,just in mem.
// bw.close();
// System.out.println("f.length()=="+f.length()+" f.exists();===+"+f.exists());
// System.out.println(" is DIR == "+ f.getCanonicalPath()+"\\"+f.getName());
}
public static void goSimplifyProject(String projectPath,String destprojectPath) throws IOException {
File destProjectDir = new File(destprojectPath);
if(!destProjectDir.exists())destProjectDir.mkdir();
if(!projectPath.endsWith("\\")) projectPath = projectPath + "\\";
if(!destprojectPath.endsWith("\\")) destprojectPath = destprojectPath + "\\";
File sourceDir = null;
if(USE_SOURCE_FOLDER)sourceDir = new File(projectPath+SOURCE_FOLDER+"\\");
else sourceDir = new File(projectPath);
simplifyProject(sourceDir,destprojectPath,true);
}
public static void simplifyProject(File fileOrDir,String destDir,boolean isRoot) throws IOException {
if(fileOrDir.isDirectory()) {
/** 此种过滤行不通!
FilenameFilter filter = new FilenameFilter() {
private String type = ".java";
@Override
public boolean accept(File dir, String name) {
//return true;
return name.endsWith(type);
}
};*/
File[] files = fileOrDir.listFiles();
for (int i = 0; i < files.length; i++) {
File file = files[i];
if(isRoot) {
if(USE_SOURCE_FOLDER)destDir = destDir+SOURCE_FOLDER+"\\";
File destRootDir = new File(destDir);
if(!destRootDir.exists())destRootDir.mkdir();
simplifyProject(file,destDir,false);
}else {
// Build the new directory,othewise we'll got:
//"java.io.FileNotFoundException: D:\iEMPWorkspaces\CodePurifier\src2\po\Sdfa.java (系统找不到指定的路径。)"
//System.out.println("lastDir=="+lastDir);
String lastDir = fileOrDir.getName();
File directory = new File(destDir+lastDir);
if(!directory.exists())directory.mkdir();
simplifyProject(file,destDir+lastDir+"\\",false);
}
}
}else if(fileOrDir.isFile()){
if(fileOrDir.getName().endsWith(".java"))simplifyFile(fileOrDir,destDir);
}
}
/**
*
* FOR JAVA FILE ONLY CURRENTLY
*
* REMOVE THE COMMENTS AND THE GETTER/SETTER, AND CHANGE THE MODIFIRE OF RALATED ONE TO 'PUBLIC'
*
* ASSUME THAT THE GETTER/SETTER USES PARAMETER AS THE SAME NAME OF THE VARAIBLE
*
* THATS ALL
*
* @param file
* @throws IOException
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void simplifyFile(File file,String destDir) throws IOException {
FileInputStream fis = new FileInputStream(file);
System.out.println(file.getAbsolutePath());
String outputFile = destDir+file.getName();
//System.out.println("output FIle Path"+outputFile);
FileOutputStream fos = new FileOutputStream(new File(outputFile));// assume the outputFile is not the input one
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos));
boolean isComments = false;
boolean isEmptyRow = false;
String line = "";
String oline = "";
List privateVars = new ArrayList();
StringBuffer sb = new StringBuffer();
while((line=br.readLine())!=null) {
oline = line;
line = line.trim().replaceAll("\\s+", " ");
// white line
if("".equals(line)) {
continue;
}
// 去掉注释
//if(isComments&&!(line.endsWith("*/"))&&!line.endsWith("*/ "))continue;
//TODO 暂不考虑 行中/*前面还有代码的情况
// 眼考虑 /* */在同一行的情况,—— 如果*/后面还有内容,则不管了 !
if(line.startsWith("/*")&&(line.endsWith("*/")||line.endsWith("*/ "))) {
continue;
}
if(line.startsWith("/*")) {
isComments = true;
continue;
}
if(isComments&&!line.endsWith("*/")&&!line.endsWith("*/ ")) {//line.startsWith("*")&&
//ifComments = true;
continue;
}
//TODO 暂不考虑 行中*/后面还有代码的情况
if(line.endsWith("*/")||line.endsWith("*/ ")) {
//System.out.println("line : "+line);
isComments = false;
continue;
}
if(line.startsWith("//")) {
continue;
}
String[] strs = line.replace(';', ' ').trim().split(" ");
if(line.startsWith("private ") &&strs.length==3 && line.endsWith(";")) {
//line = line.replaceFirst("private", "public");
//oline = oline.replaceFirst("private", "public");
VarObject vo = new VarObject();
vo.setVar(strs[2]);
vo.setType(strs[1]);
privateVars.add(vo);
}
String regex = "\\s*[\\}\\)\\]>]\\s*";
if(oline.matches(regex)) {
if(isEmptyRow) {
sb.deleteCharAt(sb.length()-1);
sb.append(line+"\n");
}
else sb.append(oline+"\n");
isEmptyRow = true;
}else {
isEmptyRow = false;
sb.append(oline+"\n");
}
}
br.close();
String ret = sb.toString();
System.out.println("Before:\n" + ret);
//ret = ret.replaceAll("\\) ",")").replaceAll("\\s?\\(\\s+\\)\\s?", "()");//.replaceAll("\n", "");
ret = filterGetterSetters(ret, privateVars);
ret = filterLogs(ret);
ret = filterAnnotations(ret);
ret = filterTryCatchs(ret);
bw.write(ret);
bw.close();
System.out.println("After:\n" + ret);
}
// 无止境的 CLRF 空字符。。。 \\s* 繁琐。。。
private static String filterAnnotations(String str) {
String regex = "((@\\s*Override)|(@\\s*SuppressWarnings\\s*\\(.*\\)))\\s*";
str = str.replaceAll(regex, "");
return str;
}
private static String filterGetterSetters(String str,List privateVars) {
for (Iterator iterator = privateVars.iterator(); iterator.hasNext();) {
VarObject vo = (VarObject) iterator.next();
String getterRegEx = "\\s+public\\s+"+vo.getType()+"\\s+get"+capitalize(vo.getVar())+"\\s*\\(\\s*\\)\\s*\\{\\s*return\\s+"+vo.getVar()+"\\s*;\\s*\\}";
String setterRegEx = "\\s+public\\s+void\\s+set"+capitalize(vo.getVar())+"\\(\\s*"
+vo.getType()+"\\s+\\w+\\s*\\)\\s*\\{\\s*(this\\.)?"+vo.getVar()+"\\s*=\\s*\\w+;\\s*\\}";
Matcher mgetter = Pattern.compile(setterRegEx).matcher(str);
Matcher msetter = Pattern.compile(getterRegEx).matcher(str);
if(mgetter.find() && msetter.find()){
str = mgetter.replaceAll("");
str = msetter.replaceAll("");
str = str.replaceFirst("private\\s+"+vo.getType()+"\\s+"+vo.getVar(), "public "+vo.getType()+" "+vo.getVar());
//oline = oline.replaceFirst("private", "public");
}
}
return str;
}
// TODO 要考虑 log 里面内容换行的情况 。
// TODO 要考虑 System.out.println 里面内容换行的情况 ,但是 如果 是 System.\n out\n .println的情况就不管了,实在太繁琐。。。。。
private static String filterLogs(String str) {
String regexLog = "\\s+((private)|(public))\\s+.+log\\s+=\\s+Logger\\.getLogger.+;";
String regex = LOGGER_NAME+"\\s*\\.\\s*((debug)|(warn)|(error)|(fatal)|(finest)|(finer)|(fine)|(warning)|(severe))\\s*\\(\\s*\".*\"\\s*\\)\\s*;\\s*";
//String regex = "log\\s*\\.\\s*[(fine)|(severe)]";
//String regex4Javalog = "^log\\s*\\.\\s*[(finest)(finer)(fine)(warning)(severe)]\\s*\\(\\s*\".*\"\\s*\\)\\s*;";
str = str.replaceAll(regexLog, "").replaceAll(regex, "");
//if(Pattern.compile(regex).matcher(str).find()) {
//}
// TODO (.|\\s)*此处引起异常
//str = str.replaceAll("\\s+System\\s*\\.\\s*out\\s*\\.\\s*print(ln)?\\s*\\(\\s*\"(.|\\s)*\"\\s*\\)\\s*;", "");
return str;
}
// 太复杂, 而且可能影响原功能,所以,只做简单处理。。 这显然是会影响原有功能的!!
private static String filterTryCatchs(String str) {
// TODO
String regexTry = "";//"try\\s*\\{\\s*";// 暂不处理
String regexCatch = "\\}\\s*catch\\s*\\(\\s*\\w+\\s+\\w+\\s*\\)\\s*\\{\\s*.+\\s*";
String regexFinally = "";//"\\s*\\}\\s*finally\\s*\\{\\s*.+\\s*\\}";// 暂不处理
//str = str.replaceAll(regexTry, "").replaceAll(regexCatch, "").replaceAll(regexFinally, "");
//
if(Pattern.compile(regexCatch).matcher(str).find()) {
str = str.replaceAll(regexCatch, " }catch(Exception e){e.printStackTrace();");
}
return str;
}
// 将 }\n } \n ) \n) ... 的情况合并成 } } ) )...
private static String foldEmptyRow(String str) {
// TODO
return str;
}
public static String capitalize(String str) {
if(str==null)return "";
if(str.length()==1)return str.toUpperCase();
Character firstLetter = str.charAt(0);
return Character.toUpperCase(firstLetter)+str.substring(1);
}
@SuppressWarnings("unchecked")
public static boolean checkParetheis(String str) {
Matcher matcher = Pattern.compile("[\\(\\{\\[<\\)\\}\\]>\"\']").matcher(str);
Pattern p = Pattern.compile("[\\)\\}\\]>\"\']");
Stack stack = new Stack<>();
while(matcher.find()) {
String c = matcher.group();
if(p.matcher(c).matches()) {
if(stack.size()>0){
char top = ((String) stack.peek()).charAt(0);
if(c.charAt(0)==converseChar(top)) {
stack.pop();
}else {
stack.push(c);
}
}else {
stack.push(c);
}
}else {
stack.push(c);
}
}
System.out.println("stack remains :" +stack.toString());
if(stack.size()>0)return false;
return true;
}
@SuppressWarnings("unchecked")
public static boolean checkParetheis(File f) {
if(f.exists()) {
FileInputStream fis = null;
try {
fis = new FileInputStream(f);
char c = 'a';
int ifEnd = 0;
Stack stack = new Stack<>();
while((ifEnd=fis.read())!=-1) {// -1 是可以转换为char的,但是 转换之后 c 已经不再是 -1 !!!
c = (char)ifEnd;
System.out.print(c);
if(c=='('||c=='{'||c=='<'||c=='[')stack.push(c);
else if(c==')'||c=='}'||c=='>'||c==']') {
char top = (char) stack.peek();
if(c==converseChar(top)) {
stack.pop();
}else {
stack.push(c);
}
}else if(c=='"'||c=='\'') {
if(stack.size()>0){
char top = (char) stack.peek();
if(c==converseChar(top)) {
stack.pop();
}else {
stack.push(c);
}
}else {
stack.push(c);
}
}
}
System.out.println("ifEnd :"+ifEnd+" c :"+ (char)(-1));
if(stack.size()==0)return true;
System.out.println("stack remains :" +stack.toString());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return false;
}
public static char converseChar(char c) {
if(c=='(')return ')';
if(c=='{')return '}';
if(c=='<')return '>';
if(c=='[')return ']';
if(c=='"')return '"';
if(c=='\'')return '\'';
return ' ';
}
}
VarObject是一个简单javaBean:
package com.luobk.po;
/**
*
* @author LBK
*
*/
public class VarObject {
public String getVar() {
return var;
}
public void setVar(String var)
//dsfs
{
this.var = var;
}
public String getType( )
{
return type;
}
public void setType(String v)
{
type = v;
}
// var name
private String var;
// var type
private String type;
}
写是写出来了,但是还是有很多问题。 比如对try catch块到底如何处理比较好? 对于简单行(单字符行)的处理的处理怎么样才好?而且,程序本身有bug,有些地方只是做了简单处理,复杂情况下则引发bug。而且,没有考虑对其他的类的影响...