java的模型,Java项目模型

Java项目模型

在上节中我们讲解的是Eclipse的资源模型,讲解到的项目也是普通的Eclipse项目,Eclipse的项目有很多种,包括Java项目、C++项目、C#项目等,每种项目都有自己的特点。我们最常接触到的项目就是Java项目,因此我们重点来讲解一下Java项目模型。

Java模型是用来对与创建、编辑和构建Java程序相关联的对象进行建模的一些类。Java模型类是在org.eclipse.jdt.core中定义的。这些类实现资源的特定于Java的行为,并进一步将Java资源分解成模型元素。

3.7.1  类结构

Java模型的继承结构图如图3.13所示。

p_w_picpath044.jpg

图3.13  Java模型的继承结构图

IJavaElement的子类接口还有IMethod、IType等,在这里没有全部列出。Java模型中的类结构比较简单,级次也非常少。

下面介绍一下各个接口的主要方法。

(1)   IJavaElement

l

exists:判断元素是否存在。处理Java元素与处理资源对象相似。当使用Java元素时,实际上是在使用某些底层的模型对象的句柄。必须使用exists()来确定元素是否真正存在于工作空间中。

l   getElementName:返回元素的名称。

l   getJavaModel:返回其对应的JavaModel,返回值类型是IJavaModel。

l   getJavaProject:返回元素对应的Java工程,返回值类型是IJavaProject。

l   getParent:返回父元素,返回值类型是IJavaElement。

l   getResource:返回元素对应的资源,返回值类型是IResource。

(2)   IClassFile

此接口代表编译后的class二进制文件。

l   isClass:判断是否是Java类。

l   isInterface:判断是否是接口。

(3)   ICompilationUnit

此接口代表Java源文件。

l   getAllTypes:返回此文件中定义的所有类型,返回值类型是IType[]。一个Java文件中可以定义多个类型。

l   getPrimary:返回主类型,返回值类型是ICompilationUnit。

(4)   IJavaModel

此接口表示根 Java 元素,对应于工作空间。是所有具有 Java

性质的项目的父类。它对于Java项目的作用和IWorkspaceRoot对于IProject的作用相似。

l   contains:判断是否存在指定的资源。

l   getJavaProject:返回指定名字的Java项目,返回值类型是IJavaProject。

l   getJavaProjects:返回所有的Java项目,返回值类型是IJavaProject[]。

l   getWorkspace:返回所在的工作空间。

(5)   IJavaProject

此接口表示Java项目。

l   IJavaElement findElement(IPath path):返回项目的path路径下的Java元素。

l   IPackageFragment findPackageFragment(IPath

path):返回项目的path路径下的IPackageFragment。

l   IPackageFragmentRoot findPackageFragmentRoot(IPath

path):返回项目的path路径下的IPackageFragmentRoot。

l   findType:根据一个全名取得此元素的类型,此类有数个重载方法,返回值类型为IType。

l

getAllPackageFragmentRoots:返回所有的IPackageFragmentRoot,返回值类型是IPackageFragmentRoot[]。

l   getOutputLocation:返回输出路径,返回值类型是IPath。

l   getRequiredProjectNames:返回依赖项目,返回值类型是字符串数组。

l   setOutputLocation:设定输出路径。

(6)   IPackageFragment

此接口表示整个包或者包的一部分。

l

createCompilationUnit:创建一个ICompilationUnit,返回值类型是ICompilationUnit。

l   getClassFile:返回指定名称对应的IClassFile,返回值类型是IClassFile。

l   getClassFiles:返回所有的IClassFile,返回值类型是IClassFile[]。

l

getCompilationUnit:返回指定名称对应的ICompilationUnit,返回值类型是ICompilationUnit。

l

getCompilationUnits:返回所有ICompilationUnit,返回值类型是ICompilationUnit[]。

l   getKind:判断此包是源码包还是普通包,返回值是int型,如等于IPackage-

FragmentRoot.K_SOURCE则是源文件包,如等于IPackageFragmentRoot.K_BINARY则为普通包。

l   hasSubpackages:是否有子包。

(7)   IPackageFragmentRoot

此接口表示一组包段,并将各段映射至底层资源,它可以是文件夹、JAR或ZIP文件。

l   createPackageFragment:创建一个IPackageFragment,返回值类型是IPackage-

Fragment。

l

getKind:此包段是源码包段还是二进制包段,返回值类型是int,如果等于IPackageFragmentRoot.K_SOURCE则是源文件包段,如果等于IPackageFragment-

Root.K_BINARY则为二进制包段。

l   getPackageFragment:根据包名返回对应的IPackageFragment。

3.7.2  常用工具类

(1)

JavaCore(定义在org.eclipse.jdt.core包下)

JavaCore从Plugin继承,它是JDT插件的生命周期管理器。不过对于第三方插件开发人员来说,它的重要性更多地体现在它提供的一些工具类方法中。

l   IJavaElement create(IFile file):从文件创建对应的Java元素。

l   IJavaElement create(IFolder folder):从文件夹创建对应的Java元素。

l   IJavaProject create(IProject

project):得到IProject对应的IJavaProject。

l   IJavaElement create(IResource resource):从资源创建对应的Java元素。

l   IJavaModel create(IWorkspaceRoot

root):从工作空间根目录得到对应的IJavaModel。

l   IClassFile createClassFileFrom(IFile

file):从文件创建对应的IClassFile。

l   ICompilationUnit createCompilationUnitFrom(IFile

file):从文件创建对应的ICompilationUnit。

(2)

JavaUI(定义在org.eclipse.jdt.ui包下)

JavaUI中定义了常用的Java插件界面相关的方法。

l

createPackageDialog:创建一个包选择对话框,返回值是SelectionDialog。

l

createTypeDialog:创建一个类型选择对话框,返回值是SelectionDialog。

l   IEditorPart openInEditor(IJavaElement

element):用编辑器打开指定的Java元素并返回编辑器实例。

l   revealInEditor(IEditorPart part,

IJavaElement element):在编辑器中定位元素element。

3.7.3  常用技巧

插件开发中经常会碰到一些常用的技巧,掌握这些技巧可以极大地提高插件的开发效率,并且可以减小插件的体积。下面列出一些常见的技巧。

(1)

由一个普通项目得到Java项目

Java项目是一种特殊的项目,需要注意的是IJavaProject并不是从IProject继承的。不能将一个IProject对象强制转换成一个IJavaProject对象,也不能把一个IJavaProject实例赋值给IProject变量。

l   由IProject项目得到Java项目的方式:

IJavaProject

javaPoject = JavaCore.create(IProject);

l   由IJavaProject得到IProject的方式:

调用IJavaProject的IProject

getProject();

(2)

得到工作空间中的所有Java项目

我们可以首先得到工作空间中的所有项目,然后逐个进行转换。不过这不免麻烦了一些,下面介绍更好的方式。IJavaModel是所有Java项目的根,通过它就可以得到所有的Java项目:

IWorkspaceRoot root =

ResourcesPlugin.getWorkspace().getRoot();

IJavaModel jModel =

JavaCore.create(root);

IJavaProject jProject[] =

jModel.getJavaProjects();

(3)

打开Java编辑器并显示Java编译单元的特定成员

代码如下:

void showMethod(IMember member) {

ICompilationUnit cu =

member.getCompilationUnit();

IEditorPart javaEditor =

JavaUI.openInEditor(cu);

JavaUI.revealInEditor(javaEditor,

member);

}

(4)

在工程下创建一个com.cownew包,并创建一个Hello.java文件

代码如下:

IPackageFragmentRoot pkroot = JavaProject

.getPackageFragmentRoot(JavaProject.getResource());

IPackageFragment pkg = pkroot.createPackageFragment("com.cownew",

true,

new NullProgressMonitor());

pkg.createCompilationUnit("Hello.java", "package com.cownew;",

true,

new NullProgressMonitor());

(5)   打开【打开类型】对话框

以下代码段使用 JavaUI

类来打开【打开类型】对话框:

SelectionDialog dialog = JavaUI.createTypeDialog(parent,

new ProgressMonitorDialog(parent),

SearchEngine.createWorkspaceScope(),

IJavaElementSearchConstants.CONSIDER_ALL_TYPES,

false);

dialog.setTitle("打开类型");

dialog.setMessage("请选择要打开的类型");

if (dialog.open() == IDialogConstants.CANCEL_ID)

return null;

Object[] types = dialog.getResult();

if (types == null || types.length == 0)

return null;

System.out.println(types[0]);

用类似方法还可以创建【打开包】和【打开主要类型】对话框。

(6)   打包指定的文件

我们写一些工具的时候也许需要把文件打成jar包,然后进行发布到应用服务器等操作,调用JDT提供的类可简化这个操作(用到的打Jar包的类都在org.eclipse.ui.jarpackager下):

void exportToJar(IType mainType, IFile[] filestoExport) {

JarPackageData description= new

JarPackageData();

IPath location= new

Path("C:/cownew.jar");

description.setJarLocation(location);

description.setSaveManifest(true);

description.setManifestMainClass(mainType);

description.setElements(filestoExport);

IJarExportRunnable runnable=

description.createJarExportRunnable(parentShell);

new

ProgressMonitorDialog(parentShell).run(true,true, runnable);

}

参数mainType表示Jar包的main类,filestoExport为要打包的文件。

(7)   自动设置Java项目的构建路径

有一些插件会将需要的jar包自动设置到构建路径上,比如使用WTP的新建向导新建web项目的时候就会把web开发需要的jar包自动放入项目的构建路径,使用PDE的“将项目转换为插件项目”功能后项目的构建路径中就增加了插件依赖项的库。那么它们是怎么实现的呢?

Java项目的构建路径有如下几种:源文件夹、二进制库、依赖项目、类路径变量和类路径容器。

l

源文件夹:一个包含源代码编译单元的文件夹,这些源代码编译单元组织在它们的相应包目录结构下面。源文件夹用来更好地在大型项目中组织源文件,并且只能在包含项目内引用源文件夹。如图3.14所示。

l   二进制库:类文件文件夹(包含在工作空间内部)或类文件归档文件(包含在工作空间内部或外部)。

l   依赖项目:另一个 Java

项目。依赖项目总是将它的源文件夹提供给从属项目使用。(可选)它还可以提供它的任何标记为已导出的类路径条目。这意味着除了将它的源添加至其从属项之外,项目还将导出这样标记的所有类路径条目。这将允许先决条件项目更好地隐藏它们自己的结构更改。例如,给定的项目可以选择从使用源文件夹切换到导出库。完成此操作并不要求其从属项目更改它们的类路径。如图3.15和图3.16所示。

l

类路径变量:可以相对于类路径变量来动态解析项目或库的位置,类路径变量是作为条目路径的第一个段指定的。条目路径的其余部分被追加至已解析的变量路径。

l   类路径容器:对一组结构化项目或库的间接引用。类路径容器用来引用一组描述复杂库结构的类路径条目。

p_w_picpath045.jpg

图3.14

源文件夹

p_w_picpath046.jpg

图3.15

构建依赖项目

p_w_picpath047.jpg

图3.16  Jar和类文件夹依赖

每种不同的构建路径都有不同的作用:源文件夹是把源码进行构建的途径,二进制库是导入少量jar包的方式,依赖项目是供多项目分模块开发使用的,使用类路径变量可以避免二进制包的路径依赖,而类路径容器则为大量二进制库的引入提供了方便。

JDT为这些不同的构建路径提供了一个统一的接口:IClassPathEntry,只要调用IJavaProject的setRawClasspath方法就可以为项目设定构建路径。

IProject project = ... // 获取一些项目资源

IJavaProject javaProject = JavaCore.create(project);

IClasspathEntry[] newClasspath = ...;

javaProject.setRawClasspath(newClasspath, someProgressMonitor);

可以看到setRawClasspath方法需要一个IClasspathEntry数组,数组中的元素就是要设置的每一个构建路径。前面提到的JavaCore类提供了一系列的静态方法来帮助我们生成不同的IClasspathEntry,而无须关注生成的细节。下面来看不同构建路径的添加方式。

