eclipse插件开发JDT.docx
《eclipse插件开发JDT.docx》由会员分享,可在线阅读,更多相关《eclipse插件开发JDT.docx(110页珍藏版)》请在冰豆网上搜索。
![eclipse插件开发JDT.docx](https://file1.bdocx.com/fileroot1/2023-6/9/faf2fb7c-0e51-4974-ba9d-695264b8e133/faf2fb7c-0e51-4974-ba9d-695264b8e1331.gif)
eclipse插件开发JDT
JDT核心
JDT核心
JDT核心()是用于定义Java核心元素和API的插件。
在开发特定于Java的功能部件时,总是应该将此插件列示为先决条件。
JDT核心包使您能够访问Java模型对象和无外设JavaIDE基础结构。
JDT核心包包括:
?
∙-定义用于描述Java模型的类。
∙-定义编译器基础结构的API。
∙-支持可用于检查编译单元的结构直到语句级别的“抽象语法树”(AST)。
∙-支持对代码片段编辑测试窗或调试器中的代码段进行评估。
∙-支持Java“文档对象模型”(DOM),它可用于表示Java编译单元的结构。
∙-支持在工作空间的Java模型中搜索与特定描述相匹配的Java元素。
?
?
∙-提供用于处理.class文件和Java模型元素的实用程序类。
从版本3.0起,建议不要使用。
应使用来完成对编译单元的结构的处理。
Java模型
Java模型是用来对与创建、编辑和构建Java程序相关联的对象进行建模的一些类。
Java模型类是在中定义的。
这些类实现资源的特定于Java的行为,并进一步将Java资源分解成模型元素。
Java元素
包定义了用来对组成Java程序的元素建模的一些类。
JDT使用内存中的对象模型来表示Java程序的结构。
此结构是从项目的类路径派生的。
模型是分层的。
可以将程序的元素分解成子元素。
处理Java元素与处理资源对象相似。
当使用Java元素时,实际上是在使用某些底层的模型对象的句柄。
必须使用exists()协议来确定元素是否真正存在于工作空间中。
?
下表总结了不同种类的Java元素。
元素
描述
IJavaModel
表示根Java元素,对应于工作空间。
所有具有Java性质的项目的父代。
它还允许访问不具有java性质的项目。
IJavaProject
表示工作空间中的Java项目。
(IJavaModel的子代)
IPackageFragmentRoot
表示一组包段,并将各段映射至底层资源,它可以是文件夹、JAR或ZIP文件。
(IJavaProject的子代)
IPackageFragment
表示工作空间中的一个部分,对应于整个包或者包的一部分。
(IPackageFragmentRoot的子代)
ICompilationUnit
表示Java源(.java)文件。
(IPackageFragment的子代)
IPackageDeclaration
表示编译单元中的软件包声明。
(ICompilationUnit的子代)
IImportContainer
表示编译单元中的包导入声明的集合。
(ICompilationUnit的子代)
IImportDeclaration
表示单个包导入声明。
(IImportContainer的子代)
IType
表示编译单元内的源类型,或者是类文件中的二进制类型。
IField
表示类型中的字段。
(IType的子代)
IMethod
表示类型中的方法或构造函数。
(IType的子代)
IInitializer
表示类型中的静态或实例初始化方法。
(IType的子代)
IClassFile
表示已编译(二进制)类型。
(IPackageFragment的子代)
ITypeParameter
表示类型参数。
(不是任何Java元素的子元素,可使用IType.getTypeParameter(String)或IMethod.getTypeParameter(String)获得)
ILocalVariable
表示方法或初始化方法中的局部变量。
(不是任何Java元素的子元素,可使用ICodeAssist.codeSelect(int,int)获得)
所有Java元素都支持IJavaElement接口。
某些元素显示在“包”视图中。
这些元素实现IOpenable接口,原因是必须在打开它们之后才能浏览它们。
下图说明这些元素在“包”视图中是如何表示的。
实现IOpenable的Java元素基本上是根据在底层资源文件中找到的信息创建的。
相同的元素是在资源导航器视图中按类别表示的。
?
其他元素对应于组成Java编译单元的项。
下图显示Java编译单元和内容大纲窗口(它显示编译单元中的源元素)。
因为这些元素可以提供相应的源代码,所以它们实现了ISourceReference接口。
(当在内容大纲窗口中选择了这些元素时,就会在Java编辑器中显示它们相应的源代码)。
Java元素及其资源
许多Java元素对应于工作空间中的通用资源。
当您想根据通用资源来创建Java元素时,最好是从类JavaCore开始。
以下代码段说明如何从Java元素的相应资源中获取Java元素。
privatevoidcreateJavaElementsFrom(IProjectmyProject,IFoldermyFolder,IFilemyFile){
IJavaProjectmyJavaProject=JavaCore.create(myProject);
if(myJavaProject==null)
//theprojectisnotconfiguredforJava(hasnoJavanature)
return;
//getapackagefragmentorpackagefragmentroot
IJavaElementmyPackageFragment=JavaCore.create(myFolder);
//geta.java(compilationunit),.class(classfile),or
//.jar(packagefragmentroot)
IJavaElementmyJavaFile=JavaCore.create(myFile);
}
一旦有了Java元素,就可以使用JDTAPI来遍历和查询模型。
还可以查询包含在Java元素内的非Java资源。
?
?
privatevoidcreateJavaElementsFrom(IProjectmyProject,IFoldermyFolder,IFilemyFile){
...
//getthenonJavaresourcescontainedinmyproject.
Object[]nonJavaChildren=myJavaProject.getNonJavaResources();
...
Java项目
当根据简单项目来创建Java项目时,JavaCore将检查项目是否是用Java性质来配置的。
JDT插件使用项目性质来将项目指定为具有Java行为。
当“新建Java项目”向导创建项目时,将为项目指定此性质()。
如果未对项目配置Java性质,则在要求创建项目时,JavaCore将返回null。
JavaCore还用来维护Java类路径,包括用于查找源代码和库的位置以及用于生成输出二进制(.class)文件的位置。
什么是Java项目的唯一特征?
Java项目将它们的类路径记录在“.classpath”文件中,并将Java增量项目构建器添加到项目的构建规范中。
否则,它们只是常规项目,并且可以由插件利用其他性质(和其他增量构建器)来配置。
对于那些想要利用除了它们自己的行为之外的Java行为来配置项目的插件,它们通常使用NewJavaProjectWizardPage来为项目指定除了它们自己的定制性质或行为之外的Java性质。
IJavaModel可以认为是工作空间中具有Java项目性质的所有项目的父代(因此,可以当作IJavaProject)。
处理Java代码
插件可以使用JDTAPI来创建类或接口、将方法添加到现有类型中或者改变方法的类型。
改变Java对象最简单的方法就是使用Java元素API。
可以使用更常见的技术来处理Java元素的初始源代码。
使用Java元素来修改代码
生成编译单元
通过程序来生成编译单元最简单的方法是使用IPackageFragment.createCompilationUnit。
指定编译单元的名称和内容。
于是在包中创建了编译单元,并返回新的ICompilationUnit。
通常,可以通过在对应包目录的相应文件夹中创建扩展名为“.java”的文件资源来按类别创建编译单元。
使用类属资源API对于Java工具是一种旁门左道,因此,在通知类属资源更改侦听器以及JDT侦听器将Java模型更新为新编译单元之前,Java模型不会更新。
修改编译单元
大部分对Java源代码的简单修改可以使用Java元素API来完成。
例如,可以通过编译单元查询类型。
一旦您具有IType,就可以使用诸如createField、createInitializer、createMethod或createType的协议来将源代码成员添加至类型。
在这些方法中提供了源代码以及关于成员的位置的信息。
ISourceManipulation接口定义Java元素的常见源代码处理。
这包括用于重命名、移动、复制或删除类型的成员的方法。
工作副本
可以通过处理编译单元来修改代码(于是修改了底层IFile)或人们可以修改编译单元的内存副本(称为工作副本)。
工作副本是使用getWorkingCopy方法从编译单元中获取的。
(注意,要创建工作副本,编译单元不需要存在于Java模型中。
)当不再需要这种工作副本时,创建这种工作副本的人员应负责使用discardWorkingCopy方法来废弃它。
工作副本修改内存缓冲区。
getWorkingCopy()方法创建缺省缓冲区,但是客户机可以使用getWorkingCopy(WorkingCopyOwner,IProblemRequestor,IProgressMonitor)方法提供它们自己的缓冲区实现。
客户机可以直接处理此缓冲区的文本。
如果客户机直接处理文本,则它们必须经常使用reconcile(int,boolean,WorkingCopyOwner,IProgressMonitor)方法来使工作副本与缓冲区同步。
最后,可以使用commitWorkingCopy方法将工作副本保存至磁盘(用来替换原始的编译单元)。
?
?
例如,下列代码段使用定制工作副本拥有者在编译单元上创建工作副本。
该代码段将修改缓冲区、协调更改、将更改落实到磁盘,最后废弃工作副本。
//Getoriginalcompilationunit
ICompilationUnitoriginalUnit=...;
//Getworkingcopyowner
WorkingCopyOwnerowner=...;
//Createworkingcopy
ICompilationUnitworkingCopy=originalUnit.getWorkingCopy(owner,null,null);
//Modifybufferandreconcile
IBufferbuffer=((IOpenable)workingCopy).getBuffer();
buffer.append("classX{}");
workingCopy.reconcile(NO_AST,false,null,null);
//Commitchanges
workingCmitWorkingCopy(false,null);
//Destroyworkingcopy
workingCopy.discardWorkingCopy();
工作副本还可以供若干使用工作副本拥有者的客户机共享。
稍后可以使用findWorkingCopy方法来检索工作副本。
因此,对于原始编译单元和工作副本拥有者,共享工作副本很重要。
下列内容说明了客户机1如何创建共享的工作副本、客户机2如何检索此工作副本、客户机1如何废弃此工作副本以及尝试检索共享的工作副本的客户机2如何注意到该工作副本不再存在:
//Client1&2:
Getoriginalcompilationunit
ICompilationUnitoriginalUnit=...;
//Client1&2:
Getworkingcopyowner
WorkingCopyOwnerowner=...;
//Client1:
Createsharedworkingcopy
ICompilationUnitworkingCopyForClient1=originalUnit.getWorkingCopy(owner,null,null);
//Client2:
Retrievesharedworkingcopy
ICompilationUnitworkingCopyForClient2=originalUnit.findWorkingCopy(owner);
//Thisisthesameworkingcopy
assertworkingCopyForClient1==workingCopyForClient2;
//Client1:
Discardsharedworkingcopy
workingCopyForClient1.discardWorkingCopy();
//Client2:
Attempttoretrievesharedworkingcopyandfindoutit'snull
workingCopyForClient2=originalUnit.findWorkingCopy(owner);
assertworkingCopyForClient2==null;
使用DOM/ASTAPI来修改代码
有三种方法来创建CompilationUnit。
第一种方法是使用ASTParser。
第二种方法是使用ICompilationUnit#reconcile(...)。
第三种方法是从头开始对AST(抽象语法树)使用工厂方法。
从现有的源代码创建AST
必须使用ASTParser.newParser(int)来创建ASTParser的实例。
将使用下列其中一个方法将源代码提供给ASTParser:
∙setSource(char[]):
从源代码创建AST
∙setSource(IClassFile):
从类文件创建AST
∙setSource(ICompilationUnit):
从编译单元创建AST
然后通过调用createAST(IProgressMonitor)来创建AST。
结果是每个节点都有一个具有正确源位置的AST。
在使用setResolveBindings(boolean)创建树之前,必须请求绑定的解决方案。
解决绑定是一个成本很高的操作,仅当需要时才执行。
一旦修改了树,就会丢失所有位置和绑定。
通过协调工作副本来创建AST
如果工作副本不一致(已修改),则可以通过调用方法来创建AST。
要请求AST创建,使用AST.JLS2作为第一个参数来调用reconcile(...)方法。
仅当问题请求程序是活动或者当强制执行问题检测时,才会计算其绑定。
解决绑定是一个成本很高的操作,仅当需要时才执行。
一旦修改了树,就会丢失所有位置和绑定。
从头开始
可以通过对AST使用工厂方法来从头创建CompilationUnit。
这些方法名以new...开头。
以下是创建HelloWorld类的一个示例。
第一个代码段是生成的输出:
packageexample;
importjava.util.*;
publicclassHelloWorld{
publicstaticvoidmain(String[]args){
}
}
下列代码段是生成输出的相应代码。
ASTast=newAST();
CompilationUnitunit=ast.newCompilationUnit();
PackageDeclarationpackageDeclaration=ast.newPackageDeclaration();
packageDeclaration.setName(ast.newSimpleName("example"));
unit.setPackage(packageDeclaration);
ImportDeclarationimportDeclaration=ast.newImportDeclaration();
QualifiedNamename=
ast.newQualifiedName(
ast.newSimpleName("java"),
ast.newSimpleName("util"));
importDeclaration.setName(name);
importDeclaration.setOnDemand(true);
unit.imports().add(importDeclaration);
TypeDeclarationtype=ast.newTypeDeclaration();
type.setInterface(false);
type.setModifiers(Modifier.PUBLIC);
type.setName(ast.newSimpleName("HelloWorld"));
MethodDeclarationmethodDeclaration=ast.newMethodDeclaration();
methodDeclaration.setConstructor(false);
methodDeclaration.setModifiers(Modifier.PUBLIC|Modifier.STATIC);
methodDeclaration.setName(ast.newSimpleName("main"));
methodDeclaration.setReturnType(ast.newPrimitiveType(PrimitiveType.VOID));
SingleVariableDeclarationvariableDeclaration=ast.newSingleVariableDeclaration();
variableDeclaration.setModifiers(Modifier.NONE);
variableDeclaration.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("String"))));
variableDeclaration.setName(ast.newSimpleName("args"));
methodDeclaration.parameters().add(variableDeclaration);
MethodInvocationmethodInvocation=ast.newMethodInvocation();
name=
ast.newQualifiedName(
ast.newSimpleName("System"),
ast.newSimpleName("out"));
methodInvocation.setExpression(name);
methodInvocation.setName(ast.newSimpleName("println"));
InfixExpressioninfixExpression=ast.newInfixExpression();
StringLiteralliteral=ast.newStringLiteral();
literal.setLiteralValue("Hello");
infixExpression.setLeftOperand(literal);
literal=ast.newStringLiteral();
literal.setLiteralValue("world");
infixExpression.setRightOperand(literal);
methodInvocation.arguments().add(infixExpression);
ExpressionStatementexpressionStatement=ast.newExpressionStatement(methodInvocation);
block.statements().add(expressionStatement);
methodDeclaration.setBody(block);
type.bodyDeclarations().add(methodDeclaration);
unit.types().add(type);
检索额外的位置
DOM/AST节点只包含一对位置(起始位置和节点的长度)。
这并不总是够用。
要检索中间位置,应使用IScannerAPI。
例如,我们具有想要对其了解instanceof运算符的位置的InstanceofExpression。
可以编写以下方法来实现此目的:
privateint[]getOperatorPosition(Expressionexpression,char[]source){
if(expressioninstanceofInstanceofExpression){
IScannerscanner=ToolFactory.createScanner(false,false,false,false);
scanner.setSource(source);
intstart=expression.getStartPosition();
intend=start+expression.getLength();
scanner.resetTo(start,end);
inttoken;
try{
while((token