Java 模型
Java 模型是用来对与创建、编辑和构建 Java 程序相关联的对象进行建模的一些类。Java 模型类是在 org.eclipse.jdt.core 中定义的。这些类实现资源的特定于 Java 的行为,并进一步将 Java 资源分解成模型元素。
Java 元素
包 org.eclipse.jdt.core 定义了用来对组成 Java 程序的元素建模的一些类。JDT 使用内存中的对象模型来表示 Java 程序的结构。此结构是从项目的类路径派生的。模型是分层的。可以将程序的元素分解成子元素。
处理 Java 元素与处理资源对象相似。当使用 Java 元素时,实际上是在使用某些底层的模型对象的句柄。必须使用 exists() 协议来确定元素是否真正存在于工作空间中。
元素 IJavaModel IJavaProject IPackageFragmentRoot 描述 表示根 Java 元素,对应于工作空间。所有具有 Java 性质的项目的父代。它还允许访问不具有 java 性质的项目。 表示工作空间中的 Java 项目。(IJavaModel 的子代) 表示一组包段,并将各段映射至底层资源,它可以是文件夹、JAR 或 ZIP 文件。(IJavaProject 的子代) 表示工作空间中的一个部分,对应于整个包或者包的一部分。(IPackageFragmentRoot 的子代) 表示 Java 源(.java)文件。(IPackageFragment 的子代) 表示编译单元中的软件包声明。(ICompilationUnit 的子代) 表示编译单元中的包导入声明的集合。(ICompilationUnit 的子代) 表示单个包导入声明。(IImportContainer 的子代) 表示编译单元内的源类型,或者是类文件中的二进制类型。 表示类型中的字段。(IType 的子代) 表示类型中的方法或构造函数。(IType 的子代) 表示类型中的静态或实例初始化程序。(IType 的子代) 表示已编译(二进制)类型。(IPackageFragment 的子代) IPackageFragment ICompilationUnit IPackageDeclaration IImportContainer IImportDeclaration IType IField IMethod IInitializer IClassFile 所有 Java 元素都支持 IJavaElement 接口。
某些元素显示在“包”视图中。这些元素实现 IOpenable 接口,原因是必须在打开它们之后才能浏览它们。下图说明这些元素在“包”视图中是如何表示的。
实现 IOpenable 的 Java 元素基本上是根据在底层资源文件中找到的信息创建的。相同的元素是在资源导航器视图中按类别表示的。
其它元素对应于组成 Java 编译单元的项。下图显示 Java 编译单元和内容大纲窗口(它显示编译单元中的源元素)。
因为这些元素可以提供相应的源代码,所以它们实现了 ISourceReference 接口。(当在内容大纲窗口中选择了这些元素时,就会在 Java 编辑器中显示它们相应的源代码)。
Java 元素及其资源
许多 Java 元素对应于工作空间中的一般资源。当您想根据一般资源来创建 Java 元素时,最好是从类 JavaCore 开始。以下代码段说明如何从 Java 元素的相应资源中获取 Java 元素。
private void createJavaElementsFrom(IProject myProject, IFolder myFolder, IFile myFile) {
IJavaProject myJavaProject = JavaCore.create(myProject); if (myJavaProject == null)
// the project is not configured for Java (has no Java nature) return;
// get a package fragment or package fragment root
IJavaElement myPackageFragment = JavaCore.create(myFolder);
// get a .java (compilation unit), .class (class file), or // .jar (package fragment root)
IJavaElement myJavaFile = JavaCore.create(myFile);
}
一旦有了 Java 元素,就可以使用 JDT API 来遍历和查询模型。还可以查询包含在 Java 元素内的非 Java 资源。
private void createJavaElementsFrom(IProject myProject, IFolder myFolder, IFile myFile) { ...
// get the non Java resources contained in my project.
Object[] nonJavaChildren = myJavaProject.getNonJavaResources(); ...
Java 项目
当根据简单项目来创建 Java 项目时,JavaCore 将检查项目是否是用 Java 性质来配置的。JDT 插件使用项目性质来将项目指定为具有 Java 行为。当“新建 Java 项目”向导创建项目时,将为项目指定此性质(org.eclipse.jdt.core.JavaCore#NATURE_ID)。如果未对项目配置 Java 性质,则在要求创建项目时,JavaCore 将返回 null。
JavaCore 还用来维护 Java 类路径,包括用于查找源代码和库的位置以及用于生成输出二进制(.class)文件的位置。
什么是 Java 项目的唯一特征?Java 项目将它们的类路径记录在“.classpath”文件中,并将 Java 增量项目构建器添加到项目的构建规范中。否则,它们只是常规项目,并且可以由插件利用其它性质(和其它增量构建器)来配置。对于那些想要利用除了它们自己的行为之外的 Java 行为来配置项目的插件,它们通常使用 NewJavaProjectWizardPage 来为项目指定除了它们自己的定制性质或行为之外的 Java 性质。
IJavaModel 可以认为是工作空间中具有 Java 项目性质的所有项目的父代(因此,可以当作 IJavaProject)。
处理 Java 代码
插件可以使用 JDT API 来创建类或接口、将方法添加到现有类型中或者改变方法的类型。 改变 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 方法将工作副本保存至磁盘(用来替换原始的编译单元)。
例如,下列代码段使用定制工作副本拥有者在编译单元上创建工作副本。该代码段将修改缓冲区、协调更改、将更改提交到磁盘,最后废弃工作副本。
// Get original compilation unit ICompilationUnit originalUnit = ...;
// Get working copy owner WorkingCopyOwner owner = ...;
// Create working copy
ICompilationUnit workingCopy = originalUnit.getWorkingCopy(owner, null, null);
// Modify buffer and reconcile
IBuffer buffer = ((IOpenable)workingCopy).getBuffer(); buffer.append(\
workingCopy.reconcile(NO_AST, false, null, null);
// Commit changes
workingCopy.commitWorkingCopy(false, null);
// Destroy working copy
workingCopy.discardWorkingCopy();
工作副本还可以供若干使用工作副本拥有者的客户机共享。稍后可以使用 findWorkingCopy 方法来检索工作副本。因此,对于原始编译单元和工作副本拥有者,共享工作副本很重要。 下列内容说明了客户机 1 如何创建共享的工作副本、客户机 2 如何检索此工作副本、客户机 1 如何废弃此工作副本以及尝试检索共享的工作副本的客户机 2 如何注意到该工作副本不再存在:
// Client 1 & 2: Get original compilation unit ICompilationUnit originalUnit = ...;
// Client 1 & 2: Get working copy owner WorkingCopyOwner owner = ...;
// Client 1: Create shared working copy
ICompilationUnit workingCopyForClient1 = originalUnit.getWorkingCopy(owner, null, null);
// Client 2: Retrieve shared working copy ICompilationUnit workingCopyForClient2 = originalUnit.findWorkingCopy(owner);
// This is the same working copy
assert workingCopyForClient1 == workingCopyForClient2;
// Client 1: Discard shared working copy workingCopyForClient1.discardWorkingCopy();
// Client 2: Attempt to retrieve shared working copy and find out it's null workingCopyForClient2 = originalUnit.findWorkingCopy(owner); assert workingCopyForClient2 == null;
使用 DOM/AST API 来修改代码
有三种方法来创建 CompilationUnit。第一种方法是使用 ASTParser。第二种方法是使用 ICompilationUnit#reconcile(...)。第三种方法是从头开始对 AST(抽象语法树)使用工厂方法。
从现有的源代码创建 AST
必须使用 ASTParser.newParser(int) 来创建 ASTParser 的实例。
将使用下列其中一个方法将源代码提供给 ASTParser:
? ? ?
结果是每个节点都有一个具有正确源位置的 AST。在使用 setResolveBindings(boolean) 创建树之前,必须请求绑定的解决方案。解决绑定是一个成本很高的操作,仅当需要时才执行。一旦修改了树,就会丢失所有位置和绑定。
setSource(char[]):从源代码创建 AST setSource(IClassFile):从类文件创建 AST
setSource(ICompilationUnit):从编译单元创建 AST
然后通过调用 createAST(IProgressMonitor) 来创建 AST。
通过协调工作副本来创建 AST
如果工作副本不一致(已修改),则可以通过调用方法 reconcile(int, boolean, org.eclipse.jdt.core.WorkingCopyOwner, org.eclipse.core.runtime.IProgressMonitor) 来创建 AST。要请求 AST 创建,使用 AST.JLS2 作为第一个参数来调用 reconcile(...) 方法。
仅当问题请求程序是活动或者当强制执行问题检测时,才会计算其绑定。解决绑定是一个成本很高的操作,仅当需要时才执行。一旦修改了树,就会丢失所有位置和绑定。
从头开始
可以通过对 AST 使用工厂方法来从头创建 CompilationUnit。这些方法名以 new... 开头。以下是创建 HelloWorld 类的一个示例。 第一个代码段是生成的输出:
package example;
import java.util.*; public class HelloWorld {
public static void main(String[] args) {
}
}
System.out.println(\
下列代码段是生成输出的相应代码。
AST ast = new AST();
CompilationUnit unit = ast.newCompilationUnit(); PackageDeclaration packageDeclaration =
ast.newPackageDeclaration();
OID));
packageDeclaration.setName(ast.newSimpleName(\ unit.setPackage(packageDeclaration); ImportDeclaration importDeclaration = QualifiedName name =
ast.newQualifiedName(
ast.newSimpleName(\ast.newSimpleName(\
ast.newImportDeclaration();
importDeclaration.setName(name); importDeclaration.setOnDemand(true); unit.imports().add(importDeclaration);
TypeDeclaration type = ast.newTypeDeclaration(); type.setInterface(false);
type.setModifiers(Modifier.PUBLIC);
type.setName(ast.newSimpleName(\MethodDeclaration methodDeclaration = methodDeclaration.setConstructor(false);
methodDeclaration.setModifiers(Modifier.PUBLIC | methodDeclaration.setName(ast.newSimpleName(\
ast.newMethodDeclaration();
Modifier.STATIC);
methodDeclaration.setReturnType(ast.newPrimitiveType(PrimitiveType.V
SingleVariableDeclaration variableDeclaration = variableDeclaration.setModifiers(Modifier.NONE);
ast.newSingleVariableDeclaration();
variableDeclaration.setType(ast.newArrayType(ast.newSimpleType(ast.n
variableDeclaration.setName(ast.newSimpleName(\ methodDeclaration.parameters().add(variableDeclaration); org.eclipse.jdt.core.dom.Block block = ast.newBlock(); MethodInvocation methodInvocation = name =
ast.newQualifiedName(
ast.newSimpleName(\ast.newSimpleName(\
ewSimpleName(\
ast.newMethodInvocation();
methodInvocation.setExpression(name);
methodInvocation.setName(ast.newSimpleName(\InfixExpression infixExpression = ast.newInfixExpression(); infixExpression.setOperator(InfixExpression.Operator.PLUS); StringLiteral literal = ast.newStringLiteral();
literal.setLiteralValue(\
infixExpression.setLeftOperand(literal); literal = ast.newStringLiteral(); literal.setLiteralValue(\
infixExpression.setRightOperand(literal);
methodInvocation.arguments().add(infixExpression); ExpressionStatement expressionStatement = block.statements().add(expressionStatement); methodDeclaration.setBody(block);
type.bodyDeclarations().add(methodDeclaration); unit.types().add(type);
ast.newExpressionStatement(methodInvocation);
检索额外的位置
DOM/AST 节点只包含一对位置(起始位置和节点的长度)。这并不总是够用。要检索中间位置,应使用 IScanner API。例如,我们具有想要对其了解 instanceof 运算符的位置的 InstanceofExpression。可以编写以下方法来实现此目的:
{
if (expression instanceof InstanceofExpression) {
IScanner scanner = ToolFactory.createScanner(false, scanner.setSource(source);
int start = expression.getStartPosition(); int end = start + expression.getLength(); scanner.resetTo(start, end); int token; try {
while ((token = scanner.getNextToken()) !=
switch(token) {
case
return new int[]
private int[] getOperatorPosition(Expression expression, char[] source)
false, false, false);
ITerminalSymbols.TokenNameEOF) {
ITerminalSymbols.TokenNameinstanceof: {scanner.getCurrentTokenStartPosition(), scanner.getCurrentTokenEndPosition()};
}
}
}
}
}
} catch (InvalidInputException e) {
return null;
IScanner 用来将输入源代码分成多个标记。每个标记都具有特定值,这些特定值是在 ITerminalSymbols 接口中定义的。迭代和检索正确的标记是非常简单的。如果想要在 SuperMethodInvocation 中查找 super 关键字的位置,则还建议您使用扫描程序。
源代码修改
某些源代码修改不是通过 Java 元素 API 来提供的。编辑源代码(例如,更改现有元素的源代码)的较常见方法是使用编译单元的原始源代码和 DOM/AST 的重写 API 来完成的。 要执行 DOM/AST 重写,存在两组 API:描述性重写和修改重写。
描述性 API 不修改 AST,而是使用 ASTRewrite API 来生成修改描述。AST 重写程序收集对节点修改的描述并将这些描述转换为可以适用于原始源代码的文本编辑。
// creation of a Document
ICompilationUnit cu = ... ; // content is \ String source = cu.getBuffer().getContents(); Document document= new Document(source);
// creation of DOM/AST from a ICompilationUnit ASTParser parser = ASTParser.newParser(AST.JLS2); parser.setSource(cu);
CompilationUnit astRoot = (CompilationUnit) parser.createAST(null);
// creation of ASTRewrite
ASTRewrite rewrite = new ASTRewrite(astRoot.getAST());
// description of the change
SimpleName oldName = ((TypeDeclaration)astRoot.types().get(0)).getName(); SimpleName newName = astRoot.getAST().newSimpleName(\ rewrite.replace(oldName, newName, null);
// computation of the text edits
TextEdit edits = rewrite.rewriteAST(document, cu.getJavaProject().getOptions(true));
// computation of the new source code edits.apply(document);
String newSource = document.get();
// update of the compilation unit cu.getBuffer().setContents(newSource);
修改 API 允许直接修改 AST:
?
请求记录修改(CompilationUnit.recordModifications())。
? ?
对 AST 节点执行修改。
当完成修改时,生成可以适用于原始源代码的文本编辑(CompilationUnit.rewrite(...))。
// creation of a Document
ICompilationUnit cu = ... ; // content is \ String source = cu.getBuffer().getContents(); Document document= new Document(source);
// creation of DOM/AST from a ICompilationUnit ASTParser parser = ASTParser.newParser(AST.JLS2); parser.setSource(cu);
CompilationUnit astRoot = (CompilationUnit) parser.createAST(null);
// start record of the modifications astRoot.recordModifications();
// modify the AST
TypeDeclaration typeDeclaration = (TypeDeclaration)astRoot.types().get(0) SimpleName newName = astRoot.getAST().newSimpleName(\ typeDeclaration.setName(newName);
// computation of the text edits
TextEdit edits = astRoot.rewrite(document, cu.getJavaProject().getOptions(true));
// computation of the new source code edits.apply(document);
String newSource = document.get();
// update of the compilation unit cu.getBuffer().setContents(newSource);
对 Java 元素的更改作出响应
如果事后插件需要知道对 Java 元素所作的更改,则可以向 JavaCore 注册 Java IElementChangedListener。
JavaCore.addElementChangedListener(new MyJavaElementChangeReporter());
还可以更具体,并使用 addElementChangedListener(IElementChangedListener, int) 来指定您感兴趣的事件的类型。
例如,如果您只想在协调操作期间侦听事件:
JavaCore.addElementChangedListener(new MyJavaElementChangeReporter(), ElementChangedEvent.POST_RECONCILE);
JavaCore 支持二种类型的事件:
? ?
POST_CHANGE:在相应的 POST_CHANGE 资源更改通知期间,将通知此事件类型的侦听器。
POST_RECONCILE:在结束对工作副本的协调操作时,将通知此事件类型的侦听器(请参阅 ICompilationUnit.reconcile(int, boolean, WorkingCopyOwner, IProgressMonitor))。
Java 元素更改侦听器在概念上类似于资源更改侦听器(在跟踪资源更改中进行了描述)。下列代码段实现 Java 元素更改指示器,它将元素变化打印到系统控制台中。
public class MyJavaElementChangeReporter implements IElementChangedListener { public void elementChanged(ElementChangedEvent event) { IJavaElementDelta delta= event.getDelta(); if (delta != null) {
System.out.println(\ System.out.print(delta); } } }
IJavaElementDelta 包括已更改的元素和描述所发生的更改类型的标志。在大多数时候,delta 树的根位于“Java 模型”级别。然后,客户机必须使用 getAffectedChildren 来浏览此 delta 以了解已经更改了哪些项目。
以下示例方法遍历 delta,并打印已添加、已除去和已更改的元素:
void traverseAndPrint(IJavaElementDelta delta) { switch (delta.getKind()) { case IJavaElementDelta.ADDED:
System.out.println(delta.getElement() + \ break;
case IJavaElementDelta.REMOVED:
System.out.println(delta.getElement() + \ break;
case IJavaElementDelta.CHANGED:
System.out.println(delta.getElement() + \ if ((delta.getFlags() & IJavaElementDelta.F_CHILDREN) != 0) { System.out.println(\ }
if ((delta.getFlags() & IJavaElementDelta.F_CONTENT) != 0) { System.out.println(\ }
/* Others flags can also be checked */ break; }
IJavaElementDelta[] children = delta.getAffectedChildren(); for (int i = 0; i < children.length; i++) { traverseAndPrint(children[i]);
} }
有多种操作可以触发 Java 元素更改通知。以下是一些示例:
? ? ? ? ? ? ?
创建资源,例如 IPackageFragment.createCompilationUnit(delta 指示编译单元的添加)
修改资源,例如 ICompilationUnit.createType(delta 指示编译单元已更改,并且已添加类型来作为此编译单元的子代)
修改项目的类路径,例如 IJavaProject.setRawClasspath(delta 指示包段根目录已添加至类路径、已从类路径中除去或在类路径上已重新排序)
修改类路径变量值,例如 JavaCore.setClasspathVariable(delta 还指示包段根目录已经受到影响)
更改 .jar 文件的源连接,例如 IPackageFragmentRoot.attachSource(delta 指示源已拆离,然后连接)
使工作副本与它的缓冲区协调,例如 IWorkingCopy.reconcile
修改以“.java”结尾并且在项目的类路径上的 IFile,例如,使用 IFile.setContents(delta 指示编译单元已更改,但是未提供更详细的信息,原因是这不是通过“Java 模型”操作来完成的)
与 IResourceDelta 相似,可以使用 IWorkspaceRunnable 来对 Java 元素 delta 进行批处理。立即就会合并和报告在 IWorkspaceRunnable 中运行的若干“Java 模型”操作产生的 delta。
JavaCore 提供了 run 方法来对 Java 元素更改进行批处理。 例如,以下代码段将触发 2 个 Java 元素更改事件:
// Get package
IPackageFragment pkg = ...;
// Create 2 compilation units
ICompilationUnit unitA = pkg.createCompilationUnit(\ ICompilationUnit unitB = pkg.createCompilationUnit(\
\\
而以下代码段将触发 1 个 Java 元素更改事件:
// Get package
IPackageFragment pkg = ...;
// Create 2 compilation units JavaCore.run(
new IWorkspaceRunnable() { {
ICompilationUnit unitA = pkg.createCompilationUnit(\
\
public void run(IProgressMonitor monitor) throws CoreException
ICompilationUnit unitB = pkg.createCompilationUnit(\ }
\ }, null);
设置 Java 构建路径
本节描述如何设置 Java 构建路径。构建路径是用于构建 Java 项目(IJavaProject)的类路径。
类路径只是一组用于描述可用类型的类路径条目(IClassPathEntry)。类型能够以源或二进制格式出现,并且条目在路径中的次序定义了用于在构建期间解析类型的查找次序。 Java 构建路径反映在 Java 项目元素的结构中。可以查询项目的包段根目录
(IPackageFragmentRoot)。每个类路径条目都映射至一个或多个包段根目录,每个包段根目录进一步包含一组包段。
此处对构建路径的讨论不涉及 Java 运行时路径,Java 运行时路径可以独立于构建路径进行定义。(有关对运行时类路径的讨论,请参阅运行 Java 代码。)
更改构建路径
可以通过对相应项目的 Java 元素使用 setRawClasspath 来使用程序更改项目的构建路径。以下代码设置项目资源的类路径:
IProject project = ... // get some project resource IJavaProject javaProject = JavaCore.create(project); IClasspathEntry[] newClasspath = ...;
javaProject.setRawClasspath(newClasspath, someProgressMonitor);
(注意:术语“原始”类路径用来强调尚未解析任何用来描述条目位置的变量的事实。) Java 构建路径保存在项目的文件结构中的名为“.classpath”的文件中。此文件可用来提供一种方法来通过某些源代码资源库与其它项目共享 Java 构建路径设置。需要特别指出的是,由于可能会毁坏此文件,所以不应该手工编辑它。
类路径条目
可以使用对 JavaCore 定义的工厂方法来定义类路径条目。类路径条目可以引用下列任何一项:
?
源文件夹 - 一个包含源代码编译单元的文件夹,这些源代码编译单元组织在它们的相应包目录结构下面。源文件夹用来更好地在大型项目中组织源文件,并且只能在包含项目内引用源文件夹。相应的工厂方法为 newSourceEntry。在给定的源文件夹中,期望根据每个编译单元的包语句来将每个编译单元嵌套在适当的文件夹结构中。例如,包“p1”中的编译单元“X.java”必须位于源文件夹的子文件夹“p1” 中。只要多个源文件夹不重叠,
就可以使用它们。可以对源文件夹指定它自己的输出位置,该输出位置确定应在何处放置生成的类文件。如果未指定任何输出位置,则将把类文件放置在包含项目的输出位置中(请参阅 IJavaProject.setOutputLocation)。
以下是指示项目“MyProject”的源文件夹“src”的类路径条目的示例:
IClassPathEntry srcEntry = JavaCore.newSourceEntry(new Path(\
? 二进制库 - 类文件文件夹(包含在工作空间内部)或类文件归档文件(包含在工作空间内部或外部)。归档库可以具有相连接的源归档,当请求类文件元素的源(getSource)时,就会抽取这些源归档。这些库的工厂方法是 newLibraryEntry。 以下是指示“MyProject”的类文件文件夹“lib”的类路径条目的示例:
IClassPathEntry libEntry = JavaCore.newLibraryEntry( new Path(\ null, //no source null, //no source false); //not exported
以下类路径条目具有源连接:
IClassPathEntry libEntry = JavaCore.newLibraryEntry( new Path(\
new Path(\ new Path(\ true); //exported
源归档根路径描述根在源归档中的位置。如果设置为空,则将动态地推断归档的根。 ?
先决条件项目 - 另一个 Java 项目。先决条件项目总是将它的源文件夹提供给从属项目使用。(可选)它还可以提供它的任何标记为已导出的类路径条目(请参阅支持附加布尔自变量“isExported”的工厂方法)。这意味着除了将它的源添加至其从属项之外,项目还将导出这样标记的所有类路径条目。这将允许先决条件项目更好地隐藏它们自己的结构更改。例如,给定的项目可以选择从使用源文件夹切换到导出库。完成此操作并不要求其从属项目更改它们的类路径。项目先决条件的工厂方法是 newProjectEntry。 以下类路径条目指示先决条件项目“MyFramework”。
IClassPathEntry prjEntry = JavaCore.newProjectEntry(new Path(\
? 通过使用一些类路径变量实现对项目或库的间接引用 - 可以相对于类路径变量来动态解析项目或库的位置,类路径变量是作为条目路径的第一个段指定的。条目路径的其余部分被追加至已解析的变量路径。类路径变量的工厂方法是 newVariableEntry。类路径变量对于整个工作空间来说是全局的,并且可以通过 JavaCore 方法 getClasspathVariable 和 setClasspathVariable 来处理。
可能会注册自动的类路径变量初始化程序,当启动工作空间时,通过扩展点 org.eclipse.jdt.core.classpathVariableInitializer 来调用该类路径变量初始化程序。 以下类路径条目指示一个库,该库的位置存放在变量“HOME”中。使用变量“SRC_HOME”和“SRC_ROOT”来定义源连接:
IClassPathEntry varEntry = JavaCore.newVariableEntry( new Path(\
new Path(\ new Path(\ true); //exported // no progress
JavaCore.setClasspathVariable(\new Path(\null);
? 指示类路径容器的条目 - 对一组结构化项目或库的间接引用。类路径容器用来引用一组描述复杂库结构的类路径条目。与类路径变量相似,类路径容器
(IClasspathContainer)是动态解析的。类路径容器可以由不同的项目使用,从而导致它们的路径条目对每个项目解析为不同的值。它们还提供关于它们表示的库的元信息(库的名称、种类和描述)。可以通过 JavaCore 方法 getClasspathContainer 和 setClasspathContainer 来处理类路径容器。
可能会注册一个自动的类路径容器初始化程序,当需要绑定容器时,通过扩展点 org.eclipse.jdt.core.classpathContainerInitializer 来被动地调用类路径容器初始化程序。
以下类路径条目指示系统类库容器:
IClassPathEntry varEntry = JavaCore.newContainerEntry(
new Path(\ false); //not exported
JavaCore.setClasspathContainer( new Path(\
new IJavaProject[]{ myProject }, // value for 'myProject' new IClasspathContainer[] { new IClasspathContainer() {
public IClasspathEntry[] getClasspathEntries() { return new IClasspathEntry[]{
JavaCore.newLibraryEntry(new Path(\false); }; }
public String getDescription() { return \container\
public int getKind() { return IClasspathContainer.K_SYSTEM; } public IPath getPath() { return new Path(\ } }, null);
排除模式
可以对类路径源条目指定排除模式,排除模式防止源文件夹中的特定资源在类路径中可见。对模式的使用允许过滤掉资源树的指定部分。每个排除模式路径都是相对于类路径条目的,并且使用与 Ant 相似的模式机制。只要外部模式排除内部模式,排除模式就可以用来指定嵌套的源文件夹。
关于排除模式的更多详细信息,请参阅 getExclusionPatterns()。
Java 项目 API isOnClasspath 在确定特定资源是否在类路径上之前将检查包含模式和排除模式。 注释:
? ? ? ? ? ? ? ?
排除模式比包含模式具有更高的优先顺序;换句话说,排除模式可以除去包含模式要包括的文件,反之则不行。
从构建路径排除的嵌套源文件夹可以设置为输出位置。以下是指示项目“MyProject”的源文件夹“src”的类路径条目的示例,排除的嵌套源文件夹用作输出位置:
IPath sourceFolder = new Path(\ IPath outputLocation = sourceFolder.append(\ IClassPathEntry srcEntry = JavaCore.newSourceEntry( sourceFolder, // source folder location
new Path[] { outputLocation }, // excluded nested folder outputLocation); // output location
包含模式
也可以对类路径源条目指定包含模式,这显式地将资源定义为在类路径中是可视的。当未指定包含模式时,则源条目包括以此源条目的路径为根的资源树中的所有相关文件。指定一个或多个包含模式意味着只能包含资源树的指定部分。每个指定的路径都必须是相对路径,并且将解释为相对于此源条目的路径。文件模式是区分大小写的。通过这些模式中的一个或多个模式匹配的文件将包括在相应的包段中根目录中,除非它被此条目的一个或多个排除模式排除。
请参阅 getExclusionPatterns() 以获取路径模式的语法和语义的论述。缺少任何包含模式在语义上等同于显式包含模式 **。
Java 项目 API isOnClasspath 在确定特定资源是否在类路径中之前将检查包含模式和排除模式。 示例:
? ? ?
包含模式 src/** 本身包括名为 src 的根文件夹下的所有文件。
包含模式 src/** 和 tests/** 包括名为 src 和 tests 的根文件夹下的所有文件。
包含模式 src/** 与排除模式 src/**/Foo.java 一起包括名为 src 的根文件夹下的所有文件(除了名为 Foo.java 的文件)。
类路径解析
因为类路径变量和容器允许定义动态绑定的类路径条目,所以类路径 API 对原始类路径和已解析的类路径加以区分。原始类路径是最初使用 setRawClasspath 对 Java 项目设置的类路径,并且可以通过向项目请求 getRawClasspath 来进一步查询它。可以使用
getResolvedClasspath 来查询已解析的类路径。此操作将触发对解析类路径所需的任何变量和容器的初始化。许多“Java 模型”操作都隐式地导致解析 Java 构建路径。例如,计算项目的包段根目录就需要解析构建路径。 编译 Java 代码
JDT 插件包含增量式和批处理 Java 编译器,用以根据源代码来构建 Java 的 .class 文件。编译器没有提供直接 API。在 Java 项目上,它是作为构建器来安装的。编译是使用标准平台构建机制触发的。
增量项目构建器详细描述了平台构建机制。
编译代码
可以使用构建 API 来使用程序编译项目中的 Java 源文件。
IProject myProject; IProgressMonitor
myProgressMonitor;myProject.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, myProgressMonitor);
对于 Java 项目,这将调用 Java 增量项目构建器(以及已经添加至项目的构建规范中的任何其它增量项目构建器)。生成的 .class 文件被写入到指定的输出文件夹中。附加资源文件也复制到输出文件夹中。
在完全批处理构建的情况下,输出文件夹中的所有 .class 文件都可能“已清理”,以确保找不到任何旧文件。这是使用“JDT 核心构建器选项”
(CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER)控制的。此选项的缺省值将清除输出文件夹。除非重新设置此选项,否则必须确保将所有 .class 文件(您没有这些文件的相应源文件)放置在类路径中的单独类文件文件夹中,而不是放置在输出文件夹中。
可以使用其它用于控制将哪些资源复制到输出文件夹的选项来配置增量和批处理构建器。以下样本显示如何设置资源过滤器,以便不将以“.ignore”结尾的文件和名为“META-INF”的文件夹复制到输出文件夹:
Hashtable options = JavaCore.getOptions();
options.put(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, \
JavaCore.setOptions(options);
如果文件名与所提供的模式之一相匹配,则会过滤掉这些文件名。如果整个文件夹的名称与所提供的文件夹名之一(以路径分隔符结尾)相匹配,则会过滤掉整个文件夹。
还可以将增量和批处理构建器配置为在 .classpath 文件具有错误时只生成单个错误。缺省情况下将设置此选项,它将消除大量的错误。请参阅 JDT 核心构建器选项以获取与构建器相关的选项及其缺省值的完整列表。
还可以使用 JavaCore 选项来配置编译器。例如,可以定义应当用于在编译期间发现的不同类型的问题的严重性。请参阅 JDT 核心编译器选项以获取与编译器相关的选项及其缺省值的完整列表。
当使用程序来配置构建器或编译器的选项时,应确定选项的作用域。例如,设置资源过滤器可能只适用于特定的项目。以下示例设置前面显示的那个资源过滤器,但是只设置个别的项目。
Hashtable options = myProject.getOptions(false); // get only the options set up in this project
options.put(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, \
myProject.setOptions(options);
使用 ant javac 适配器
可以通过使用 javac 适配器在 Ant 脚本内使用 Eclipse 编译器。要使用 Eclipse 编译器,只需在脚本中定义 build.compiler 属性。以下是一个简单示例。
value=\ nowarn=\ location=\
可以在 Ant javac 任务文档中找到用于 javac Ant 任务的语法。当前适配器支持 1.4.1 和 1.5.3 版本的 Javac Ant 任务。
问题确定
JDT 核心定义了一个专用标记(标记类型“org.eclipse.jdt.core.problem”)来指示编译问题。要使用程序发现由编译器检测到的问题,应该使用标准的平台标记协议。有关使用标记的概述,请参阅资源标记。
以下代码段查找编译单元中的所有 Java 问题标记。
public IMarker[] findJavaProblemMarkers(ICompilationUnit cu) throws CoreException {
IResource javaSourceFile = cu.getUnderlyingResource(); IMarker[] markers =
javaSourceFile.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); }
Java 问题标记是由 Java 项目构建器来维护的,在问题得到解决以及重新编译 Java 源代码之后,就会自动除去这些标记。
问题标识值是由 IProblem 中的其中一个常量设置的。问题标识是可靠的,但是消息是本地化的,因此可以根据缺省语言环境来更改消息。在 IProblem 中定义的常量是自我描述的。 应该定义 IProblemRequestor 的实现,以便收集在 Java 操作期间发现的问题。如果已经为创建工作副本提供了 IProblemRequestor,则可以使工作副本与问题检测相协调。为此,可以使用 reconcile 方法。以下是一个示例:
ICompilationUnit unit = ..; // get some compilation unit
// create requestor for accumulating discovered problems IProblemRequestor problemRequestor = new IProblemRequestor() { public void acceptProblem(IProblem problem) {
System.out.println(problem.getID() + \ }
public void beginReporting() {} public void endReporting() {} public boolean isActive() { active };
// use working copy to hold source with error
ICompilationUnit workingCopy = unit.getWorkingCopy(new WorkingCopyOwner() {}, problemRequestor, null);
((IOpenable)workingCopy).getBuffer().setContents(\Zork {}\
// trigger reconciliation
return true; } // will detect problems if
description.setSaveManifest(true);
description.setManifestMainClass(mainType); description.setElements(filestoExport); IJarExportRunnable runnable=
description.createJarExportRunnable(parentShell); try {
new ProgressMonitorDialog(parentShell).run(true,true, runnable); } catch (InvocationTargetException e) {
// An error has occurred while executing the operation } catch (InterruptedException e) { // operation has been canceled. } }
提供了附加 API 来创建 JarPackageData 的特定于插件的子类。这允许其它插件实现它们自己的 Jar 导出/导入向导以及将 JarPackageData 对象的内容保存到相应的 Jar 描述文件中。
JarPackageData 一旦描述了 JAR,就可以使用 JarWriter 来使用程序写该 JAR。
JDT 调试
“JDT 调试”由若干个支持运行和调试 Java 代码的插件组成。
?
org.eclipse.jdt.launching 是定义 Java 启动和运行时支持的插件。如果需要使用程序来启动 Java 虚拟机,则将需要此插件。JDT 启动与平台启动设施紧密相关,启动程序描述了该设施。
包 org.eclipse.jdt.launching 提供了用于从平台启动 Java 运行时的类。
JavaRuntime 实现了静态方法来访问已注册的 VM 和计算运行时类路径和源查找路径。一系列 VM(如 JDK)是由 IVMInstallType 类表示的。 IVMInstall 表示一个系列内的特定安装。IVMRunner 用来启动特定的 Java VM 和向调试插件注册其进程。 包 org.eclipse.jdt.launching.sourcelookup 定义了用于在文件系统中定位源代码元素的类。 ?
org.eclipse.jdt.debug 是定义 Java 调试模型的插件。如果需要使用程序来访问正在调试的程序中的对象,则将需要此插件。JDT 调试模型与平台调试模型紧密相关,平台调试模型对平台调试模型作了描述。
包 org.eclipse.jdt.debug.core 支持基于 JDI/JDWP 的可用于控制处于调试状态的 Java 程序的 Java 调试模型。
包 org.eclipse.jdt.debug.eval 提供了用于对 Java 表达式求值和报告结果的基础结构。 ?
org.eclipse.jdt.debug.ui 是定义 Java 调试用户界面扩展的插件。大多数调试器 API 是由调试模型表示和调试用户界面实用程序类中描述的平台调试器基础结构提供的。Java 调试用户界面 API 主要用于访问提示源定位器和 Java 启动配置选项卡。 包 org.eclipse.jdt.debug.ui.launchConfigurations 定义了本地和远程 Java 应用程序的启动配置选项卡。
包 org.eclipse.jdt.debug.ui 实现了一个简单的用户界面,该用户界面通过提示用户来定位源代码。
运行 Java 程序
“JDT 调试”组件包含使用用户当前为 Java 项目配置的 VM 安装来启动 Java 程序的设施。
启动已编译的 Java 程序
可以通过获取 Java 项目的适当 IVMRunner,并按名称来运行该类,从而运行在 Java 项目中已编译的 Java 程序。下列代码段说明如何启动 myJavaProject 中的类 MyClass。
IVMInstall vmInstall = JavaRuntime.getVMInstall(myJavaProject); if (vmInstall == null)
vmInstall = JavaRuntime.getDefaultVMInstall(); if (vmInstall != null) {
IVMRunner vmRunner = vmInstall.getVMRunner(ILaunchManager.RUN_MODE); if (vmRunner != null) { String[] classPath = null; try {
classPath =
JavaRuntime.computeDefaultRuntimeClassPath(myJavaProject); } catch (CoreException e) { } if (classPath != null) {
VMRunnerConfiguration vmConfig =
new VMRunnerConfiguration(\
ILaunch launch = new Launch(null, ILaunchManager.RUN_MODE, null); vmRunner.run(vmConfig, launch, null); } } }
启动 Java 程序的另一种方法是创建 Java 应用程序启动配置,然后启动它。以下代码段说明可以如何使用简单启动配置来启动 myJavaProject 内的类 MyClass。缺省情况下,获得的正在运行应用程序使用与 myJavaProject 相关联的 JRE 和类路径。
ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager(); ILaunchConfigurationType type =
manager.getLaunchConfigurationType(IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION);
ILaunchConfigurationWorkingCopy wc = type.newInstance(null, \ wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, \
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, \
ILaunchConfiguration config = wc.doSave(); config.launch(ILaunchManager.RUN_MODE, null);
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库JDT应用开发相关资料在线全文阅读。
相关推荐: