??xml version="1.0" encoding="utf-8" standalone="yes"?>
本文用eclipse的自动重构功能对一个程序实例进行重构,目的是探索Eclipse自动重构可以在多大程度上辅助重构q个q程。程序实例用《RefactoringQImproving the Design of Existing Code》一书中的例子?BR>
Eclipse的自动重构功能能够很好地支持各种E序元素的重命名Qƈ自动更新相关的引用。Eclipse能够支持Ҏ(gu)、字D在cM间移动,q自动更新引用。Eclipse较好地支持内联字Dc(din)函数的更新替换。Eclipse较好地支持抽取方法、变量等E序元素?BR>
重构的过E是一个不断尝试和探烦的过E。Eclipse的重构支持撤销和重做,q且能够预览重构l果Q这些是很实用的功能?BR>
Eclipse的重命名、抽取方法、移动、内联功能、更Ҏ(gu)法特征符{代码结构别的重构Ҏ(gu)Q是比较成熟同时也值得使用的功能。至于设计结构上的重构,eclipseq不能很好地支持。但是作者相信,自动重构的理念应该是"工具辅助下的重构工作"Qh仍然承担大部分重构工作?BR>
一、预备工?/B>
本文使用《RefactoringQImproving the Design of Existing Code》一书第一章的例子。重构前的代码及(qing)每一步重构后的代码见附g。读者最好配合《RefactoringQImproving the Design of Existing Code》一书阅L文?BR>
Eclipse使用如下版本Q?BR>
同时安装了中文语a包?BR>
二、重构第一步:(x)分解qlstatement()
目的Q?BR>
1?把statement()函数中的swich语句提炼到独立的函数amountFor()中?BR>
2?修改amountFor()参数命名
重构Ҏ(gu)Q?BR>
Extract Method
Rename Method
Ҏ(gu)Q?BR>
1、选中swich语句的代码块Q在右键菜单中选择"重构/抽取Ҏ(gu)"Q出现参数对话框。Eclipse自动分析代码块中的局部变量,扑ֈ了两个局部变量:(x)each和thisAmount。其中,each只是在代码块中被dQ但thisAmount?x)在代码块中被修攏V按照重构Extract Methodȝ出来的规则,应该把each当作抽取函数的参数、thisAmount当作抽取函数的返回倹{然而Eclipseq不做区分,直接把这两个变量当作抽取新方法的参数Q如图?BR>
我们的目的是把在抽取函数中不?x)被修改的each作ؓ(f)参数Q会(x)被修改的thisAmount作ؓ(f)q回倹{解决的办法是,?double thisAmount = 0; q行代码Udswitch语句的上面,变成q样Q?BR>
double thisAmount = 0;
switch(each.getMovie().getPriceCode()){
case Movie.REGULAR:
thisAmount += 2;
if(each.getDaysRented()>2)
thisAmount += (each.getDaysRented()-2)*1.5;
break;
case Movie.NEW_RELEASE:
thisAmount += each.getDaysRented()*3;
break;
case Movie.CHILDRENS:
thisAmount += 1.5;
if(each.getDaysRented()>3)
thisAmount += (each.getDaysRented()-3)*1.5;
break;
}
选中q段代码Q在右键菜单中选择"重构/抽取Ҏ(gu)"Qeclipseq次变得聪明点了Q如图?BR>
选择"预览"按钮预先查看重构后的l果Q符合我们最初的目的?BR>
选择"定"按钮Q重构后的代码片断如下:(x)
public String statement() {
double totalAmount = 0;
int frequentRenterPoints = 0;
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while(rentals.hasMoreElements()){
Rental each = (Rental)rentals.nextElement();
double thisAmount = amountFor(each);
frequentRenterPoints ++;
if((each.getMovie().getPriceCode())==Movie.NEW_RELEASE &&each.getDaysRented()>1)
frequentRenterPoints ++;
result += "\t" + each.getMovie().getTitle() + "\t" +String.valueOf(thisAmount) + "\n";
totalAmount += thisAmount;
}
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points";
return result;
}
/**
* @param each
* @return
*/
private double amountFor(Rental each) {
double thisAmount = 0;
switch(each.getMovie().getPriceCode()){
case Movie.REGULAR:
thisAmount += 2;
if(each.getDaysRented()>2)
thisAmount += (each.getDaysRented()-2)*1.5;
break;
case Movie.NEW_RELEASE:
thisAmount += each.getDaysRented()*3;
break;
case Movie.CHILDRENS:
thisAmount += 1.5;
if(each.getDaysRented()>3)
thisAmount += (each.getDaysRented()-3)*1.5;
break;
}
return thisAmount;
}
2、选中amountFor()的参数eachQ在右键菜单中选择"重构/重命?Q在对话框中输入新的名称QaRentalQ选择定QamountFor()中所有each的引用全部被替换成新的名U。用同样的办法修改amountFor()中的局部变量thisAmount为result。重构后的amountFor()代码如下Q?BR>
/**
* @param aRental
* @return
*/
private double amountFor(Rental aRental) {
double result = 0;
switch(aRental.getMovie().getPriceCode()){
case Movie.REGULAR:
result += 2;
if(aRental.getDaysRented()>2)
result += (aRental.getDaysRented()-2)*1.5;
break;
case Movie.NEW_RELEASE:
result += aRental.getDaysRented()*3;
break;
case Movie.CHILDRENS:
result += 1.5;
if(aRental.getDaysRented()>3)
result += (aRental.getDaysRented()-3)*1.5;
break;
}
return result;
}
三、重构第二步Q搬U?金额计算"代码
目的Q?BR>
1?函数amountFor()转移到RentalcMQƈ更名为getCharge()?BR>
2?更新q替换所有对amountFor()的引用?BR>
重构Ҏ(gu)Q?BR>
Move Method
Change Method signatrue
Inline Method
Inline Temp
Ҏ(gu)Q?BR>
1、选中函数amountFor()的定义,在右键菜单中选择"重构/Ud"Q显C参数设|对话框。把新方法名Ҏ(gu)getCharge。按?定"按钮QCustomer Class中的amountFor()函数被移动到Rental Class中,q更名ؓ(f)QgetCharge()?BR>
同时eclipse自动在Customer的amountFor()函数中添加一行对新函数的"委托"代码Q?BR>
private double amountFor(Rental aRental) {
return aRental.getCharge();
}
q行代码?x)生编译错误,原因是amountFor()的private型被传递到了新的方法中Q?BR>
/**
* @param this
* @return
*/
private double getCharge() {
…?BR> }
2、l重构!选中getCharge()Ҏ(gu)Q在右键菜单中选择"重构/更改Ҏ(gu)特征W?Q弹出参数选择对话框,把访问修饰符从privateҎ(gu)public。Eclipse的编译错误提C动消失?BR>
3、回到Customerc,把所有对amountFor()引用的地Ҏ(gu)换成直接对getCharge()的引用。选中Customercȝ函数amountFor(Rental aRental)Q在右键菜单中选择"重构/内联"Q出现参数选择对话框?BR>
选择"认"按钮Q引用amountFor()的地方被替换成对getCharge()的引用?BR>
public String statement() {
…?BR> double thisAmount = each.getCharge();
…?BR> }
4、除M(f)时变量thisAmount?BR>
选中变量thisAmountQ在右键菜单中选择"重构/内联"Q重构预览窗口如下,可见辑ֈ了重构的目的。按?认"按钮重构代码?BR>
statement()代码Q?BR>
public String statement() {
double totalAmount = 0; // L贚w?BR> int frequentRenterPoints = 0; // 常客U点
Enumeration rentals = _rentals.elements();
String result = "Rental Record for " + getName() + "\n";
while(rentals.hasMoreElements()){
Rental each = (Rental)rentals.nextElement(); //取得一W租借记?BR>
// add frequent renter points(累加 常客U点)
frequentRenterPoints ++;
// add bouns for a two day new release rental
if((each.getMovie().getPriceCode())==Movie.NEW_RELEASE && each.getDaysRented()>1)
frequentRenterPoints ++;
// show figures for this rental(昄此笔U借数?
result += "\t" + each.getMovie().getTitle() + "\t" +
String.valueOf(each.getCharge()) + "\n";
totalAmount += each.getCharge();
}
// add footer linesQ结打华ͼ
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points";
return result;
}
四、重构第三步Q提?常客U点计算"代码
目的Q提?常客U点计算"代码q放在RentalcMQ?常客U点计算"代码如下?BR>
public String statement() {
…?BR> // add frequent renter points
frequentRenterPoints ++;
// add bouns for a two day new release rental
if((each.getMovie().getPriceCode())==Movie.NEW_RELEASE && each.getDaysRented()>1)
frequentRenterPoints ++;
…?BR> }
重构后的代码如下Q?BR>
frequentRenterPoints += each.getFrequentRenterPoints();
重构Ҏ(gu)Q?BR>
Extract Method
Move Method
Change Method signatrue
Inline Method
Ҏ(gu)Q?BR>
1?首先Q抽取代码到独立的函C?BR>
?抽取Ҏ(gu)"重构代码Q函数名QgetFrequentRenterPoints。很遗憾Qeclipse的不能生成诸如:(x)frequentRenterPoints += getFrequentRenterPoints(Rental aRental); 的代码。原因是执行自增操作的局部变量frequentRenterPoints要出现在{式双Q因此抽取函数getFrequentRenterPoints()一定要把frequentRenterPoints作ؓ(f)参数。手工修改函数和对函数的引用Q重构后的代码如下:(x)
public String statement() {
…?BR> while(rentals.hasMoreElements()){
…?BR> frequentRenterPoints += getFrequentRenterPoints(each);
…?BR> }
…?BR> }
/**
* @param each
* @return
*/
private int getFrequentRenterPoints(Rental each) {
if((each.getMovie().getPriceCode())==Movie.NEW_RELEASE && each.getDaysRented()>1)
return 2;
else
return 1;
}
2?把getFrequentRenterPointsQ)Ud到RentalcM?BR>
3?对getFrequentRenterPointsQ)"更改Ҏ(gu)特征W?为public?BR>
4?对Customer的函数getFrequentRenterPoints()执行内联操作Q重构目标完成?BR>
五、重构第四步Q去除(f)时变量(totalAmount和frequentRenterPointsQ?/B>
目的Q去除(f)时变量(totalAmount和frequentRenterPointsQ?BR>
Ҏ(gu)Q?BR>
1?分析totalAmount和frequentRenterPoints的定义和引用l构如下Q?BR>
// 声明和定?BR> double totalAmount = 0;
int frequentRenterPoints = 0;
…?BR> // 在@环中修改
while(rentals.hasMoreElements()){
…?BR> frequentRenterPoints += each.getFrequentRenterPoints();
…?BR> totalAmount += each.getCharge();
…?BR> }
…?BR> // 在@环外使用
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints) + " frequent renter points";
…?BR>
上述两个变量在@环体外面定义和用,在@环中被修改,q用Replace Temp with QueryҎ(gu)去除q两个(f)时变量是一稍微复杂的重构。很遗憾Qeclipse目前不支持这L(fng)重构?BR>
2、手工修改代码?BR>
六、重构第五步Q运用多态取代与h相关的条仉辑
目的Q?BR>
1?把RentalcM的函数getCharge()Ud到MoviecM?BR>
2?把RentalcM的函数getFrequentRenterPoints()Ud到MoviecM?BR>
重构Ҏ(gu)Q?BR>
Move Method
Inline Method
Ҏ(gu)Q?BR>
1?选中RentalcM的函数getCharge()Q右键菜单选中"重构/Ud"Qeclipse提示找不到接收者,不能Ud。原因在于这行语句:(x)
switch(getMovie().getPriceCode()){//取得q出租h
选中getMovie()Q右键菜单选中"重构/内联"Q确定后代码成ؓ(f)Q?BR>
switch(_movie.getPriceCode()){ //取得q出租h
选中getCharge()Q执?重构/Ud"后,函数被移动到MoviecM。然而这只是部分达成了重构目的,我们发现Q移动后的代码把Rental作ؓ(f)参数传给了getCharge()Q手工修改一下,代码变成Q?BR>
class Movie …?BR> /**
* @param this
* @return
*/
public double getCharge(int _daysRented) {
double result = 0;
switch(getPriceCode()){ //取得q出租h
case Movie.REGULAR: // 普通片
result += 2;
if(_daysRented>2)
result += (_daysRented-2)*1.5;
break;
case Movie.NEW_RELEASE: // 新片
result += _daysRented*3;
break;
case Movie.CHILDRENS: // 儿童?BR> result += 1.5;
if(_daysRented>3)
result += (_daysRented-3)*1.5;
break;
}
return result;
}
class Rental…?BR> /**
* @param this
* @return
*/
public double getCharge() {
return _movie.getCharge(_daysRented);
}
2、用同样的步骤处理getFrequentRenterPoints()Q重构后的代码:(x)
class Movie …?BR> /**
* @param frequentRenterPoints
* @param this
* @return
*/
public int getFrequentRenterPoints(int daysRented) {
if((getPriceCode())==Movie.NEW_RELEASE && daysRented>1)
return 2;
else
return 1;
}
class Rental…?BR> /**
* @param frequentRenterPoints
* @param this
* @return
*/
public int getFrequentRenterPoints(int daysRented) {
if((getPriceCode())==Movie.NEW_RELEASE && daysRented>1)
return 2;
else
return 1;
}
七、重构第六步Q终于……我们来到?/B>
目的Q对switch语句引入state模式?BR>
Ҏ(gu)Q?BR>
很遗憾,不得不在q里提前l束eclipse的自动重构之旅。Eclipse几乎不能做结构上的重构。也许Martin Fowler在书中呼唤的自动重构工具止于"工具辅助下的重构工作"q一理念。艺术是人类的专利,~程艺术的梦惛_持箋下去?BR>
感兴的读者可以查看手工重构的最后一步代码。将重构q行到底Q?BR>
附录Qeclipse支持的重构方法(摘自eclipse中文帮助Q?BR>
![]() |
![]() |
QactionSet label="Sample Action Set" visible="true" id="myplugin.actionSet"Q?BR>Qmenu label="我的I间" id="sampleMenu"Q?BR>Qseparator name="sampleGroup"Q?BR>Q?separatorQ?BR>Q?menuQ?BR>Qaction label="天气预报" icon="icons/sample.gif" class="myplugin.actions.SampleAction" tooltip="Hello, Eclipse world" menubarPath="sampleMenu/sampleGroup" toolbarPath="sampleGroup" id="myplugin.actions.SampleAction"Q?BR>Q?actionQ?BR> |
![]() |
![]() |
![]() |
public void run(IAction action) { MessageDialog.openInformation( window.getShell(),"Myplugin Plug-in", "Hello, Eclipse world"); } |
public void run(IAction action) { WeatherDialog wd=new WeatherDialog(); wd.setSize(400, 335); wd.show(); } |
![]() |
![]() |
/* * Created on 2004-9-23 * */ package myplugin; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import javax.swing.JDialog; import javax.swing.JEditorPane; /** * QpQTitle: WatherDialogQ?pQ?BR>* QpQDescription: q个是对话框c,用于昄指定城市的当天的天气预报Q?pQ?BR>* QpQCopyright: Copyright (c) 2004Q?pQ?BR>* QpQCompany:UF SOFTQ?pQ?BR>* @author 赵勇 * @version 1.0 */ public class WatherDialog extends JDialog { String city="北京"; private JEditorPane jEditorPane = null; /** * This method initializes * / public WatherDialog(String city) { super(); this.city=city; initialize(); } /** * This method initializes this * @return void */ private void initialize() { this.setContentPane(getJEditorPane()); try { //构徏URL对象 URL url =new URL("http://weather.news.sina.com.cn//cgi-bin/figureWeather/simpleSearch.cgi?city="+city); String temp=""; BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream())); //使用openStream得到一输入ƈ由此构造一个BufferedReader对象 String inputLine; //从输入流不断的读数据Q直到读完ؓ(f)?BR>while ((inputLine = in.readLine()) != null) temp=temp+inputLine+"\n"; //关闭输入?BR>in.close(); String weather =temp.substring ( temp.indexOf( "Qbody"), temp.lastIndexOf( "bodyQ?)+5); this.jEditorPane .setText(weather); } catch (Exception e) { e.printStackTrace(); } this.setTitle("天气预报"); this.setSize(400, 166); } /** * This method initializes jEditorPane * * @return javax.swing.JEditorPane */ private JEditorPane getJEditorPane() { if (jEditorPane == null) { jEditorPane = new JEditorPane(); jEditorPane.setContentType( "text/html"); } return jEditorPane; } } // @jve:decl-index=0:visual-constraint="70,19" |
public void run(IAction action) { WeatherDialog wd=new WeatherDialog("北京"); wd.setSize(400, 335); wd.show(); } |
![]() |
如果你在上v或者其他城市,试着修改city参数?上v"Q再ơ运行,你将发现Q你仍然能够得到该城市的天气预报Q这里我们从|站上提取的信息Q有Ҏ(gu)机取巧了Q。值得注意的是QXmethod|站提供了一个天气预报的WebServiceQ可惜只有美国的城市Q不然我们可以用Web Service调用获取天气预报Q将?x)更酗?BR>
现在q行是工作台已经具备了天气预报的功能Q还需要更q一步,改插g导出发布Q拷贝到Eclipse根目录的plugins目录中,重新启动Q具体参见Eclipse帮助Q。现在你自己的EclipseQ就具备了天气预报的功能Q只要你点击鼠标Q就可以在编E之余轻杄获取天气信息?除非你的老板认ؓ(f)你在工作旉随时了解天气情况不是一个好LQ我认ؓ(f)你完全可以将q个插gU_个h收藏的插件之列。你也可以在此基上扩展,增加一些配|文件和属性设|,定制出满p求的插g。如果能够增加信息的自动qo(h)和筛选,那将是一ơ很愉快的体验,如果你有旉和兴,不妨一试?/P>
3.邮g快速监控插?/B>
现在你的工作因ؓ(f)Eclipse而更加惬意,更具创造力Q那么你q有什么不满?你是否厌倦了各种邮g客户端随旉地的?d)扰你呢Q你希望你在高兴的时候适时的了解一下邮件的概况Q好了,既然惛_了ؓ(f)什么犹豫呢Q因Z是程序员Q你是要用Eclipse享受完全DIY的乐?BR>
3.1生成插g
本部分我们将在以上myplugin插g的基上增加一个邮件过滤显C的对话框,cM的我们通过VisualEditer创徏一个名为MailDialog的对话框Qƈ增加一个JEditPane用来昄邮箱中我们关注的信息?BR>
修改plugin.xmlQ增加一?我的邮g"菜单
Qaction
label="邮g信息"
icon="icons/sample.gif"
class="myplugin.actions.MailAction"
tooltip="邮g信息"
menubarPath="sampleMenu/sampleGroup"
toolbarPath="sampleGroup"
id="myplugin.actions.MailAction"Q?BR>Q?actionQ?/TD>
现在Q你知道要创Z个MailAction的Actionc,q在在Run中增加如下代?BR>
MailConfig mail=new MailConfig();
String popServer="server";
String popUser="zhaoyong";
String popPassword="1234";
//讄需要过滤的关键字:(x)发g人和邮g主题
String [] strFrom=new String[] {"zhaoyong"};
String [] strSubject=new String[] {"试"};
MailConfig[] mc =new MailConfig [] { mail };
MailDialog md=new MailDialog(mc);
System.err.println("run run run ") ;
md.setSize(400, 335);
md.show();
以上的代码编译不?x)通过Q但是别着急,慢慢来,很快了?BR>
3.2构徏邮g监控对话?
当然你需要徏立一个MailConfigcȝ来表CZ个邮q具体讄已及(qing)相关信息Q这里就不在累述说明Q详情参见参考资料中的代码。需要说明的式MailConfig除了要记录一个邮q地址Q用户名和密码外Q还提供2个关键字数组Q如果ؓ(f)I,不加qo(h)Q如果关键字有|pȝ?x)根据发件h和邮件标题中是否包含关键字来q行昄邮g信息Q已保证你的l对自由?BR>
首先我们需要实C个MailConfigc,表示邮g配置Q每个MailConfig的对象代表一个邮件帐P我们的系l将能显C多个邮q配置Q每个MailConfig中用一个数l来保存需要过滤的收g人和邮g地址?BR>
MailConfigcȝ中的变量如下Q?
String popServer;
String popUser;
String popPassword;
//讄需要过滤的关键字:(x)发g人和邮g主题
String [] strFrom;
String [] strSubject;
//是否昄邮g内容
boolean isViewContent=false;
同样Q我们将使用一个对话框来显C邮件信息,MailDialog需要引用javaMail.jar,和activation.jarq两个类包,保已经有这两个cdq加入到目的类路径中。最后的MailDialog代码如下Q?BR>
package myplugin;
import java.io.IOException;
import java.util.Properties;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.InternetAddress;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JTextPane;
/**
* @author zhaoyong
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class MailDialog extends JDialog
{
private JEditorPane jEditorPane = null;
private JTextPane jTextPane = null;
//可以昄多个邮g配置
MailConfig[] mc= null;
/**
* This method initializes
* 构造函?BR>* @param mc : 需要显C的多个邮箱配置对象?BR>*/
public MailDialog(MailConfig[] mc)
{
super();
if(mc!=null)
this.mc = mc;
else
System.err.println("邮g配置错误Q?) ;
initialize();
}
/**
* This method initializes this
* 初始?BR>* @return void
*/
private void initialize()
{
try
{
//讑֮昄内容的面?BR>this.setContentPane(getJTextPane());
//取得所有的新邮件信?BR>String s= getAllMailInfo();
//信息显C在对话框中
this.jTextPane .setText(s);
this.setTitle("邮g信息");
this.setSize(251, 100);
}
catch (Exception e)
{
//发生错误昄错误信息
this.jTextPane .setText(e.toString());
e.printStackTrace();
}
}
/**取得所有的邮箱的需要监控的邮g信息
*
* @return String
*/
private String getAllMailInfo()
{
String allMailInfo="";
if (mc.length Q?)
allMailInfo="没有配置邮箱Q?;
else
{
for(int i=0;iQmc.length;i++)
{
//循环获取每个邮箱的邮件信?
allMailInfo=allMailInfo+getMailInfo(mc[i]);
}
}
//q没有收到相关的邮g
if (allMailInfo.trim().length() ==0)
allMailInfo="未检到相关新邮Ӟ";
return allMailInfo;
}
/*
*得到一个邮׃满条g的所有新邮g的字W串形式
**/
private String getMailInfo(MailConfig mc)
{
//最l输出的邮g信息
String mailInfo="";
//每个邮箱服务器上的Store和Folder对象
Store store=null;
Folder folder=null;
try
{
Properties props = System.getProperties();
//与邮件服务器生成一个Session
Session session = Session.getDefaultInstance( props,null);
//l出服务器,用户名,密码q接服务?BR>store = session.getStore("pop3");
store.connect(mc.getPopServer(), mc.getPopUser(),mc.getPopPassword());
//取得默认的邮件Folder
folder = store.getDefaultFolder();
if (folder == null)
throw new Exception("No default folder");
//取得收g?BR>folder = folder.getFolder("INBOX");
if (folder == null)
throw new Exception("No POP3 INBOX");
//以只L式打开收g?BR>folder.open(Folder.READ_ONLY);
//获取所有新邮gq处?BR>Message[] msgs = folder.getMessages();
for (int i = 0; i Q?msgs.length; i++)
{
Message message= msgs[i];
//取得每封邮g的信息,需要引用MailConfig对象q行关键字过?BR>mailInfo = mailInfo+ getMessageInfo( message,mc);
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
//安全的关闭邮件服务器资源
try
{
if (folder!=null) folder.close(true);
if (store!=null) store.close();
}
catch (Exception ex2) {ex2.printStackTrace();}
}
return mailInfo;
}
/**
* 得到一邮件的信息Q需要根据MailConfigqo(h)
* @param mailInfo
* @param message
* @return 邮g信息
* @throws MessagingException
* @throws IOException
*/
private String getMessageInfo( final Message message ,final MailConfig mc)
throws MessagingException, IOException
{
//q回的改邮g信息
String mailInfo="";
String from=((InternetAddress)message.getFrom()[0]).getPersonal();
if (from==null)
from=((InternetAddress)message.getFrom()[0]).getAddress();
String subject=message.getSubject();
//如果满qo(h)信息则显C,否则q回I?BR>if(isElementinString(from,mc.getStrFrom())
||isElementinString(subject,mc.getStrSubject()) )
{
mailInfo=mailInfo+"发g?: "+from+"\n";
mailInfo=mailInfo+"邮g主题 : "+subject+"\n";
mailInfo=mailInfo+"发送时?: "+message.getSentDate() +"\n";
//如果昄内容Q则打印内容
if(mc.isViewContent)
mailInfo=mailInfo+message.getContent() +"\n";
mailInfo=mailInfo+"------------------------------------\n";
}
return mailInfo;
}
private JTextPane getJTextPane()
{
if (jTextPane == null)
{
jTextPane = new JTextPane();
}
return jTextPane;
}
/**
* 判断目标关键字数l中是否有指定的字符?q行qo(h)
* @param targetStr Q?BR>* @param keys Q?BR>* @return 如果有,q回trueQ?否则q回false
*/
private boolean isElementinString(String targetStr,String [] keys)
{
//没指定过滤条Ӟ昄所?BR>if (keys==null)
return true;
//指定字符串ؓ(f)I,直接q回false
if (targetStr==null)
return false;
for(int i=0;iQkeys.length ;i++)
{
if (targetStr.indexOf(keys[i])Q?1)
return true;
}
return false;
}
}
// @jve:decl-index=0:visual-constraint="10,10"Q-说明Q这是Visual Editord的控制信?/TD>
以上代码的注释已l保证你能够看清楚,q里׃加篏qͼ有兴的可以自己试试Q体验一切尽在掌握的快感。当然这个例子做的实在简单,因此也ؓ(f)你的q一步开发留有够的余地?BR>
3.3 打包和发?BR>
到此Q在mypulgin中增加了邮g信息菜单和对话框Q系l的plugin.xml如下Q?BR>
Q?xml version="1.0" encoding="UTF-8"?Q?BR>Q?eclipse version="3.0"?Q?BR>Qplugin
id="myplugin"
name="Myplugin Plug-in"
version="1.0.0"
provider-name=""
class="myplugin.MypluginPlugin"Q?BR>
QruntimeQ?BR>Qlibrary name="myplugin.jar"Q?BR>Qexport name="*"/Q?BR>Q?libraryQ?BR>Qlibrary name="lib/javaMail.jar"Q?BR>Qexport name="*"/Q?BR>Q?libraryQ?BR>Qlibrary name="lib/activation.jar"Q?BR>Qexport name="*"/Q?BR>Q?libraryQ?BR>Q?runtimeQ?BR>
QrequiresQ?BR>Qimport plugin="org.eclipse.ui"/Q?BR>Qimport plugin="org.eclipse.core.runtime"/Q?BR>Q?requiresQ?BR>
Qextension
point="org.eclipse.ui.actionSets"Q?BR>QactionSet
label="Sample Action Set"
visible="true"
id="myplugin.actionSet"Q?BR>Qmenu
label="我的I间"
id="sampleMenu"Q?BR>Qseparator
name="sampleGroup"Q?BR>Q?separatorQ?BR>Q?menuQ?BR>Qaction
label="天气预报"
icon="icons/sample.gif"
class="myplugin.actions.SampleAction"
tooltip="Hello, Eclipse world"
menubarPath="sampleMenu/sampleGroup"
toolbarPath="sampleGroup"
id="myplugin.actions.SampleAction"Q?BR>Q?actionQ?BR>Qaction
label="邮g信息"
icon="icons/sample.gif"
class="myplugin.actions.MailAction"
tooltip="邮g信息"
menubarPath="sampleMenu/sampleGroup"
toolbarPath="sampleGroup"
id="myplugin.actions.MailAction"Q?BR>Q?actionQ?BR>Q?actionSetQ?BR>Q?extensionQ?BR>Q?pluginQ?/TD>
实际上,我们在一个插件中加入?个功能,因此实C我们的开发环境的自我扩展和定制。同P参考Eclipse的帮助,你可以轻杄再次插件打包导出,q将其加入自qEclipse 的plugins目录Q可能需要解压羃Q,或通过help菜单的Update选项q行安装Q注意导出时需要选定相关的类包。重新启动,你将发现自己的IDE已经多了自己的菜单,开发环境已l随着自己的意愿在改变了,E序员天生的满感a(b)然而生?
现在Q你可以在需要的时候点击菜单,了解你希望监控的邮g情况或者最q的天气情况Q一切轻杄在掌握QEclipse的插Ӟ是q样全能?BR>
4.ȝ
那么Q聪明的你有没有发现什么问题,对,上面的东西太_糙Q太单了Q你可以做进一步的优化设计和功能加强,比如Q自己增加邮仉|文件而不是写在代码里面,动态监控弹出邮仉警(通过事先讑֮的紧急状态)Q你也许q会(x)惌v来很多的新的LQ比如我Z么不能有个能看电(sh)q插gQ或是Eclipse中飘出动听的音乐Q别急,q些不一定需要你亲手dQhttp://sourceforge.net/projects/rocexwang/ 有一个播攑֙插gQ但是现在仅能播N乐,国外已经有h用Eclipse开发游戏,也有人用Eclipse来做MISpȝ的。http://www.matrix.org.cn/forum_view.asp?forum_id=25&view_id=10510 有一个国人开发的俄罗斯方块游戏,虽然单了一炏V当Ӟ通过|址http://eclipse-plugins.2y.net/eclipse/index.jsp和http://www.eclipseplugincentral.com/你可以找到很多的插gQ如果你知道什么更好的插gQ请告诉我?BR>
Eclipse提供了一个纯的框架和插gl构Q得开发Q何功能的插g都能成ؓ(f)现实。本文介l了2个有的Eclipse插g的开发,可以使我们的工作环境增加了两个可q功能,同时也得你具备了基的插件开发能力,借助Eclipse的强大功能,从此你可以把你的Mx变ؓ(f)现实。同时请保持与别人的交流Q我?x)很乐意了解你的新奇的插Ӟq收藏和学习(fn)M好的插gQ打造一个完全属于自q个性化的开发环境。Eclipse在不久的将来成Z个全能的PlatformQ这一点在全世界数以万计的开发h员的手中Q正一点一点变为现实?/P>
虽然目前Eclipse目q没有最后完成,但从已有的版本中已经能领略到Eclipse设计d思想和主要功能特炏V现在就了解Eclipse不但能q大E序员对q款业界期望很高的IDE能一睹ؓ(f)快,更ؓ(f)重要的是如果能参加到Eclipse目的开发中或是阅读它的开放源代码Q这对广大程序员来说无疑是一个千载难逢的提高~程水^的好Z(x)。Eclipse计划提供多个q_的版本,象WindowsQLinuxQSolarisQHP-UX和AIXQ以下只介绍Windows版本。本文第一部分先介lEclipse的基本用方法。第二部分介l如何进行Eclipse的插件开发?/P>
Eclipse是开放源代码的项目,你可以到 www.eclipse.orgd费下载Eclipse的最新版本,一般Eclipse提供几个下蝲版本QReleaseQStable BuildQIntegration Build和Nightly BuildQ徏议下载Release或Stable版本Q笔者用的是Build20020125QStable版本Q。Eclipse本n是用Java语言~写Q但下蝲的压~包中ƈ不包含Javaq行环境Q需要用戯己另行安装JREQƈ且要在操作系l的环境变量中指明JRE中bin的\径。安装Eclipse的步骤非常简单:(x)只需下载的压羃包按原\径直接解压既可。需注意如果有了更新的版本,要先删除老的版本重新安装Q不能直接解压到原来的\径覆盖老版本。在解压~之后可以到相应的安装\径去找Eclipse.exeq行。如果下载的是Release或Stable版本Qƈ且JRE环境安装正确无误Q一般来说不?x)有什么问题,在闪C个很L(fng)月蚀囄后,Eclipse?x)显C它的缺省界面:(x)
图一
乍一看v来,Eclipse的界面有点象JBilderQ但实际操作q程中会(x)发现它更象IVJQ毕竟开发Eclipse的主导用量是开发IVJ的原班h马(可参考www.oti.comQ。另外还值得一提的是Eclipse目的参预者除了IBM以外Q还有象BorlandQRational SoftwareQRedHatQMerant{一大批业界的姣姣者,q也为Eclipse的未来奠定了良好的基?/P>
下面分别对Eclipse的各U特性作单介l,包括Q文件存放,开发环境,~译与运行,版本理Q用插件?/P>
大多IVJ的初学者都Ҏ(gu)不到Java源代码感到非怸适应Q因为IVJ把所有的源代码都存储C个reponsitory库文件中Q想要得到文本格式的源代码必ȝExport功能从reponsitory中导出源代码。用了reponsitory的IVJҎ(gu)代码的管理功能几乎达到极_(d)正是q一点得许多程序员对IVJ钟爱有加。而Eclipse源代码以文本方式保存,却实CIVJҎ(gu)代码理的几乎全部功能,q且q增加了一些新的功能,能达到这一点,不能不惊叹于Eclipse开发者的高超技巧?/P>
安装Eclipse之后Q在安装路径的下一层\径中?x)有一个workspace文g夏V每当在Eclipse中新生成一个项目,~省情况下都?x)在workspace中生和目同名的文件夹以存放该目所用到的全部文件。你可以用Windows资源理器直接访问或l护q些文g?/P>
已有的文g加入C个项目中目前有三U方式:(x)W一U是象在IVJ中的一P用IDE?File"菜单中的"Import"功能文件导入到目中。这也是推荐的方式。第二种是从Windows的资源管理器中直接拖动文件到目中。第三种是直接文件拷贝到目文g夹中Q然后在Eclipse的资源浏览窗口中选择目或文件夹q执行从本地h功能QRefresh from locateQ。需要说明的一Ҏ(gu)Q项目文件夹可以攑֜计算机的M位置Qƈ且可以在Eclipse中用新徏目的方法将目路径指定到已l存在的目文g夹,然后在Eclipse中刷新即可。但要注意的是,M目文g夹徏立或指定Q目前都只能在Eclipse中用新徏目的方法来实现Q即使是在缺省存储项目文件夹的workspace路径下新建立一个文件夹Q在Eclipse环境中也是无法将它变成一个项目,也就是说Q这个文件夹对Eclipse是不可视的?/P>
和IVJ一PEclipse开发环境被UCؓ(f)WorkbenchQ它主要׃个部分组成:(x)视图QPerspectiveQ,~辑H口QEditorQ和观察H口QViewQ。在下面的介l中Q希望读者能知道Java视图QJava包浏览窗口,资源视图Q资源浏览窗口等区别Q其实最主要的也是要区别视囑֒H口Q,以免在进一步的阅读中生淆。图二是它们之间的关pȝ构略图:(x)
图二
在图二中Q可以看出Workbench包含多个视图Q而每个视囑֏包含不同的窗口。由于每个编辑窗口有很大的共性,而且~省情况它们都在同一区域中显C,因此我们只在每个视图中标Z个编辑窗口,以绿色表C。观察窗口则各不相同Q这里以U色表示?/P>
下面首先介绍~辑H口。所有文件的昄和编辑都包含在编辑窗口里。缺省情况下打开的多个文件是以标{(TagTableQ方式在同一个窗口中排列Q可以用拖动方式这些文件排列成各种布局。方法是拖动某一个文件的标签QtagQ到~辑H口的边框,当光标有相应的变化时再释放?/P>
当文件被加入到项目中后,在资源浏览或Java包浏览窗口双?yn)LӞEclipse?x)试图打开q个文gQ其中Eclipse内嵌的编辑器能缺省打开一些文Ӟ?.javaQ?.txtQ?.class{等。如果是其它cd的文ӞEclipse?x)调用操作系l相应的~省~辑器打开Q如word文档QPDF文g{。同时Eclipse也可以象IVJ一L(fng)指定的编辑器打开相应的文件。例如在Eclipse目中双击HTML文gӞ可能希望是用Notepad打开Q而不是用pȝ~省的IE览器打开。实现的Ҏ(gu)是打开菜单栏中的WorkBenchàPreferences对话框,之后在对话框中选择WorkBenchàFile EditorsQ然后添加文件类型,?.htmlQ再为其指定~辑器即可?/P>
在编辑窗口,q值得注意的是Q习(fn)惯了IVJ的程序员在编辑JavaE序的时候,更愿意以Ҏ(gu)为独立的~辑单位Q即在编辑窗口中只显C单个的Ҏ(gu)Q而不是程序全部的源代码)Q这U方式也的确是非常合理的开发方式,不仅代码的显C更加简Pq能辅助E序员编出封装性更好的cR在Eclipse在工h上提供了一个切换按钮,可以?昄全部代码"?只显C所选单?Q这里的单元指的是单个方法、变量、导入的包等Q之间切换(可参考下面的图三Q。徏议没有用过IVJ的程序员也尝试一下在"只显C所选单?状态下q行代码开发?/P>
其次要介l的是观察窗口,它配合编辑窗口ƈ提供了多U的相关信息和浏览方式。常用的观察H口有资源浏览窗口(NavigatorQ,Java包浏览窗?Packages)Q控制台QConsoleQ,d栏(TaskQ等{?/P>
览H口和Java览H口是观察窗口核心部分。前者和W(xu)indows的浏览器差不多,能浏览项目文件夹中的所有文Ӟ后者用来浏览项目中的Java包,包中的类Q类中的变量和方法等信息。在Java览H口中可以通过用鼠标右键的菜单中的Open Type Hierarchy打开层次览H口QHierarchyQ,q个H口非常实用Q它能非常清晰的查看cȝ层次l构。类中的~译出错信息可以在Q务窗口中查到Q同时它也可以成为名W其实的dH口Q向其中d新的d描述信息Q来跟踪目的进度。控制台则主要用来显C程序的输出信息。在调试E序的时候,?x)有更丰富的观察H口来帮助程序员q行调试Q如变量值察看窗口,断点H口{等?/P>
观察H口是Q何IDE开发环境的核心Q用好观察窗口是也就是用好IDE开发环境。Eclipse提供了丰富的观察H口Q能真正用好q些H口恐怕要得经q一D|间的练?/P>
最后介l视图。一个视囑括一个或多个~辑H口和观察窗口。在开发环境的最左侧的快h中的上部分显C的是当前所打开的视囑֛标。视图是Eclipse的最灉|的部分,可以自定义每个视图中包含的观察窗口种c,也可以自定义一个新视图。这些功能都被包括在"Perspective" 菜单中。在Eclipse的Java开发环境中提供了几U缺省视图,如资源视图(Resource PerspectiveQ它也是W一ơ启动Eclipse时的~省视图Q,Java视图QJava PerspectiveQ,调试视图QDebug PerspectiveQ,团队视图(Team Perspective){等。每一U视N对应不同U类的观察窗口。可以从菜单栏中的PerspectiveàShow View看到该视囑֯应的观察H口。当Ӟ每个视图的观察窗口都是可配置的,可以在菜单栏中的PerspectiveàCustomizeq行配置。多样化的视图不但可以帮助程序员以不同角度观察代码,也可以满不同的~程?fn)惯?/P>
在IVJ中调试功能非常强大,多种跟踪方式Q断点设|,变量值察看窗口等{。这些在Eclipse中都也有提供。在本文下面介绍插g的时候,?x)结合例子更加详l的介绍如何使用配置目环境Q如何运行和调试E序?/P>
在Java视图中,工具栏中有两个按钮,分别用来q行调试和运行。ƈ且可能由于安装的插g不同Eclipse?x)存在多U运?调试E序的方式,Z定当前目用那一U方式运行,需要在目的属性选项中的讄LauncheràRun/Debug选项。通常我们需要用的是"Java Applicantion"方式。在q种方式下,如果当前位置是包含main()Ҏ(gu)的JavaE序Q点击调?q行按钮׃(x)立即开始执行调?q行功能。如果当前位|是在包或项目上QEclipse?x)搜索出当前位置所包含的所有可执行E序Q然后由E序员自己选择q行那一个?/P>
在目前的Eclipse的Release和Stable版本中缺省安装了插g开发环境(Plug-in Development EnvironmentQ即PDEQ它本n也是一个插ӞQ此时系l除?Java Applicantion" q行方式Q可能还有另外两U方式:(x)"Run-time WorkBench"?Run-time WorkBench with Tracing"Q当用PDE开发插件的时候会(x)用到q两U运行方式,在下面我们也有提到?/P>
可以把Eclipse的版本管理分Z人(或称为本圎ͼ和团队两U?/P>
Eclipse提供了强大的个h版本理机制Q每一ơ被保存的更攚w可以得到恢复。而且可以_到每一个方法的版本恢复。操作也十分方便Q在M一个能看到所要操作文件的观察H口中,例如资源览H口Q选中该文Ӟ点击右鼠标键Q选择Compare with或Replace withQ如果是恢复已经被删除的Ҏ(gu)则可以选择Add from local historyQ之后相应的本地历史记录׃(x)昄出来Q按照你的需求找到相应的版本可以了。强大的个h版本理功能为程序员提供了更多的信心Q只编下去QQ何不心的错误都可以恢复Q在Eclipse下开发,是有"后?zhn)?的!
Eclipse~省为版本管理工具CVS提供了接口,可以非常方便的连接到CVS服务器上。通过CVS版本理QEclipse为团队开发提供良好的环境。要q接CVS服务器需要先打开团队视图QTeam PerspectiveQ,然后在Reponsitories观察H口中点击鼠标右键ƈ选择新徏QNewQ,在打开的对话框中可以填入要q接的CVS库所需要的信息Q如CVS服务器类型,目前Eclipse支持三种方式Qpserver、extssh和extQ还要填入用户名Q主机名Q密码,reponsitory地址{信息?/P>
在Eclipse中用CVS需要注意的是一些术语和功能的变化,CVS中的Branchq里被称为StreamQ取消了CVS中check out、import和commit{功能,l统用鼠标右键菜单中的Team->Synchronized with Stream来替代。这些功能都通过囑Ş界面完成Q在每次操作中都?x)有当前文g和以前各个版本的比较H口Q操作非常直观,易于掌握Q因此这里也׃再做q一步介l了?/P>
使用插g可以丰富Eclipse的功能。下面将介绍如何应用插g来嵌入Tomcat服务器。这个插件ƈ不是Eclipse目l开发的Q而是一家叫sysdeo的公司开发,非常yQ只?7.8K。你可以?http://www.sysdeo.com/eclipse/tomcatPlugin.htmld费下载。另外,q个插g只支持Tomat4.0以上的版本,可以?www.apache.org得到Tomcat的最新版本?
要安装插件只需下载的zip文g按原路径解压?你的Eclipse的安装\径\plugins"下面Q然后重新启动Eclipse。启动后在菜单栏上选择PerspectiveàCustomizeQ在打开的对话框中选中OtheràTomcat。之后马上会(x)发现Eclipse有了两处变化Q菜单栏中多了一个Tomcat选项Q工h中多了两个按钮,上面是大家可能非常熟(zhn)的Tomcat猫Q如下图三。除此之外,在菜单栏中选择QWorkbenchàPreferencesQ打开对话框后?x)发现这也多了一个Tomcat选项Q在q里要求指定你的Tomcat安装根\径。之后还要检查一下在Preferences对话框中的JavaàInstalled JRE所指定的JRE和启动Tomcat的JRE是否为同一个JREQ如果不是,可能?x)导致Tomat不能正常启动。如果以上检查没有问题,可以用工具栏上?猫"直接起动Tomcat了。要注意的是Q启动过E很慢,要耐心{到以下信息出现Q?/P>
Starting service Tomcat-Standalone
Apache Tomcat/4.0.1
Starting service Tomcat-Apache
Apache Tomcat/4.0.1
之后可以在外部览器(如IEQ中输入 http://localhost:8080来测试Tomcat是否正常?
图三
如果启动正常Q可以进一步尝试在Eclipse中调试Servlet或JSPE序。下面我们将用Tomcat自带的Servlet例程HelloWorldExample.java来示范一下如何在Eclipse中调试SevletE序?/P>
首先要在Java视图中新Z个Java目Qؓ(f)了方便,可以直接项目\径指定到HelloWorldExmapleE序所在\径,如图四:(x)
囑֛
之后?Next"Q进入Java Settings对话框,选择Libraries标签Qƈ用Add External JARs按钮来指定Servlet.jar包的位置。这里直接用了Tomcat中的Servlet.jar包。如图五Q?/P>
图五
最后,点击"Finish"完成目的生成。在新生成项目中的default package可以扑ֈHelloWorldExample.javaQ双?yn)L开文gQƈ可尝试给HelloWorldExample加上一个断点(双击~辑H口左侧边界Q。之后在外部览器中输入 http://localhost:8080/examples/servlet/HelloWorldExampleQ再回过来看Eclipse发生了什么变化,是一个调试窗口呀Q在Eclipse中进行的调试操作和绝大多数的IDE大同异Q如讄断点Q单步跟t,变量值察看等{,在这里也׃用再详述了?
二.开发Eclipse插gQPlug-insQ?/SPAN>
Eclipse最有魅力的地方是它的插g体系l构。在q个体系中重要的概念是扩展点Qextension pointsQ,也就是ؓ(f)插g提供的接口。每一个插仉是在现有的扩展点上开发,q可能还留有自己的扩展点Q以便在q个插g上l开发?/P>
׃有了插gQEclipsepȝ的核心部分在启动的时候要完成的工作十分简单:(x)启动q_的基部分和查扄l的插g。在Eclipse中实现的l大部分功能是由相应的插件完成的Q比如WrokBench UI插g完成界面的外观显C,Resource Management插g完成l护或生成项目或文g{资源管理工作(在下面的W二个例子就?x)用到这个插ӞQ而Version and Configuration Management (VCM)插g则负责完成版本控制功能,{等。虽然以上提到的每一个功能都是绝大多数IDE环境所必备的功能,Eclipse却也把它们都做成了插件模式,甚至用来开发JavaE序的开发环境(Java development toolingQJDTQ也只不q是Eclipsepȝ中的一个普通插件而已。整个Eclipse体系l构p一个大拼图Q可以不断的向上加插Ӟ同时Q现有插件上q可以再加插件。下面的插g开发示例就是在WorkBench UI插g中的观察H口扩展点上开发的?/P>
本文W一部分介绍qEclipse的开发界面其中之一是观察H口Q它通常配合~辑H口昄一些有用的信息Q在q里我们只简单生成一个显C欢q信息的观察H口Q假设新插g的名子叫Welcome?/P>
W一步,先用向导新徏一个Java目。我们可以在菜单栏选择FileàNewQ或用工h的向导按键,或是在资源窗口用鼠标右键菜单中的NewQ打开向导对话框,然后用缺省方式创建项目。ƈ在项目中建立一个Welcome.java文gQ代码如下:(x)
|
Zɘq个E序能正常编译,要配|它的编译环境,x定所需的CLASSPATH。在Eclipse中可以用几种Ҏ(gu)Q常用的是两U:(x)W一是在资源H口或Java包窗口选中该项目,点击鼠标右键Q在打开的菜单中选择属性(PropertiesQ,之后在属性对话框中选择Java Build PathàLibrariesQ用Add External JARs功能d三个包,它们都是Eclipse的现有插件的cdQ可以在"你的Eclipse安装路径\plugins"下面的相应\径中扑ֈ。分别是org.eclipse.core.runtime插g中的runtime.jarQorg.eclipse.swt中的swt.jar和org.eclipse.ui中的workbench.jar。第二种指定CLASSPATH的方法是先将以上提到的三个包直接导入到Eclipse中的某下一个项目中。如果导入到和W(xu)elcome.java相同的项目中Q则无需q一步指定CLASSPATHQ否则需要在目的属性菜单中选择Java Build PathàProjectsQ然后选中q三个包所在的目?/P>
在我们的目中还要生成一个XML文gQ它的名字必plugin.xml。代码如下:(x)
|
在plugin.xml中一共有四个主要的标{:(x)pluginQrequiresQruntimeQextension。其中plugin标签的属性提供的是我们要开发的Welcome插g的基本信息,除了nameQversionQprovider-name{,最重要的是idQ它要求不能和现有的Eclipse插gid有冲H,因此我们用包名作为插件的id。requires标签中所列出的是需要的插gQ这里我们要用到Eclipse Workbench和SWT APIQ因此导入了org.eclipse.ui插g。runtime标签指明的是我们开发的插g所在JAR包的文g名。extension标签是插件扩展点的信息。org.eclipse.ui.views是Eclipsepȝ提供的观察窗口扩展点Q我们的例子是一个观察窗口(ViewQ,q表明我们是要在 org.eclipse.ui.views扩展点上q一步开发。extension中还包括category和view两个标签Q在后箋的启动Welcome插g步骤中,我们׃(x)知道q两个标{含义。要注意的是category和view标签的id的唯一性,q且在view的属性中声明了Welcome插g的类名?/P>
在Eclipse中ؓ(f)plugin.xml提供了缺省可视化的编辑器Q在~写plugin.xmlq程中可以借助q个~辑器完成一些工作。如果你直接录入了plugin.xml文g源代码,q可以用q个~辑器校验你的代码:(x)如果~辑器不能正读入,p明你的plugin.xml有一些问题?/P>
在确认Weclome.java和plugin.xml都正无误之后,可以用Eclipse菜单栏中的Export命o(h)Weclome.java导出为JAR文gQ它的名子应该和plugin.xml中runtime声明的JAR怸致。同时导出plugin.xml。安装Welcome插g的方法和本文W一部分介绍的安装Tomcat插gҎ(gu)是一L(fng)Q首先在"Eclipse的安装\径\plugins"路径下面建立一个com.nidapeng.eclipse.plugin路径Q然后将Weclome.jar和plugin.xml拷到q个路径下。之后必需重新启动EclipseQ在Eclipse启动的时候,它会(x)搜烦所有在插g路径下的插gq注册它们(仅仅是注册,只有在需要某个插件的时候,Eclipse才会(x)启动它)。在重新启动的Eclipse的菜单栏中选择PerspectiveàShow ViewàOthersQ在打开的对话框中我们会(x)扑ֈ在plugin.xml中extension的category标签中声明的name属性:(x)Welcome。在Welcome的支l点中包含了view标签name属性:(x)Welcome to Eclipse。选中它ƈ认QW(xu)elcomeH口׃(x)昄在Eclipse Workbench上的某个位置 。如果在执行了以上操作,但没有显C新H口Q可以再ơ打开Show View菜单Q此时在菜单中应该有C剙择QWelcome to EclipseQ然后选中它?/P>
上面我们完成了一个观察窗口的插gQ但q个操作q程对开发稍微复杂一些的插g显得不太方便了Q每ơ测试都要将代码打包Q发布,再重新启动EclipsepȝQؓ(f)此Eclipse提供了一个专门ؓ(f)开发插件而做插gQ有点绕_(d)QPlug-in Development EnvironmentQPDEQ。本文前面曾提到Q目前Eclipse的Release或Stable版本~省提供了这个插Ӟ因此如果安装的Eclipse是这两个版本中的一个就可以直接q行下面的步骤。下面我们再用PDE环境开发一个稍微复杂一些的插g?/P>
W一步仍然要新徏一个项目,只是在向g不是用Java目Q而是Plug-in Development中的Plug-in Project。在应用向导生成新项目的时候,要注意两点:(x)W一是PDE的项目名U就是plugin的idQ因此要保证它的唯一性,q里我们的项目名是com.nidapeng.eclipse.plugin.pde。其ơؓ(f)了进一步说明Eclipse插g的结构,在Plug-in Code Generators中,选择用向导模板生成一个缺省的插gQ如囑օQ?/P>
囑օ
q个用缺省方式生成的插gcd于我们将要的代码q不是必需的,也可以用生成I插件的方式建立我们的项目,q样做只是ؓ(f)q一步说明Eclipse的插件结构?/P>
目生成之后Q在我们的项目中?x)包含一个PdePlugin.java文gQ它?yu)是以缺省方式生成的插gcR注意到它承了AbstractUIPluginc,而AbstractUIPlugincdCorg.eclipse.ui.plugin接口。事实上Q所有的Eclipse插g都会(x)有一个相应的实现plugin接口的类Q这个类是新插件的ȝQ类g有main()函数的Javac)Q它负责理插g的生存期。在我们的AbstractUIPluginl承子类中,可以用singleton模式来保存在Eclipse中的生成的该插g的第一个也是唯一实例Q一般来_(d)在该l承子类中也要实C个getDefault()Ҏ(gu)以返回当前插件的实例。而且Q当Eclipse首次使用该插件的时候,q个ȝ是W一个被调用的类Q因此我们也可以在它的代码中执行一些初始化的工作。而且如果插g需要用PreferencesQDialogs或Images资源Q也可以通过q个cM的相应方法来得到它们的实例,如用其中的getDialogSettings()QgetPreferenceStore()QgetImageRegistry()Ҏ(gu)?/P>
但是象前面提到的QPdePlugin.java对下面的例子q不是必需的,我们不用对它q行M修改。在我们W一个例子中的Weclome插gQ根本就没有生成AbstractUIPlugin的承子c,此时pȝ?x)自动?f)Weclome插g生成一个缺省的ȝQ类gJavacL造函敎ͼ如果没有声明Q系l会(x)指定一个默认的构造函敎ͼ?/P>
下面的代码是才真正实C我们新插件的功能Q假设这个插件名子是NoticeViewQ?/P>
|
象上面的W一个Welcome插gQ这个新插g同样l承了ViewPartQ不同的是实C三个接口QRunnable,IResourceChangeListener ,IResourceDeltaVisitor。其中的Runnable大家应该很熟(zhn):(x)多线E的接口。而IResourceChangeListener和IResourceDeltaVisitor是Eclipsepȝ中的资源接口Q这里的资源是指Eclipse中的目或文件等。在下面q行NoticeView插g的过E中你可以通过d、打开、删除项目或文g来触发这两个接口中的事gQƈ在我们的观察H口中显C相关信息?/P>
在程序中比较奇怪部分的是在resourceChanged()函数里面Qƈ没有象大家想象的那样直接调用label.setText()Ҏ(gu)来显CZ息,而是调用了disp.syncExec(this)Q其中的disp是Displaycd的对象。这是因为resourceChanged()Ҏ(gu)q行的线E和lable所在插件运行的EclipseȝEƈ不是同一个线E,如果直接调用label.setText()Ҏ(gu)Q会(x)抛出一个异常?/P>
下面q需要对目中的plugin.xmlq行一些改动,主要是加上扩展点声明:(x)
|
q个xml文g和W(xu)elcome插g的plugin.xml非常接近Q这里就不做q多的说明了?/P>
要运行这个插Ӟ可以直接用Eclipse中的q行按钮Q因个项目是一个Plug-in ProjectQ此旉目会(x)自动以Run-time Workbench方式q行。运行后Q会(x)生成一个和当前Eclipse完全一致的q_Q在q个q_上可以直接运行NoticeView插gQ查看这个插件到底会(x)执行什么功能,也可以用直接Run-time Workbench方式调试插g。这里省M安装插gQ重启动Eclipse{过E,可以看到用PDE开发插件的q程比直接用Java开发环境简z了很多Q?/P>
Eclipse的开发不仅仅限于插g的开发,它还可以取代Java中的标准SwingQ进行基于Java的独立应用程序GUI开发。它带来的好处是显而易见的Q高速,资源占用低,跨^収ͼ代码开放,有大公司的支持等{?/P>
׃Eclipse目前q在开发阶D,W者在用它调试E序时发现有些性能q不是十分的E_Q一些地方会(x)遇到奇怪的问题Q要求用者能想一些办法解冟뀂不q,以现在Eclipse的开发速度Q相信过不了多久Q它的各U功能会(x)逐步完善。目前Eclipse虽然有种U不I但瑕不掩玉,W者对Eclipse的M印象q是非常不错的,q行速度Q资源占用都要好于IVJQ操作v来也大多手Q而且即在现阶段也很有意外退出等重大的Bug发生Q希望未来的Eclipse能真正达到IVJ的功能,VisualCafe的速度Q成为广大程序员开发Y件的一大利器!
Q?xml version="1.0" encoding="UTF-8"?Q?BR>Q?eclipse version="3.0"?Q?BR> QpluginQ?BR> Qextension point="org.eclipse.ui.views"Q?BR> Qcategory id="com.developer.superview.category1" name="Developer.com"/Q?BR> Qview category="com.developer.superview.category1" class="com.developer.superview.Superview" id="com.developer.superview.view1" name="Superview"/Q?BR> Q?extensionQ?BR> Q?pluginQ?/TD> |
![]() |
package com.developer.superview; import org.eclipse.jface.viewers.ArrayContentProvider; public class SuperView extends ViewPart { public void createPartControl(Composite parent) { public void setFocus() { } |
![]() |
package com.developer.superview; import org.eclipse.swt.graphics.Image; import org.eclipse.jface.resource.ImageDescriptor; public class SuperView extends ViewPart { public void createPartControl(Composite parent) { public void setFocus() { |
![]() |
Z插g的体pȝ?/SPAN>
Eclipse q_?IBM 向开发源码社区捐赠的开发框Ӟ它之所以出名ƈ不是因ؓ(f) IBM 宣称投入开发的资金L ?4 千万元 ?而是因ؓ(f)如此巨大的投入所带来的成果:(x)一个成熟的、精心设计的以及(qing)可扩展的体系l构。Eclipse 的h(hun)值是它ؓ(f)创徏可扩展的集成开发环境提供了一个开放源码^台。这个^台允怓Q何h构徏与环境和其它工具无缝集成的工兗?/P>
工具?Eclipse 无缝集成的关键是 插g。除了小型的q行时内怹外,Eclipse 中的所有东襉K是插件。从q个角度来讲Q所有功能部仉是以同等的方式创建的。从q个角度来讲Q所有功能部仉是以同等的方式创建的?
但是Q某些插件比其它插g更重要些。Workbench ?Workspace ?Eclipse q_的两个必备的插g ?它们提供了大多数插g使用的扩展点Q如?1 所C。插仉要扩展点才可以插入,q样它才能运行?/P>
?1. Eclipse Workbench ?WorkspaceQ必备的插g支持
Workbench lg包含了一些扩展点Q例如,允许(zhn)的插g扩展 Eclipse 用户界面Qɘq些用户界面带有菜单选择和工h按钮Q请求不同类型事件的通知Q以?qing)创建新视图。Workspace lg包含了可以让(zhn)与资源Q包括项目和文gQ交互的扩展炏V?/P>
当然Q其它插件可以扩展的 Eclipse lgq只有 Workbench ?Workspace。此外,q有一?Debug lg可以让?zhn)的插件启动程序、与正在q行的程序交互,以及(qing)处理错误 ?q是构徏调试器所必需的。虽?Debug lg对于某些cd的应用程序是必需的,但大多数应用E序q不需要它?/P>
q有一?Team lg允许 Eclipse 资源与版本控制系l(VCSQ交互,但除非?zhn)正在构?VCS ?Eclipse 客户机,否则 Team lgQ就?Debug lg一P不会(x)扩展或增强它的功能?/P>
最后,q有一?Help lg可以让?zhn)提供应用E序的联机文档和与上下文敏感的帮助。没有h?x)否认帮助文档是专业应用E序必备的部分,但它q不是插件功能的必要部分?/P>
上述每个lg提供的扩展点都记录在 Eclipse Platform Help 中,该帮助在 Platform Plug-in Developer 指南的参考部分中。乍一看,其?API 参考大全的 Workbench 部分Q一开始会(x)令h望而却步。我们不?x)深入了解众多可用扩展点的详l信息,而只是粗略地看一个简单插件及(qing)其组件?/P>
插g?/SPAN>
创徏插g最单的Ҏ(gu)是?Plug-in Development EnvironmentQPDEQ。PDE ?Java Development ToolingQJDTQIDE ?Eclipse 的标准扩展。PDE 提供了一些向g帮助创徏插gQ包括我们将在这里研I的“Hello, world”示例?/P>
?Eclipse 菜单Q选择 File=>New=>OtherQ或?Ctrl-NQ,然后选择 Select 对话框左边的 Plug-in Development 向导。在 Select 对话框的双Q选择 Plug-in Project。按 Next。在下一屏上Q输入项目名Uͼ我用了 com.example.hello
。再ơ按 Next。在下一屏上Q请注意Q插件标识就与项目名U相同。用项目名UC为插件标识可以将该插件与另一个插件的名称发生冲突的机?x)减到最。再按一?Next。下一屏让(zhn)选择是手工创建初始插件代码,q是q行代码生成向导。保留代码生成向导的~省选项Q选择“Hello, World”,然后?NextQ如?2 所C?
下一屏要求一些附加信息。请注意q一屏上的信息:(x)它包含了插g名称、版本号、提供者名U和cd。这些是关于插g的重要信息,我们在E后研究。可以接受向导提供的~省倹{按 Next。在下一屏幕上,接受包名、类名和消息文本的缺省倹{选择“Add the action set to the resource perspective”复选框。按 Finish?/P>
如果接到通知Q向导需要启用某些其它插件才能完成,那么?OK?/P>
q一?x)儿Q向导将完成Q而在(zhn)的工作Z会(x)有一个新的项目,名ؓ(f) com.example.hello
Q如?3 所C?
?3. PDE 透视图:(x)Welcome to Hello Plug-in
?Package Explorer 中,工作台的左边是向导创建的一些东西的概述。大多数w不引人关注:(x)包括目c\径中的许?.jar
文gQ这些包括插件和 Java q行时所需?Eclipse c)、一个图标文件夹Q包含了工具栏按钮的囑ŞQ,以及(qing) build.properties
文gQ包含自动构本所使用的变量)?
q里最有意思的东西?src 文g夹,它包含了插g?plugin.xml 文g的源代码 ?plug-in.xml 是插件的清单文g。我们将先查?plugin.xml?/P>
插g清单文g
插g清单文g plugin.xml 包含?Eclipse 插仉成到框架所使用的描qC息。缺省情况下Q当W一ơ创建插件时Q会(x)在清单编辑器区域中打开 plugin.xml。编辑器底部的选项卡让(zhn)可以选择关于插g的不同信息集合。Welcome 选项卡显CZ消息“Welcome to Hello Plug-In”,q且要讨Z所使用的模板和关于使用 Eclipse 实现插g的提C。选择“Source”选项卡可以让(zhn)查?plugin.xml 文g的完整源代码?/P>
让我们看看插件清单文件的各个部分。首先是关于插g的常规信息,包括它的名称、版本号、实现它的类文g的名U和 .jar
文g名?
|
接着Q列Z我们的插件所需的插Ӟ(x)
清单 2. 插g清单文g ?必需的插?/B>
|
列出的第一个插?org.eclipse.core.resources
是工作区插gQ但实际上我们的插gq不需要它。第二个插g org.eclipse.ui
是工作台。我们需要工作台插gQ因为我们将扩展它的两个扩展点,正如后面?extension 标记所指出的?
W一?extension 标记拥有点属?org.eclipse.ui.actionSets
。操作集合是插gd到工作台用户界面的一l?基?/I>?卻I菜单、菜单项和工h。操作集合分l了基|q样用户可以更方便地理它们。例如,我们?Hello 插g的菜单和工具栏项出现在 Resource 透视图中Q因为当在运行代码生成向导时Q我们做了这L(fng)选择。如果用戯更改它,可以使用 Window=>Customize Perspective菜单选项从要?Resource 透视图中昄的项中除厠ZSample Action Set”?
操作集合包含了两个标讎ͼ(x) menu 标记Q描q菜单项应该出现在工作台菜单的什么位|,以及(qing)如何出现Q和 action 标记Q描q它应该做什么)?其?action 标记标识了执行操作的cR注Q这个类不是上面列出的插件类?
清单 3. 操作集合
|
许多菜单和操作属性的目的相当明显 ?例如Q提供工hC文本和标识工具栏项的图形。但q要注意 action 标记中的 menubarPath
Q这个属性标识了 menu 标记中定义的哪个菜单调?action 标记中定义的操作。有兌个和其它工作台扩展点的详l信息,请参?Platform Plug-in Developer GuideQ尤其是“Plugging into the workbench”章节(可以?Eclipse 的帮助菜单中获取该指南)?
׃我们选择了将插gd?Resource 透视图,于是生成了第二个 extension 标记。这个标C(x)D?Eclipse W一ơ启动ƈ装入我们的插件时Q将插gd?Resource 透视图?/P>清单 4. extension 标记
|
如果忽略q最后一?extensionQ用户就需要?Window=>Customize Perspective插件添加到 ResourceQ或其它Q透视图?
插g源代?/SPAN>
代码生成向导生成了两?Java 源文Ӟ打开 PDE Package Explorer 中的 src 文g夹就可以看到它们。第一个文?HelloPlugin.java
是插件类Q它l承?AbstractUIPlugin
抽象cR?HelloPlugin
负责理插g的生命周期,在更为扩展的应用E序中,它负责维护诸如对话框讄和用户首选项{内宏V?HelloPlugin
要做的事p么多Q?
|
W二个源文g SampleAction.java
包含的类执行在清单文g的操作集合中指定的操作?SampleAction
实现?IWorkbenchWindowActionDelegate
接口Q它允许 Eclipse 使用插g的代理,q样不是在万不得已的情况下,Eclipse 无需装入插gQ这优化工作在装入插件时发生内存和性能斚w的问题降到最低)?IWorkbenchWindowActionDelegate
接口Ҏ(gu)使插件可以与代理q行交互Q?
|
q行和调试插?/SPAN>
当开?Eclipse 的插件时Q必d?Eclipse q用新的插g重新启动它以便进行测试和调试Q这很笨拙。幸好,Eclipse PDE 提供了一个自托管Qself-hostedQ的开发环境,它让(zhn)无需插件安装在工作台的单独实例中即可运行?/P>
要运?Hello 插gQ选择 Run=>Run As=>Run-time Workbench来启动另一?Workbench 实例Q而该实例d了插件的菜单选项和工hQ如?5 所C?
我们可以通过单击工具栏按钮或从“Sample Menu”菜单激zL件。Q何一U方法都?x)生成一个框Q其标题是“Hello Plug-in”,内容是“Hello, Eclipse world”,以及(qing)一?OK 按钮Q按该按钮可以关闭这个框?/P>
通过选择 Run=>Debug As=>Run-time WorkbenchQ按cM的方法调试插件。这ơ,当插件在W二个工作台实例中运行时Q我们可以在最初的工作C单步执行源代码,以及(qing)(g)查变量等?
一旦插件经q测试ƈ准备发布Q我们就需要将它适当打包Q以便在 Eclipse 中安装?/P>
打包插g
Eclipse 在启动时?x)查看其插g目录来确定要装入哪些插g。要安装插gQ我们需要在插g目录中创Z个子目录Qƈ程序文件和清单文g复制到那里。徏议目录名U能表示插g的标识,q且后面跟下划线和版本号Q但是这U做法不是必需的。假?Eclipse 安装?C:\eclipse 中;我们要创Z个目录:(x)
C:\eclipse\plugins\com.example.hello_1.0.0.
按照 Java E序的标准,我们的程序文仉要归档到 .jar
文g??我们的插件清单文Ӟ(zhn)也许记得它包含q个:(x)
|
要创?hello.jar
文gQ我们可以通过H出昄目名称Qƈ?Eclipse 菜单选择 File=>ExportQ以导出插g文g。选择 JAR 文g作ؓ(f)导出方式Q按 NextQ然后浏览到我们为它创徏的目录。下一步,我们q需要将 plugin.xml 文g复制到这个目录。也可以使用 File=>Export菜单选项Q但误C选择 File System 作ؓ(f)导出目的圎ͼ?
q就是安装插件所需的全部操作,但?zhn)需要停止ƈ重新启动 EclipseQ以便能识别q个新的插g。从帮助菜单中选择“About Eclipse Platform”,可以扑ֈ关于已安装插件的信息Q包括版本号。在出现的屏q上有一个按钮是 Plug-in DetailsQ向下滚动列表来L Hello 插g?qing)其版本受?/P>
更新插g版本
在目录名UC包含版本L(fng)目的是允许在同一台机器上共存某个插g的多个版本(每次只装入一个版本)。我们可以通过创徏一?Hello 插g的已更新版本来看看这是如何工作的Q例如,?plugin.xml 文g中的版本hҎ(gu)?.0.1”,然后?SampleAction.java
中的文本更改成“New and improved Hello, Eclipse world”。从 Eclipse 菜单中选择 Project=> Rebuild All。下一步,项目文件以 JAR 形式导出到新的插件目录,例如Q?com.example.hello_1.0.1
。将修订q的 plugin.xml 文g复制到同一个目录中。当停止q新启?Eclipse Ӟ只会(x)装入已更新的插g?
插g片段和功能部?/SPAN>
Eclipse 由插件组成,但在开?Eclipse 的插件时Q还要慎重考虑另外两个U别的组??插g片段和功能部件?/P>
插g 片段Q如名称所暗示的)是完整插件的l成部分 ?目标插g。片D|供的功能与目标插件的功能合ƈ。片D可以用于将插g本地化成各种语言Q在无需形成一个全新发行版的情况下Q以增量形式功能部件添加到现有插gQ或者提供特定于q_的功能。在许多斚wQ片D与插g一栗主要的区别是片段没有插gc??片段的生命周期由其目标插件管理。此外,片段的清单文件叫?fragment.xmlQ它列出了目标插件的标识和版本号Q以?qing)片D늚标识和版本号?
另一斚wQ插?功能部gҎ(gu)不包含编码。在 Eclipse 体系l构术语中,功能部g是将一l相x件打包到完整的品中。例如,JDT 是包含了?Java ~辑器、调试器和控制台q样的插件的功能部g。名?feature.xml 的清单文件描qC一个功能部件归档文件。在其中Q该清单文g包含了对该功能部件所包含的插件和其它资源的引用、关于如何更新该功能部g的信息、版权信息和许可证信息?
?Eclipse 中, d能部?/I>讄?Eclipse q_的外观。主功能部g旨在定诸如l予 Eclipse 其n份的闪屏和其它特征之cȝ东西。Eclipse 只允怸个主功能部g。用q种方式Q通过创徏一l插Ӟ它们打包到功能部g中,q且使这个功能部件成Z功能部gQ就可以重新创徏 Eclipse 的品牌,q将它用于创建全C不同的品。如果从 Eclipse.org 下蝲Q缺省主功能部g?eclipse.org.platform
?
后箋步骤
在插件的介绍里我们只是稍微了解一些插件的必要用法。学?fn)插件的更多知识的最?jng)_考资料是 Plug-in Developer's GuideQ可以从 Eclipse 中的帮助菜单中获得该指南。该文档包含了编E指南、Eclipse API 和插件扩展点的参考大全、Eclipse.org 上可用的~程CZ的指南,以及(qing)常见问题列表。另一个优U参考资料是 Eclipse 本n的源代码。根据?zhn)的兴,?zhn)也许想要查找一些示例,以了解不同工作台功能部gQ如视图和编辑器Q是如何扩展的,或者如何?SWTQEclipse 囑Ş APIQ。此外,下面?参考资?/A>可以帮助(zhn)学到更多知识?
public class InvokatronDocument
extends Properties
{
public static final String PACKAGE = "package";
public static final String SUPERCLASS = "superclass";
public static final String INTERFACES = "interfaces";
}
String package =
document.getProperty(InvokatronDocument.PACKAGE);
public class InvokatronWizard extends Wizard
implements INewWizard {
private InvokatronWizardPage page;
private InvokatronWizardPage2 page2;
private ISelection selection;
public InvokatronWizard() {
super();
setNeedsProgressMonitor(true);
ImageDescriptor image =
AbstractUIPlugin.
imageDescriptorFromPlugin("Invokatron",
"icons/InvokatronIcon32.GIF");
setDefaultPageImageDescriptor(image);
}
public void init(IWorkbench workbench,
IStructuredSelection selection) {
this.selection = selection;
}
public void addPages() {
page=new InvokatronWizardPage(selection);
addPage(page);
page2 = new InvokatronWizardPage2(
selection);
addPage(page2);
}
public boolean performFinish() {
//First save all the page data as variables.
final String containerName =
page.getContainerName();
final String fileName =
page.getFileName();
final InvokatronDocument properties =
new InvokatronDocument();
properties.setProperty(
InvokatronDocument.PACKAGE,
page2.getPackage());
properties.setProperty(
InvokatronDocument.SUPERCLASS,
page2.getSuperclass());
properties.setProperty(
InvokatronDocument.INTERFACES,
page2.getInterfaces());
//Now invoke the finish method.
IRunnableWithProgress op =
new IRunnableWithProgress() {
public void run(
IProgressMonitor monitor)
throws InvocationTargetException {
try {
doFinish(
containerName,
fileName,
properties,
monitor);
} catch (CoreException e) {
throw new InvocationTargetException(e);
} finally {
monitor.done();
}
}
};
try {
getContainer().run(true, false, op);
} catch (InterruptedException e) {
return false;
} catch (InvocationTargetException e) {
Throwable realException =
e.getTargetException();
MessageDialog.openError(
getShell(),
"Error",
realException.getMessage());
return false;
}
return true;
}
private void doFinish(
String containerName,
String fileName,
Properties properties,
IProgressMonitor monitor)
throws CoreException {
// create a sample file
monitor.beginTask("Creating " + fileName, 2);
IWorkspaceRoot root = ResourcesPlugin.
getWorkspace().getRoot();
IResource resource = root.findMember(
new Path(containerName));
if (!resource.exists() ||
!(resource instanceof IContainer)) {
throwCoreException("Container \"" +
containerName +
"\" does not exist.");
}
IContainer container =
(IContainer)resource;
final IFile iFile = container.getFile(
new Path(fileName));
final File file =
iFile.getLocation().toFile();
try {
OutputStream os =
new FileOutputStream(file, false);
properties.store(os, null);
os.close();
} catch (IOException e) {
e.printStackTrace();
throwCoreException(
"Error writing to file " +
file.toString());
}
//Make sure the project is refreshed
//as the file was created outside the
//Eclipse API.
container.refreshLocal(
IResource.DEPTH_INFINITE, monitor);
monitor.worked(1);
monitor.setTaskName(
"Opening file for editing...");
getShell().getDisplay().asyncExec(
new Runnable() {
public void run() {
IWorkbenchPage page =
PlatformUI.getWorkbench().
getActiveWorkbenchWindow().
getActivePage();
try {
IDE.openEditor(
page,
iFile,
true);
} catch (PartInitException e) {
}
}
});
monitor.worked(1);
}
private void throwCoreException(
String message) throws CoreException {
IStatus status =
new Status(
IStatus.ERROR,
"Invokatron",
IStatus.OK,
message,
null);
throw new CoreException(status);
}
}
public class InvokatronWizardPage2 extends WizardPage {
private Text packageText;
private Text superclassText;
private Text interfacesText;
private ISelection selection;
public InvokatronWizardPage2(ISelection selection) {
super("wizardPage2");
setTitle("Invokatron Wizard");
setDescription("This wizard creates a new"+
" file with *.invokatron extension.");
this.selection = selection;
}
private void updateStatus(String message) {
setErrorMessage(message);
setPageComplete(message == null);
}
public String getPackage() {
return packageText.getText();
}
public String getSuperclass() {
return superclassText.getText();
}
public String getInterfaces() {
return interfacesText.getText();
}
public void createControl(Composite parent) {
Composite controls =
new Composite(parent, SWT.NULL);
GridLayout layout = new GridLayout();
controls.setLayout(layout);
layout.numColumns = 3;
layout.verticalSpacing = 9;
Label label =
new Label(controls, SWT.NULL);
label.setText("&Package:");
packageText = new Text(
controls,
SWT.BORDER | SWT.SINGLE);
GridData gd = new GridData(
GridData.FILL_HORIZONTAL);
packageText.setLayoutData(gd);
packageText.addModifyListener(
new ModifyListener() {
public void modifyText(
ModifyEvent e) {
dialogChanged();
}
});
label = new Label(controls, SWT.NULL);
label.setText("Blank = default package");
label = new Label(controls, SWT.NULL);
label.setText("&Superclass:");
superclassText = new Text(
controls,
SWT.BORDER | SWT.SINGLE);
gd = new GridData(
GridData.FILL_HORIZONTAL);
superclassText.setLayoutData(gd);
superclassText.addModifyListener(
new ModifyListener() {
public void modifyText(
ModifyEvent e) {
dialogChanged();
}
});
label = new Label(controls, SWT.NULL);
label.setText("Blank = Object");
label = new Label(controls, SWT.NULL);
label.setText("&Interfaces:");
interfacesText = new Text(
controls,
SWT.BORDER | SWT.SINGLE);
gd = new GridData(
GridData.FILL_HORIZONTAL);
interfacesText.setLayoutData(gd);
interfacesText.addModifyListener(
new ModifyListener() {
public void modifyText(
ModifyEvent e) {
dialogChanged();
}
});
label = new Label(controls, SWT.NULL);
label.setText("Separated by ','");
dialogChanged();
setControl(controls);
}
private void dialogChanged() {
String aPackage = getPackage();
String aSuperclass = getSuperclass();
String interfaces = getInterfaces();
String status = new PackageValidator().isValid(aPackage);
if(status != null) {
updateStatus(status);
return;
}
status = new SuperclassValidator().isValid(aSuperclass);
if(status != null) {
updateStatus(status);
return;
}
status = new InterfacesValidator().isValid(interfaces);
if(status != null) {
updateStatus(status);
return;
}
updateStatus(null);
}
}
public class InterfacesValidator implements ICellEditorValidator
{
public String isValid(Object value)
{
if( !( value instanceof String) )
return null;
String interfaces = ((String)value).trim();
if( interfaces.equals(""))
return null;
String[] interfaceArray = interfaces.split(",");
for (int i = 0; i < interfaceArray.length; i++)
{
IStatus status = JavaConventions
.validateJavaTypeName(interfaceArray[i]);
if (status.getCode() != IStatus.OK)
return "Validation of interface " + interfaceArray[i]
+ ": " + status.getMessage();
}
return null;
}
}