开源代码应用之Eclipse篇

来源:互联网 发布:淘宝外卖怎么找不到了 编辑:程序博客网 时间:2024/06/08 06:10

开写这篇的时候,恰逢Eclipse Mars(4.5)正式发布,终于由日蚀变登火星了,也离我开始基于Eclipse开发产品已经过去10年,这10年间,经历了Eclipse由私有核心框架到拥抱OSGi, 由单一Java IDE成长为巨无霸式的技术平台,由纯桌面到Web,嵌入式全面开花,个人也经历了从普通开发者成长为committer,又离开社区的过程,唯一不变的是:Eclipse依然是我开发Java唯一的选择。

对于这样一个由全世界最smart的一群人贡献和维护的开源项目(群),我相信任何热爱这个行业的工程师都能从中获得收益,这次就谈谈我基于Eclipse写的一个小工具。

不知道大家有没有类似的体会,每到产品发布期截止的时候,team就会开始忙乱的整理Java源代码中的license声明问题,严格统一的开发风格对所有的team来讲,基本都是一种奢望,从头开始不可能,那怎么办,不修复吧,不能发布,修复吧,这样的烂活没人愿意干,大概说来,修复Java源代码里面的license声明分为以下两个主流方式:
1. 既然是Java源代码,那就Java上啊,不就读出文件来,插入或替换吗?,定位吗,嗯,文件头的easy,成员变量型的,得想想...
2. 杀鸡焉用牛刀?,组合下Unix里面的小命令,分分钟搞定。

两种方式下的结果我都见过,实话说,的确不怎么样。

这件事情简单吗?说实话不难,但 Oracle依然把Java源代码里的license声明整成下面这个模样,就为了把以前Sun的license声明改成自己的。

oracle

这对很多有代码格式强迫症的工程师来讲,比杀了他们还难受啊。

其实我并没有接到这样的烂活,我只是思考了下,如果要处理好,该怎么办?嗯,这事要搞好,要是能操纵Java源代码的每一个部分不就行了?

哇靠,有人马上会跳起来说,这得懂编译器哪,对,就是编译器,不过也没有那么复杂,也就用了一丁丁点AST知识,不知道AST?哦,哪也没问题,有Eclipse替你做。

于是我开始动手实现这么一个能快速修复Java源代码中license声明的小工具,基本思路是基于Eclipse JDT里的AST实现,在Java语法这个粒度来修改,并做成一个Eclipse Plug-in,这下大家安装后,简单到点个button,就能完成工作。

具体实现步骤如下:

1. 生成一个Eclipse Plug-in项目,选个模版,最简单的那种,能点toolbar上面的button,弹出个"hello, world"对话框就可以。不知道怎么开发一个Eclipse Plug-in啊,没关系,看完这篇blog,你就会了。(别忘了好评!)

2. 在Action的回调方法里面,代码如下。

123456789101112131415161718192021222324
public void run(IAction action) {      license = getLicenseContent(LICENSE_FILE_NAME);      license_inline = getLicenseContent(LICENSE_INLINE_FILE_NAME);      if (license_inline.endsWith("\n")) {          license_inline = license_inline.substring(0, license_inline.length() - 1);      }      sum = 0;       IWorkspace workspace = ResourcesPlugin.getWorkspace();      IWorkspaceRoot root = workspace.getRoot();      IProject[] projects = root.getProjects();      for (IProject project : projects) {          try {              if (project.isOpen()) {                  processProject(project);              }                 } catch (Exception e) {              MessageDialog.openInformation(window.getShell(), "Fix License", "Exception happened, please check the console log.");              e.printStackTrace();              return;          }      }      MessageDialog.openInformation(window.getShell(), "Fix License", "All java source files have been processed. Total = " + sum);  }

首先获得license的内容,分为主license和行内license,具体内容这里就不显示了,然后获取Eclipse里面所有的项目,遍历每个项目并处理,这里只处理打开的项目,如果你有不想处理的项目,关闭就行。

3. 处理项目。

1234567891011121314
private void processProject(IProject project) throws Exception {      if (project.isNatureEnabled("org.eclipse.jdt.core.javanature")) {          IJavaProject javaProject = JavaCore.create(project);          IPackageFragment[] packages = javaProject.getPackageFragments();          for (IPackageFragment mypackage : packages) {              if (mypackage.getKind() == IPackageFragmentRoot.K_SOURCE) {                  for (ICompilationUnit unit : mypackage.getCompilationUnits()) {                      sum = sum + 1;                      processJavaSource(unit);                  }              }          }      }  }

当然只修复Java项目,没有Java nature的,一律抛弃。
获得Java项目后,获取所有的package,这里的package和通常意义上Java的package不同,具体意义看API,就当课后作业。
再进一步,就可以获取Java源文件,并取得编译单元,有了这个,以后的路就有方向了。

4. 处理Java源文件。

123456789101112131415161718
private void processJavaSource(ICompilationUnit unit) throws Exception {      ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager();      IPath path = unit.getPath();      try {          bufferManager.connect(path, null);          ITextFileBuffer textFileBuffer = bufferManager.getTextFileBuffer(path);          IDocument doc = textFileBuffer.getDocument();          if ((license !=null) && (license.length() > 0)) {              processHeadLicense(doc);          }          if ((license_inline != null) && (license_inline.length() > 0)) {              processInlineLicense(doc);          }          textFileBuffer.commit(null, false);      } finally {          bufferManager.disconnect(path, null);      }  }

这里用到了一些Eclipse Jface text包里面的东西,和Java里面常见的文件读写API有些不一样,但基本思想是一致的。等取到了IDocument对象,就可以开始正式的license处理。

5. 处理Java文件头license声明。