源文件夹。使用JavaCore.newSourceEntry方法。下面的代码的作用是构造项目MyProject的源文件夹src的类路径条目:

IClassPathEntry srcEntry =

JavaCore.newSourceEntry(new

Path("/MyProject/src"));

二进制库IClasspathEntry。使用JavaCore.newLibraryEntry

方法。下面的代码就是构造MyProject的类文件lib的类路径条目:

IClassPathEntry libEntry = JavaCore.newLibraryEntry(new

Path("/MyProject/lib"),null,

null,false);

以下类路径条目具有源代码连接:

IClassPathEntry libEntry = JavaCore.newLibraryEntry(

new Path("d:/lib/foo.jar"), // jar包路径

new Path("d:/lib/foo_src.zip"), //jar包的源码包的路径

new Path("src"), //源归档根路径

true);

设定关联源代码包有利于代码的跟踪调试。

依赖项目。使用JavaCore.newProjectEntry方法。下面的代码就是构造依赖项目MyFramework:

IClassPathEntry prjEntry =

JavaCore.newProjectEntry(new

Path("/MyFramework"),

true);

类路径变量。使用JavaCore.newVariableEntry方法。类路径变量对于整个工作空间来说是全局的,并且可以通过 JavaCore 方法

getClasspathVariable 和 setClasspathVariable 来处理。

可能会注册自动的类路径变量初始化方法,当启动工作空间时,通过扩展点

org.eclipse. jdt.core.classpathVariableInitializer来调用该类路径变量初始化方法。

以下类路径条目指示一个库,该库的位置存放在变量HOME中。使用变量SRC_HOME和SRC_ROOT来定义源代码连接:

IClassPathEntry

varEntry = JavaCore.newVariableEntry(

new

Path("HOME/foo.jar"), //库路径

new

Path("SRC_HOME/foo_src.zip"), //源码归档路径

new

Path("SRC_ROOT"), //源码归档根路径

true);

JavaCore.setClasspathVariable("HOME",

new Path("d:/myInstall"), null);

⑤    类路径容器。通过

JavaCore的getClasspathContainer和setClasspathContainer两个方法来处理类路径容器。

可能会注册一个自动的类路径容器初始化方法,当需要绑定容器时,通过扩展点

org.eclipse.jdt.core.classpathContainerInitializer来被动地调用类路径容器初始化方法。

以下类路径条目指示系统类库容器:

IClassPathEntry varEntry =

JavaCore.newContainerEntry(

new

Path("JDKLIB/default"),false);

