• 第3篇-如何编写一个面试时能拿的出手的开源项目?


    前2篇的链接如下:

    第1篇-如何编写一个面试时能拿的出手的开源项目? 

    第2篇-如何编写一个面试时能拿的出手的开源项目?

    第1篇介博文中详细介绍过编写一个规范开源项目所要遵循的规范,并且初步实现了博主自己的开源项目Javac AST View插件,不过只搭建了项目开发的基本框架,树状结构的数据模型也是硬编码的;第2篇从Eclipse编辑器中读取Java源代码并转换为Javac的抽象语法树,然后又将Javac的抽象语法树转换为了Eclipse树形插件所识别的数据模型,在视图中动态展现出来。本篇将为这个树形插件视图完善功能。主要就是添加读入、刷新、展开与折叠的功能按钮,同时双击树中的某个结点,选中Eclipse中对应的源代码信息。

    1、添加读入、刷新、展开与折叠功能按钮

    代码比较简单,只需要定义相应的Action,然后添加到工具栏管理器IToolBarManager中即可。

    定义的相应Action如下: 

    private void makeActions() {
    	
    	
    	fFocusAction = new Action() {
    		@Override
    		public void run() {
    			performSetFocus();
    		}
    	};
    	fFocusAction.setText("&Show AST of active editor");
    	fFocusAction.setToolTipText("Show AST of active editor"); 
    	fFocusAction.setActionDefinitionId(IWorkbenchCommandConstants.FILE_REFRESH);
    	ASTViewImages.setImageDescriptors(fFocusAction, ASTViewImages.SETFOCUS);
    	
    	fRefreshAction = new Action() {
    		@Override
    		public void run() {
    			try {
    				refreshAST();
    			} catch (CoreException e) {
    				e.printStackTrace();
    			}
    		}
    	};
    	fRefreshAction.setText("&Refresh AST"); 
    	fRefreshAction.setToolTipText("Refresh AST"); 
    	fRefreshAction.setEnabled(false);
    	ASTViewImages.setImageDescriptors(fRefreshAction, ASTViewImages.REFRESH);
    	
    	fCollapseAction = new Action() {
    		@Override
    		public void run() {
    			performCollapse();
    		}
    	};
    	fCollapseAction.setText("C&ollapse"); 
    	fCollapseAction.setToolTipText("Collapse Selected Node"); 
    	fCollapseAction.setEnabled(true);
    	ASTViewImages.setImageDescriptors(fCollapseAction, ASTViewImages.COLLAPSE);
    
    	fExpandAction = new Action() {
    		@Override
    		public void run() {
    			performExpand();
    		}
    	};
    	fExpandAction.setText("E&xpand"); 
    	fExpandAction.setToolTipText("Expand Selected Node"); 
    	fExpandAction.setEnabled(true);
    	ASTViewImages.setImageDescriptors(fExpandAction, ASTViewImages.EXPAND);
    	
    	fDoubleClickAction = new Action() {
    		@Override
    		public void run() {
    			performDoubleClick();
    		}
    	};
    
    }
    

    单击4个按钮后执行的对应动作由3个函数定义,如下: 

    private void refreshAST() throws CoreException {
    	internalSetInput(uri);
    }
    
    protected void performCollapse() {
    	IStructuredSelection selection= (IStructuredSelection) fViewer.getSelection();
    	if (selection.isEmpty()) {
    		fViewer.collapseAll();
    	} else {
    		fViewer.getTree().setRedraw(false);
    		for (Object s : selection.toArray()) {
    			fViewer.collapseToLevel(s, AbstractTreeViewer.ALL_LEVELS);
    		}
    		fViewer.getTree().setRedraw(true);
    	}
    }
    
    protected void performExpand() {
    	IStructuredSelection selection= (IStructuredSelection) fViewer.getSelection();
    	if (selection.isEmpty()) {
    		fViewer.expandToLevel(3);
    	} else {
    		fViewer.getTree().setRedraw(false);
    		for (Object s : selection.toArray()) {
    			fViewer.expandToLevel(s, AbstractTreeViewer.ALL_LEVELS);
    		}
    		fViewer.getTree().setRedraw(true);
    	}
    }
    
    protected void performSetFocus() {
    	IEditorPart part= EditorUtility.getActiveEditor();
    	if (part instanceof ITextEditor) {
    		try {
    			setInput((ITextEditor) part);
    		} catch (CoreException e) {
    			showAndLogError("Could not set Javac AST view input ", e); //$NON-NLS-1$
    		}
    	}
    }
    

    performSetFocus()函数执行读入动作、refreshAST()函数执行刷新动作、performCollapse()函数执行语法树合上动作,而performExpand()函数执行语法树展开动作。可以展开特定语法树节点,只要选中这个语法树节点,然后点击展开按钮即可。  

    向工具栏管理器中添加定义好的Action,如下: 

    private void contributeToActionBars() {
    	IActionBars bars = getViewSite().getActionBars();
    	bars.getToolBarManager().add(fFocusAction);
    	bars.getToolBarManager().add(fRefreshAction);
    	bars.getToolBarManager().add(fCollapseAction);
    	bars.getToolBarManager().add(fExpandAction);
    	bars.setGlobalActionHandler(ActionFactory.REFRESH.getId(), fFocusAction);
    }
    

    在createPartControl()函数中调用相关方法,如下:

    makeActions();
    contributeToActionBars();
    

    效果如下:

      

    2、选中源代码功能

    要选中Eclipse插件中某个范围的源代码,需要调用相关函数,同时传递2个重要的参数,一个就是字符的开始位置pos,另外就是长度length。这两个参数我们可以直接从Javac的相关API中获取,修改createAST()函数,如下:

    public static EndPosTable ept = null;
    
    private JCCompilationUnit createAST(URI is) {
    	Context context = new Context();
    	JavacFileManager.preRegister(context);
    	JavaFileManager fileManager = context.get(JavaFileManager.class);
    	JavaCompiler comp = JavaCompiler.instance(context);
    	JavacFileManager dfm = (JavacFileManager) fileManager;
    
    	JavaFileObject jfo = dfm.getFileForInput(is.getPath());
    	comp.genEndPos = true;
    	JCCompilationUnit tree = comp.parse(jfo);
    	ept = tree.endPositions;	
         comp.parseFiles(otherFiles);
    
    	return tree;
    }

    需要打开JavaCompiler的genEndPos开关,这样Javac在分析Java源代码字符流的过程中,就会保存对应的语法树节点到字符结束位置的对应关系,这个关系就保存在EndPostTable中,所以我们用全局变量ept保存即可。

    在JavacASTNode中新定义2个属性,用来保存对应语法树节点在字符流中的开始与结束位置,如下:

    private int startpos,endpos;
    

    然后修改JavacASTNode的构造函数,如下:

    public JavacASTNode(int startpos,int endpos) {
    	children = new ArrayList<JavacASTNode>();
    	this.startpos = startpos;
    	this.endpos = endpos;
    }
    

    在访问者方法中为这2个属性赋值,例如在visitCompilationUnit()和visitClass()方法中赋值,实现如下:

    @Override
    public JavacASTNode visitCompilationUnit(CompilationUnitTree node, Void p) {
    	JCCompilationUnit t = (JCCompilationUnit) node;
    	JavacASTNode currnode = new JavacASTNode(t.getStartPosition(),t.getEndPosition(JavacASTViewer.ept));
    	currnode.setProperty("root");
    	currnode.setType(t.getClass().getSimpleName());
    	
    	traverse(currnode,"packageAnnotations",t.packageAnnotations);
    	traverse(currnode,"pid",t.pid);
    	traverse(currnode,"defs",t.defs);
    
    	return currnode;
    }
    
    @Override
    public JavacASTNode visitClass(ClassTree node, Void p) {
    	JCClassDecl t = (JCClassDecl) node;
    	JavacASTNode currnode = new JavacASTNode(t.getStartPosition(),t.getEndPosition(JavacASTViewer.ept));
    
    	traverse(currnode,"extending",t.extending);
    	traverse(currnode,"implementing",t.implementing);
    	traverse(currnode,"defs",t.defs);
    	
    	return currnode;
    }
    

    通过调用节点类的getStartPosition()获取开始位置,调用getEndPosition()获取结束位置,不过调用这个方法需要传递之前保存的EndPosTable信息。

    定义监听器监听双击事件,如下:

    package astview.listener;
    
    import org.eclipse.jface.text.DocumentEvent;
    import org.eclipse.jface.text.IDocumentListener;
    import org.eclipse.jface.viewers.DoubleClickEvent;
    import org.eclipse.jface.viewers.IDoubleClickListener;
    
    import astview.JavacASTViewer;
    
    public class ListenerMix implements IDocumentListener,IDoubleClickListener {
    
    	private JavacASTViewer fView;
    
    	public ListenerMix(JavacASTViewer view) {
    		fView= view;
    	}
    
    	public void dispose() {
    		fView= null;
    	}
    
    	// ...
    
    	@Override
    	public void doubleClick(DoubleClickEvent event) {
    		fView.handleDoubleClick();
    	}
    }
    

    使用这个监听器,在createPartControl()中为fViewer添加监听器,如下:

    fViewer.addDoubleClickListener(fSuperListener);
    

    这样在双击语法树某个节点时,监听器会调用fView.hanbndleDoubleClick()方法对动作做相应的处理,函数的实现如下:

    protected void performDoubleClick() {
    	if (fEditor == null) {
    		return;
    	}
    
    	ISelection selection = fViewer.getSelection();
    	Object obj = ((IStructuredSelection) selection).getFirstElement();
    	if(obj!=null && obj instanceof JavacASTNode) {
    		JavacASTNode node = (JavacASTNode)obj;
    		EditorUtility.selectInEditor(fEditor, node.getStartpos(),node.getEndpos()-node.getStartpos());
    	}
    }
    

    调用EditorUtility工具类中的selectInEditor()方法,这个方法的定义如下:

    public static void selectInEditor(ITextEditor editor, int offset, int length) {
    	IEditorPart active = getActiveEditor();
    	if (active != editor) {
    		editor.getSite().getPage().activate(editor);
    	}
    	editor.selectAndReveal(offset, length);
    }
    

    调用IEditorPart的selectAndReveal()方法,同时传递开始位置和选中的长度就完成了这个功能。  

    3、发布插件

    发布Eclipse插件非常简单,网上相关的资料也非常多,这里就不做过多介绍。作者将打包好的插件放到了JavacASTViewer项目的zip目录下,大家可以下载下来以Install的方式安装。

    4、编写README.md

    编写文档介绍项目以及使用方法,具体内容如下:

    # JavacTreeViewer
    ## 1、项目简介
    以插件的形式直观的查看基于OpenJDK的编译器Javac的抽象语法树。
    ## 2、项目描述
    可以在Eclipse中以插件的方式安装、查看Javac的抽象语法树结构。类似于Eclipse AST View插件。Eclipse AST View插件显示的是ECJ编译器的抽象语法树,而JavacASTViewer显示的是基于OpenJDK的编译器Javac的抽象语法树结构。
    ## 3、用法
    可直接下载项目源代码,自己导出Eclipse插件包,或者下载本项目中zip目录下作者打包好的插件包,以Install的方式安装。
    目前只测试了Eclipse IDE for RCP and RAP Developers 4.14.0版本。
    

     

    这样一个插件就开发完了,不过后续还需要增加更多的测试用例,以及完善更多的功能。

     



  • 相关阅读:
    SpringMVC文件上传
    JavaSE——链表集合
    java 线程Thread 技术--1.5Lock 与condition 演示生产者与消费模式
    XML 解析技术
    java 线程Thread 技术--方法演示生产与消费模式
    java 线程Thread 技术--线程状态与同步问题
    java 线程Thread 技术--创建线程的方式
    java 线程Thread 技术--线程创建源码解释
    JDK1.5 Excutor 与ThreadFactory
    springboot学习记录
  • 原文地址:https://www.cnblogs.com/extjs4/p/12381467.html
Copyright © 2020-2023  润新知