• 开源码应用之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("
    ")) {
    			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上开源,喜欢能够去下载并測试。源码里面附有一份具体安装的文档。


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







  • 相关阅读:
    使用Docker Swarm搭建分布式爬虫集群
    如果你不知道做什么,那就学一门杂学吧
    正则表达式re.sub替换不完整的问题现象及其根本原因
    Visual Studio 2019 正式版今日发布 key
    net core 记录自定义端口多个方式
    HTTP Error 500.0
    来自后端的逆袭 blazor简介 全栈的福音
    创建一个RAS 非对称 公私密钥示例
    树莓派安装window ioT
    WPF USB设备采集开源工具介绍
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/7286640.html
Copyright © 2020-2023  润新知