CodeSimplifier
看开源项目源代码或者看项目源码的时候,常常觉得代码看起来比较啰嗦,总觉得有些的应该可以简化的。
比如:
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。而且,没有考虑对其他的类的影响...