JavaCore.setClasspathContainer(

new

Path("JDKLIB/default"),

new

IJavaProject[]{ myProject },

new IClasspathContainer[] {

new IClasspathContainer()

{

public

IClasspathEntry[] getClasspathEntries() {

return new

IClasspathEntry[]{

JavaCore.newLibraryEntry(

new

Path("d:/rt.jar"), null, null, false);

};

}

public String

getDescription() {

return "Basic JDK

library container"; }

public int getKind() {

return IClasspathContainer.K_SYSTEM; }

public IPath getPath()

{ return new Path("JDKLIB/basic"); }

}

},

null);

我们只要调用相应的方法创建我们的类路径条目就可以了,然后把这些条目组成的数组通过setRawClasspath方法设定到项目中。需要注意的是如果我们只把要添加的类路径条目传入

setRawClasspath方法的话,就会替换原有的项目构建路径,这常常是我们不希望的。可以调用IJavaProject的

readRawClasspath方法读取项目已有的设置,把我们要设置的构建路径添加到它的后面,然后再调用setRawClasspath方法设定新的项目构建路径。

3.7.4  设定构建路径实战

在这个例子中,将要实现一个“为项目添加

lucene支持”的功能,用户在项目上右击,选择菜单中的【为项目添加lucene支持】命令以后,插件就会把lucene的jar包和源码包复制到项目的lib目录下,并且将jar包加入构建路径。如图3.17所示为增加lucene支持前的项目结构。

p_w_picpath048.jpg

图3.17  增加lucene支持之前的项目结构

用户在项目上右击,在弹出的快捷菜单中选择【为项目添加lucene支持】命令后的项目结构如图3.18所示。

p_w_picpath049.jpg

图3.18  增加lucene支持之后的项目结构

图3.19是项目的构建路径。

p_w_picpath050.jpg

图3.19  增加的lucene包

首先新建一个插件工程,并将JDT相关的依赖项加入。然后添加一个org.eclipse.ui.popupMenus的扩展点,如果不熟悉怎么添加,可以使用插件向导中的“弹出菜单”向导。

需要注意contribution的配置,如图3.20所示。

p_w_picpath051.jpg

图3.20  contribution的配置

此插件只针对Java项目起作用,因此

objectClass中填入org.eclipse.jdt.core.IJavaProject;adaptable选择true;如果是用向导生成的那么请记住清空nameFilter。下面是核心类ActionAddLucene的实现代码:

public class ActionAddLucene implements IObjectActionDelegate

{

private static final String FILESEPARATOR =

System.getProperty("file.separator","/");

private static final String LUCENESRCJAR =

"lucene-1.4.3-src.jar";

private static final String LUCENEJAR = "lucene-1.4.3.jar";

private static final String LIB = "lib";

private static final String RESOUCELIB = "resoucelib";

private IStructuredSelection structSelection;

public ActionAddLucene()

{

super();

}

public void setActivePart(IAction action, IWorkbenchPart

targetPart)

{

}

public void run(IAction action)

{

Object selectObj = structSelection.getFirstElement();

if (selectObj instanceof IProject)

{

IProject project = (IProject) selectObj;

IJavaProject javaProject = JavaCore.create(project);

IClasspathEntry[] oldPaths =

javaProject.readRawClasspath();

IClasspathEntry luceneLibEntry =

JavaCore.newLibraryEntry(project

.getFile(LIB + FILESEPARATOR + LUCENEJAR)

.getFullPath(), project

.getFile(LIB + FILESEPARATOR +

LUCENESRCJAR).getFullPath(), null,

false);

if(classPathExists(oldPaths,luceneLibEntry))

{

return;

}

URL luceneLib =

Activator.getDefault().getBundle().getEntry(

RESOUCELIB + FILESEPARATOR + LUCENEJAR);

URL luceneSrc =

Activator.getDefault().getBundle().getEntry(

RESOUCELIB + FILESEPARATOR + LUCENESRCJAR);

IClasspathEntry[] newPaths =

new IClasspathEntry[oldPaths.length +

1];

System.arraycopy(oldPaths, 0, newPaths, 0,

oldPaths.length);

IFolder libFolder = project.getFolder(LIB);

if (!libFolder.exists())

{

try

{

libFolder.create(true, true, null);

} catch (CoreException e)

{

handleException(e);

}

}

copyURLToFile(luceneLib, project,

LIB + FILESEPARATOR + LUCENEJAR);

copyURLToFile(luceneSrc, project,

LIB + FILESEPARATOR +

LUCENESRCJAR);

newPaths[oldPaths.length] = luceneLibEntry;

try

{

javaProject.setRawClasspath(newPaths, null);

} catch (JavaModelException e)

{

handleException(e);

}

}

}

private static boolean classPathExists(IClasspathEntry[]

entrys,

IClasspathEntry entry)

{

for(int i=0,n=entrys.length;i

{

if(entrys[i].getPath().equals(entry.getPath()))

{

return true;

}

}

return false;

}

private static void handleException(Exception e)

{

Activator.getDefault().getLog().log(

new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0,

e.getMessage(), e));

}

private static void copyURLToFile(URL url, IProject project,

String destFileName)

{

InputStream inStream = null;

try

{

inStream = url.openStream();

IFile file = project.getFile(destFileName);

if (!file.exists())

{

file.create(inStream, true, null);

}

} catch (IOException e)

{

handleException(e);

} catch (CoreException e)

{

handleException(e);

} finally

{

try

{

if (inStream != null)

inStream.close();

} catch (IOException e)

{

handleException(e);

}

}

}

public void selectionChanged(IAction action, ISelection

selection)

{

structSelection = (IStructuredSelection)selection;

}

}

下面解释一下代码中的重点部分。

(1)   IClasspathEntry[] oldPaths =

javaProject.readRawClasspath();

读取项目原有的构建路径条目。

(2)   IClasspathEntry

luceneLibEntry = JavaCore.newLibraryEntry(

project.getFile(LIB + FILESEPARATOR

+ LUCENEJAR).getFullPath(),

project.getFile(LIB + FILESEPARATOR

+ LUCENESRCJAR).getFullPath(),

null,

false);

这一句构建lucene的jar包。

第1个参数是二进制jar包的位置,我们的二进制jar包的位置为项目路径下的lib/lucene-1.4.3-src.jar。

第2个参数是jar包对应的源码包的位置。

第3个参数为源码包的根路径,因为有的源码jar包的源码根路径不是jar包的根路径,比如simplejta的源码jar包的格式如图3.21所示。

p_w_picpath052.jpg

图3.21

Jar包的结构

对于这种情况就要指定第2个参数为“src”,lucene的源码包的源码根路径就是jar包的根路径,因此我们设置此参数为null。

第4个参数表示是否导出,我们设置为false。

(3)   URL luceneLib =

Activator.getDefault().getBundle().getEntry(RESOUCELIB + FILE- SEPARATOR +

LUCENEJAR);

我们把“lucene-1.4.3.jar”、

“lucene-1.4.3-src.jar”放到我们插件的“resoucelib”目录下,当用户单击【为项目添加lucene支持】的时候要把这两个文件复制到项目的lib目录下,因此需要首先读取插件路径“resoucelib”目录下的这两个jar包。

读取插件路径下的文件时我们使用插件Activator类提供的方法即可,比如如下调用:

Activator.getDefault().getBundle().getEntry("config/my.xml");

就可以读取到插件根目录下的文件“config/my.xml”,返回类型是java.net.URL。

(4)   copyURLToFile(luceneLib,

project, LIB + FILESEPARATOR + LUCENEJAR);

Activator.getDefault().getBundle().getEntry读取到的文件位置是URL类型的,我们需要把这个URL对应的文件复制到项目的lib下。下面看一下copyURLToFile的主干代码:

inStream = url.openStream();

IFile file = project.getFile(destFileName);

if (!file.exists())

{

file.create(inStream, true, null);

}

URL类有一个openStream可以打开文件的输入流,IFile也有一个接受输入流的create方法用来创建文件,因此我们只需要把url的输入流输出给IFile的create方法即可。

这里我们也可以由url得到其对应的磁盘上的路径,也可以得到IFile对应的磁盘上的路径,然后使用Java

IO来进行文件复制操作。但是这样做不仅代码数量变多了,而且由于使用的不是Eclipse的资源管理API,会带来无法自动刷新等问题,因此建议尽量使用Eclipse提供的API来完成此功能。

3.7.5  如何研读JDT代码

学习Eclipse插件开发的最好的方式就是研读

Eclipse的源码,而对插件开发者最有参考价值的就是JDT(Java Development

Tools)的代码,相信把所有的包研读一遍以后就会成为插件开发的高手了。下面是各个主要包的内容,读者可以根据需要有选择地进行研读。

l   org.eclipse.jdt.ui——提供用于在用户界面中显示 Java 元素的支持类。

l

org.eclipse.jdt.ui.actions——提供JDT的大部分菜单和按钮的相关代码,为针对Java用户界面行为的应用程序编程接口。

l   org.eclipse.jdt.ui.refactoring——重构相关的类。

l   org.eclipse.jdt.ui.search——搜索相关的类。

l   org.eclipse.jdt.ui.text——提供用于显示 Java 文本的支持类。

l   org.eclipse.jdt.ui.text.folding——Java代码编辑器的代码折叠的实现代码。

l   org.eclipse.jdt.ui.text.java——Java代码编辑器代码补全的实现代码。

l   1org.eclipse.jdt.ui.text.java.hover——Java 编辑器中显示文本悬浮的实现代码。

l   org.eclipse.jdt.ui.wizards——创建和配置Java元素的向导页。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值