zoukankan      html  css  js  c++  java
  • 第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版本。
    

     

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

     



  • 相关阅读:
    WPF DelegateCommand 出现Specified cast is not valid
    WPF DelegateCommand 出现Specified cast is not valid
    WPF DelegateCommand 出现Specified cast is not valid
    win10 sdk 是否向下兼容
    win10 sdk 是否向下兼容
    win10 sdk 是否向下兼容
    PHP extract() 函数
    PHP end() 函数
    PHP each() 函数
    PHP current() 函数
  • 原文地址:https://www.cnblogs.com/extjs4/p/12381467.html
Copyright © 2011-2022 走看看