开源码应用之Eclipse篇
开写这篇的时候,恰逢Eclpse Mars(4.5)正式公布,最终由日蚀变登火星了,也离我開始基于Eclipse开发产品已经过去10年,这10年间,经历了Eclipse由私有核心框架到拥抱OSGi, 由单一Java IDE成长为巨无霸式的技术平台。由纯桌面到Web,嵌入式全面开花,个人也经历了从普通开发人员成长为committer,又离开社区的过程,唯一不变的是:Eclipse依旧是我开发Java唯一的选择。
对于这样一个由全世界最smart的一群人贡献和维护的开源项目(群)。我相信不论什么热爱这个行业的project师都能从中获得收益,这次就谈谈我基于Eclipse写的一个小工具。
不知道大家有没有类似的体会,每到产品公布期截止的时候,team就会開始忙乱的整理Java源码中的license声明问题,严格统一的开发风格对全部的team来讲,基本都是一种奢望。从头開始不可能,那怎么办。不修复吧,不能公布,修复吧。这种烂活没人愿意干,大概说来。修复Java源码里面的license声明分为下面两个主流方式:
1. 既然是Java源码,那就Java上啊,不就读出文件来。插入或替换吗?,定位吗,嗯,文件头的easy,成员变量型的,得想想...
2. 杀鸡焉用牛刀?,组合下Unix里面的小命令,分分钟搞定。
两种方式下的结果我都见过。实话说,的确不怎么样。
这件事情简单吗?说实话不难,但 Oracle依旧把Java源码里的license声明整成以下这个模样,就为了把曾经Sun的license声明改成自己的。
这对非常多有代码格式强迫症的project师来讲。比杀了他们还难受啊。
事实上我并没有接到这种烂活。我仅仅是思考了下。假设要处理好。该怎么办?嗯,这事要搞好,要是能操纵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的回调方法里面,代码例如以下。
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. 处理项目。
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源文件。
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声明。
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声明类似以下这个样例。
public class Demo {
public static final String COPYRIGHT = "(C) Copyright IBM Corporation 2013, 2014, 2015."; public Demo() { } public void hello() { } }
它的处理方式例如以下。
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是否已经修复。
来看看一个简单的測试结果吧。
这个是修复前的
package com.demo; public class Demo {
public Demo() { } public void hello() { } }
这个是修复后的
/* 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上开源,喜欢能够去下载并測试。源码里面附有一份具体安装的文档。
开源码应用之Eclipse篇的更多相关文章
- 22个值得收藏的android开源码-UI篇
本文介绍了android开发人员中比較热门的开源码,这些代码绝大多数能够直接应用到项目中. FileBrowserView 一个强大的文件选择控件.界面比較美丽,使用也非常easy. 特点:能够自己定 ...
- 从零教你如何获取hadoop2.4源码并使用eclipse关联hadoop2.4源码
从零教你如何获取hadoop2.4源码并使用eclipse关联hadoop2.4源码http://www.aboutyun.com/thread-8211-1-1.html(出处: about云开发) ...
- Openfire4源码部署到eclipse中并编译
Openfire4源码部署到eclipse中并编译 概述 Openfire是众所周知的基于xmpp协议的IM开源服务,所有操作,配置,监控,调试等以B/S方式进行展示,非常的方便管理员进行管理.它的强 ...
- spring-如何将spring源码成功导入Eclipse中
一.从 github上下载Spring源码到本机 二.利用 Gradle 编译 Spring 源码 环境: - Spring源码版本:spring-framework-4.3.x - Gradle版本 ...
- Hadoop2源码分析-准备篇
1.概述 我们已经能够搭建一个高可用的Hadoop平台了,也熟悉并掌握了一个项目在Hadoop平台下的开发流程,基于Hadoop的一些套件我们也能够使用,并且能利用这些套件进行一些任务的开发.在Had ...
- (转)Linux-HA开源软件Heartbeat(配置篇)
原文:http://ixdba.blog.51cto.com/2895551/548625 http://gzsamlee.blog.51cto.com/9976612/1828870 Linux-H ...
- C++开源码项目汇总
Google的C++开源码项目 v8 - V8 JavaScript Engine V8 是 Google 的开源 JavaScript 引擎. V8 採用 C++ 编写,可在谷歌浏览器(来自 G ...
- 如何将spring源码导入到eclipse中
如何将spring源码导入到eclipse中 1. 下载spring源码 可以在github官网中找到spring源码来下载,或者直接通过git下载,是一样的,这里演示 直接在github网站下载, ...
- 鸿蒙内核源码分析(GN应用篇) | GN语法及在鸿蒙的使用 | 百篇博客分析OpenHarmony源码 | v60.01
百篇博客系列篇.本篇为: v60.xx 鸿蒙内核源码分析(gn应用篇) | gn语法及在鸿蒙的使用 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙 ...
随机推荐
- Java下String逗号数组和List<String>的互相转换
说明:很遗憾,组装的时候只能遍历. 方法: public static String listToString(List<String> list){ if(list==null){ re ...
- 疑似checkpoint堵塞数据库连接
注:这个说法是不成立的,问题已经解决,但是无法正确的定位到具体什么原因:[20140702]奇怪的应用程序超时 背景: 开发通过应用程序的日志发现间歇性的出现,数据库连接超时 原因: 只能大概猜测,没 ...
- python函数getopt用法
python内建模块,用来处理命令行参数 格式:getopt(args, shortopts, longopts = []) 参数args一般是sys.argv[1:]sys.argv[0]表示执行文 ...
- t-SNE和LDA PCA的学习
t-SNE 可以看这篇文章: http://bindog.github.io/blog/2016/06/04/from-sne-to-tsne-to-largevis/ LDA可以看这篇文章: htt ...
- linux服务器网络配置
一.配置linux服务器的网络 手动修改配置网卡文件 先检查网卡是否正常 lspci |grep Ether 与网卡相关的TCP/IP网络配置文件位置 /etc/sysconfig/network-s ...
- 从HTML5移动应用现状谈发展趋势
时光如梭,自2008年HTML5诞生以来已经过去了5年的时间,作为新一代的Web标准,它自问世以来就受到方方面面的强烈关注,也引起了许多争议,支持者因其开放强大的特点而鼓吹它的美好前景,质疑者因其迟迟 ...
- [Spring boot] Integrating with h2 database
In pom.xml add dependency: <dependencies> <dependency> <groupId>org.springframewor ...
- Node.js 使用爬虫批量下载网络图片到本地
图片网站往往广告众多,用Node.js写个爬虫下载图片,代码不长,省事不少,比手动一张张保存简直是天与地的区别.以前用Java也做过远程图片下载,但Node.js的下载速度更让人咂舌,这也是非阻塞式变 ...
- Invalid code signing entitlements. Your application bundle's signature contains
http://code4app.com/requirement/54128041933bf0e0308b5204 Invalid code signing entitlements. Your app ...
- Andrew Ng机器学习课程6
Andrew Ng机器学习课程6 说明 在前面尾随者台大机器学习基石课程和机器学习技法课程的设置,对机器学习所涉及到的大部分的知识有了一个较为全面的了解,可是对于没有动手敲代码并加以使用的情况,基本上 ...