12345678910111213141516171819202122232425
private void processHeadLicense(IDocument doc) throws Exception {      CompilationUnit cu = getAST(doc);      Comment comment = null;      if (cu.getCommentList().size() == 0) {          doc.replace(0, 0, license);      } else {          comment = (Comment)cu.getCommentList().get(0);          String firstComment = doc.get().substring(comment.getStartPosition(), comment.getStartPosition() + comment.getLength());          if (validateHeadLicense(firstComment)) {              doc.replace(comment.getStartPosition(), comment.getLength(), license);          } else {              doc.replace(0, 0, license);          }             }  }   private CompilationUnit getAST(IDocument doc) {      ASTParser parser = ASTParser.newParser(AST.JLS4);      parser.setKind(ASTParser.K_COMPILATION_UNIT);      parser.setSource(doc.get().toCharArray());      parser.setResolveBindings(true);      CompilationUnit cu = (CompilationUnit) parser.createAST(null);       return cu;  }

基于AST就可以得到Java源代码里面的所有comments,接下来就可以根据各种情况插入或替换文件头的license声明。

6. 成员变量型license声明。
这种license声明类似下面这个例子。

123456789101112
public class Demo {      public static final String COPYRIGHT = "(C) Copyright IBM Corporation 2013, 2014, 2015.";       public Demo() {       }       public void hello() {       }   }

它的处理方式如下。

123456789101112131415161718192021222324252627282930313233343536373839
private void processInlineLicense(IDocument doc) throws Exception {      CompilationUnit cu = getAST(doc);      cu.recordModifications();      AST ast = cu.getAST();       if (cu.types().get(0) instanceof TypeDeclaration) {          TypeDeclaration td = (TypeDeclaration)cu.types().get(0);          FieldDeclaration[] fd = td.getFields();          if (fd.length == 0) {              td.bodyDeclarations().add(0, createLiceseInLineField(ast));          } else {              FieldDeclaration firstFd = fd[0];              VariableDeclarationFragment vdf = (VariableDeclarationFragment)firstFd.fragments().get(0);              if (vdf.getName().getIdentifier().equals("COPYRIGHT")) {                  td.bodyDeclarations().remove(0);                  td.bodyDeclarations().add(0, createLiceseInLineField(ast));              } else {                  td.bodyDeclarations().add(0, createLiceseInLineField(ast));              }          }                 }       //record changes      TextEdit edits = cu.rewrite(doc, null);      edits.apply(doc);  }   private FieldDeclaration createLiceseInLineField(AST ast) {      VariableDeclarationFragment vdf = ast.newVariableDeclarationFragment();      vdf.setName(ast.newSimpleName("COPYRIGHT"));      StringLiteral sl = ast.newStringLiteral();      sl.setLiteralValue(license_inline);      vdf.setInitializer(sl);      FieldDeclaration fd = ast.newFieldDeclaration(vdf);      fd.modifiers().addAll(ast.newModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL));      fd.setType(ast.newSimpleType(ast.newSimpleName("String")));       return fd;  }

成员变量类型的license声明处理起来稍显麻烦,主要原因是牵扯到Java成员变量的创建和解析,但其实也不是很难理解,而且从中可以学到AST是如何精细处理Java类的各个组成部分的。

7. 测试。
启动一个新的调试Eclipse Plug-in的Eclipse Runtime,导入任意几个Java项目,从菜单或工具栏上面选择“Fix License” action,完成之后检查任意的Java源文件,看看license是否已经修复。

来看看一个简单的测试结果吧。
这个是修复前的

123456789101112
package com.demo;   public class Demo {      public Demo() {       }       public void hello() {       }   }

这个是修复后的

1234567891011121314151617181920212223
/* IBM Confidential  * OCO Source Materials  *   * (C)Copyright IBM Corporation 2013, 2014, 2015.  *  * The source code for this program is not published or otherwise  * divested of its trade secrets, irrespective of what has been  * deposited with the U.S. Copyright Office. */  package com.demo;   public class Demo {      public static final String COPYRIGHT = "(C) Copyright IBM Corporation 2013, 2014, 2015.";       public Demo() {       }       public void hello() {       }   }

8. 打包分发。
这个工具Plug-in可以按Eclipse的标准插件打包并安装,或者生成一个Update Site以供用户在线安装。

好了,啰嗦了这么多,到了该结束的时刻,最后一句,这个小工具所有的源代码已经在GitHub上开源,喜欢可以去下载并测试,源代码里面附有一份详细安装的文档。

工具地址:https://github.com/alexgreenbar/open_tools.git

本文章版权归环信所有,转载请注明出处。更多技术文章请访问http://blog.easemob.com/
阅读全文
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 美的炖锅 一锅炖 隔水电炖锅可以煮饭吗 电炖盅哪个牌子好 炖盅内胆 小熊隔水电炖盅 小熊电炖盅使用方法图 隔水炖盅什么牌子好 炖盅炖汤 电炖盅使用方法 电炖盅怎么用 电炖盅什么牌子好 美的电炖盅 隔水炖盅哪个牌子好 炖盅图片 隔水炖盅 炖盅怎么用图解 隔水炖盅怎么用 炖盅的做法 电子炖盅 炖燕窝的炖盅 燕窝炖盅 陶瓷炖盅的害处 盅盅面 隔水电炖盅哪个牌子好 苏泊尔电炖盅 小熊电炖盅价格 原盅炖汤 小熊隔水炖盅食谱 隔水电炖盅怎么用 苏泊尔炖盅 海鲜冬瓜盅 营养炖盅 九阳炖盅 炖盅燕窝 八宝冬瓜盅 天际隔水炖盅食谱 瓷炖盅 紫砂锅什么牌子好 近视墨镜什么牌子好 乌鸡和什么煲汤好