熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java開源技術 >> 正文

關於Eclipse插件開發之定制向導(圖)

2022-06-13   來源: Java開源技術 

  Invokatron的歷史
  
  首先我們詳細說明一下Invokatron本身在前面的文章中我們討論過Invokatron是一個生成Java代碼的的圖形工具你可以簡單地通過拖放操作建立類的方法拖入的方法被編輯的方法(也就是插件)調用我們將讓數據來驅動應用程序的設計在後面一篇文章中我們將開發這個GUI現在我們需要做的是找到插件將輸入和存儲的重要數據它通常被稱為應用程序的模型(model)在設計這個系統的時候我們需要考慮下面一些內容
  
  · 哪些細節數據需要保存?
  
  · 這些數據在內存中用什麼來表現?POJOJavaBean還是EJB?
  
  · 這些數據的存儲格式是怎樣的?數據庫表XML文件屬性文件還是串行二進制文件?
  
  · 輸入數據的方式有哪幾種?用新建文件向導還是在文檔屬性頁面上使用彈出對話框用編輯器繪制在文本編輯器中輸入的其它向導?
  
  在我們繼續工作之前必須回答這些問題不可能有適合所有項目的答案它完全依賴於你的需求在我們的例子中我做出了一些隨意的可能有問題的決定如下所示
  
  · 一個Java類它包含類名程序包超類(superclass)和實現接口我們以它為基礎在後面的文章中添加更多數據
  
  · 我將把數據表現為擴展Properties類的類它建立了編輯器的文檔類
  
  · 我將使用的格式是屬性文件很容易使用Properties類來分析它
  
  · 在新建文件向導中我將先尋找數據接著讓用戶改變屬性窗口或文本編輯器中的數據這個步驟將在下一篇文章中完成
  
  Document(文檔)類
  
  下一步是編寫文檔類建立一個新程序包(invokatronmodel)和一個新類(InvokatronDocument)下面是我們的文檔類的開頭
  
  public class InvokatronDocument
  extends Properties
  {
  public static final String PACKAGE = package;
  public static final String SUPERCLASS = superclass;
  public static final String INTERFACES = interfaces;
  }
  
  使用Properties類可以更簡單地分析和保存我們的數據Getter和 setter不是必須的但是如果你想要也可以加上它們這個類還沒有完成我們將添加一個接口在後面的部分中Eclipse需要使用它
  
  有了這個類之後我們要獲取一個屬性就非常簡單了
  
  String package =documentgetProperty(InvokatronDocumentPACKAGE);
  
  定制向導
  
  請看一看前面的文章中所出現的向導你應該記得我們可以通過點擊(我們自己添加的)工具條按鈕或者菜單項來訪問它是它的界面
  
 
  圖舊的向導

  
  它只有一個頁面右上角沒有圖片我們想輸入更多的信息並提供一個很好的圖片換句話說我們希望定制這個向導
  
  我們來分析一下這個向導請打開InvokatronWizardjava文件請注意這個類是如何擴展Wizard並實現INewWizard接口的你應該理解它裡面的很多方法為了定制向導我們簡單地調用或重載其中的某些方法下面是一些重要的方法
  
  生命周期方法
  
  我們應該重載這些方法把初始化和析構(destruction)代碼插入向導中
  
  · Constructor(構造函數)向導實例化的時候在Eclipse給它傳遞信息之前調用向導的一般初始化實現通常你希望調用美化方法(後面有描述)並設置對話框的默認值
  
  · init(IWorkbench workbench IStructuredSelection editorSelection): Eclipse調用它為向導提供工作台的信息請重載它保存IWorkbench和對象的句柄供以後使用如果它是一個編輯器向導而不是新向導我們最好把當前的編輯器選項作為第二個參數
  
  · dispose()Eclipse調用它執行清理工作重載它來清除向導使用的資源
  
  · finalize()清除代碼可能使用dispose()代替
  
  美化方法
  
  這些方法都是用於裝飾向導窗體的
  
  · setWindowTitle(String title)設置窗體的標題行字符串
  
  · setDefaultPageImageDescriptor(ImageDescriptor image)用於提供顯示在向導的所有頁面右上方的圖片
  
  · setTitleBarColor(RGB color)指定標題欄用什麼顏色
  
  按鈕方法
  
  這些方法控制著向導按鈕的實用性和行為
  
  · boolean canFinish()重載它用於指定Finish(完成)按鈕是否激活(根據向導的狀態)
  
  · boolean performFinish()重載它來實現向導的根本的業務邏輯如果向導沒有完成(錯誤的條件)就返回false
  
  · boolean performCancel()重載它在用戶點擊Cancel(取消)按鈕的時候進行清除操作如果向導不能終止則返回false
  
  · boolean isHelpAvailable()重載它用於指定Help(幫助)按鈕是否可視
  
  · boolean needsPreviousAndNextButtons()重載它來指定Previous(前一步)和Next(後一步)按鈕是否可視
  
  · boolean needsProgressMonitor()重載它來指定進度條部件是否可視當點擊Finish按鈕調用performFinish()方法的時候它就會出現
  
  頁面方法
  
  這些方法控制著頁面的外觀
  
  · addPages()向導顯示的時候調用重載它給向導插入新頁面
  
  · createPageControls(Composite pageContainer)Eclipse調用它來實例化所有的向導頁面(用前面的addPages()方法已經添加的頁面)重載它給向導添加持續可視的窗體小部件(除頁面之外的部件)
  
  · IWizardPage getStartingPage()重載它來檢測哪個頁面是向導的第一個頁面
  
  · IWizardPage getNextPage(IWizardPage nextPage)在默認情況下點擊Next按鈕將進入addPages()所提供的數組中的下一個頁面你可能希望根據用戶選擇進入不同的頁面重載它來計算後一個頁面
  
  · IWizardPage getPreviousPage(IWizardPage previousPage)與getNextPage()類似用於計算前一個頁面
  
  · int getPageCount()檢索addPages()添加的頁面的數量在典型情況下你不必重載它除非你希望顯示頁面的數量和形式
  
  其它有用的方法
  
  這些都是有用的輔助方法
  
  · setDialogSettings(IDialogSettings settings)你可以載入對話框的狀態並通過在init()中調用這個方法來設置這些值在典型情況下這些設置可以作為向導字段的默認值請查看DialogSettings類了解更詳細的信息
  
  · IDialogSettings getDialogSettings()當我們需要數據的時候就調用這個方法來檢索它在performFinish()的對話框的末尾你再次可以把數據保存到文件中
  
  · IWizardContainer getContainer()對於檢索Shell運行的後台線程刷新窗口等非常有用
  
  向導頁面方法
  
  你已經看到了向導是由一個或多個頁面組成的這些頁面擴展了WizardPage類並實現了IWizardPage接口為了定制單獨的頁面你必須了解很多方法下面是一些重要的方法
  
  · Constructor用於實例化頁面
  
  · dispose()重載它用於實現清除代碼
  
  · createControl(Composite parent)重載它來給頁面添加控件
  
  · IWizard getWizard()用於獲取父向導對象對於調用getDialogSettings()是有用處的
  
  · setTitle(String title)調用它來設置顯示在向導標題區域中的字符串
  
  · setDescription(String description)調用它來提供標題下面顯示的文本內容
  
  · setImageDescriptor(ImageDescriptor image)調用它來提供頁面右上方出現的圖片(用於代替默認的圖片)
  
  · setMessage(String message)調用它來顯示描述字符串下方的消息文本這些文本是用於警告或提示用戶的
  
  · setErrorMessage(String error)調用它來高亮度顯示描述字符串下方的消息文本它一般意味著向導不能繼續除非錯誤被修正
  
  · setPageComplete(boolean complete)如果為trueNext按鈕就可視
  
  · performHelp()重載它來提供內容敏感的幫助信息當點擊Help按鈕的時候向導會調用它
  
  編寫向導的代碼
  
  有了這些方法之後我們就能夠開發出具有極大的靈活性的向導了我們現在修改以前建立的Invokatron向導給它添加一個頁面來請求用戶輸入初始的文檔數據我們還給向導添加了一個圖片新代碼是粗體的
  
  public class InvokatronWizard extends Wizard
  implements INewWizard {
  private InvokatronWizardPage page;
  private InvokatronWizardPage page;
  private ISelection selection;
  
  public InvokatronWizard() {
  super();
  setNeedsProgressMonitor(true);
  ImageDescriptor image =AbstractUIPluginimageDescriptorFromPlugin(Invokatron icons/InvokatronIconGIF);
  setDefaultPageImageDescriptor(image);
  }
  
  public void init(IWorkbench workbenchIStructuredSelection selection) {
  thisselection = selection;
  }
  
  在構造函數中我們打開了進度條並設置了向導的圖片你可以下載並保存下面的圖片
  
 

  請把這個圖片保存在Invokatron/icons文件夾之下為了更容易載入這個圖片我們使用了便捷的AbstractUIPluginimageDescriptorFromPlugin()方法
  
  請注意你應該知道盡管這個向導是INewWizard類型的但是並非所有的向導都是用於建立新文檔的你可以參考其它一些資料來了解如何建立獨立的向導的信息
  
  下面是addPages()方法
  
  public void addPages() {
  page=new InvokatronWizardPage(selection);
  addPage(page);
  page = new InvokatronWizardPage(selection);
  addPage(page);
  }
  
  在這個方法中我們添加了一個新頁面(InvokatronWizardPage我們在後面編輯它下面是用戶點擊向導的完成按鈕的時候執行的一些方法
  
  public boolean performFinish() {
  //首先把所有的頁面數據保存在變量中
  final String containerName = pagegetContainerName();
  final String fileName =pagegetFileName();
  final InvokatronDocument properties = new InvokatronDocument();
  propertiessetProperty(InvokatronDocumentPACKAGEpagegetPackage());
  propertiessetProperty(InvokatronDocumentSUPERCLASSpagegetSuperclass());
  propertiessetProperty(InvokatronDocumentINTERFACESpagegetInterfaces());
  
  //現在調用完成(finish)方法
  IRunnableWithProgress op =new IRunnableWithProgress() {
  public void run(IProgressMonitor monitor)
  throws InvocationTargetException {
  try {
  doFinish(containerName fileNamepropertiesmonitor);
  } catch (CoreException e) {
  throw new InvocationTargetException(e);
  } finally {
  monitordone();
  }
  }
  };
  try {
  getContainer()run(true false op);
  } catch (InterruptedException e) {
  return false;
  } catch (InvocationTargetException e) {
  Throwable realException =egetTargetException();
  MessageDialogopenError(getShell()ErrorrealExceptiongetMessage());
  return false;
  }
  return true;
  }
  
  為了保存數據我們必須做一個後台事務該事務是由向導的容器(Eclipse工作台)來執行的並且必須實現IRunnableWithProgress接口包含(唯一)一個run()方法傳遞進來的IProgressMonitor允許我們報告事務的進度實際的數據保存工作在一個輔助方法(doFinish())中進行
  
  private void doFinish(String containerNameString fileName Properties properties
  IProgressMonitor monitor)
  throws CoreException {
  // 建立一個示例文件
  monitorbeginTask(Creating + fileName );
  IWorkspaceRoot root = ResourcesPlugingetWorkspace()getRoot();
  IResource resource = rootfindMember(new Path(containerName));
  if (!resourceexists() || !(resource instanceof IContainer)) {
  throwCoreException(Container \ + containerName + \ does not exist);
  }
  IContainer container =(IContainer)resource;
  final IFile iFile = containergetFile(new Path(fileName));
  final File file =iFilegetLocation()toFile();
  try {
  OutputStream os = new FileOutputStream(file false);
  propertiesstore(os null);
  osclose();
  } catch (IOException e) {
  eprintStackTrace();
  throwCoreException(Error writing to file + filetoString());
  }
  
  //確保項目已經刷新了該文件在Eclipse API 之外建立
  containerrefreshLocal(IResourceDEPTH_INFINITE monitor);
  
  monitorworked();
  
  monitorsetTaskName(Opening file for editing);
  getShell()getDisplay()asyncExec(new Runnable() {
  public void run() {
  IWorkbenchPage page =PlatformUIgetWorkbench()getActiveWorkbenchWindow()getActivePage();
  try {
  IDEopenEditor(pageiFiletrue);
  } catch (PartInitException e) {
  }
  }
  });
  monitorworked();
  }
  
  我們還做了很多工作
  
  · 我們檢索了自己希望保存文件的位置(用Eclipse的IFile類)
  
  · 我們還獲取了該File
  
  · 我們把屬性保存到了這個位置
  
  · 接著我們讓Eclipse工作台刷新項目這樣就可以顯示該文件了
  
  · 我們最後調度了一個事務它在以後執行這個事務包括在編輯器中打開那個新文件
  
  · 在整個過程中我們通過調用IProgressMonitor對象(它是作為參數傳遞進來的)的方法來提示用戶目前的進展情況
  
  最後一個方法是一個輔助的方法當該文件保存失敗的時候它在向導中顯示錯誤信息
  
  private void throwCoreException(String message) throws CoreException {
  IStatus status =new Status(IStatusERRORInvokatronIStatusOKmessagenull);
  throw new CoreException(status);
  }
  }
  
  向導可以捕獲CoreException異常接著可以把它所包含的Status對象顯示給用戶看向導不會被關閉
  
  編寫新的向導頁面的代碼
  
  下一步我們編寫InvokatronWizardPage它的整個類都是全新的
  
  public class InvokatronWizardPage extends WizardPage {
  private Text packageText;
  private Text superclassText;
  private Text interfacesText;
  
  private ISelection selection;
  
  public InvokatronWizardPage(ISelection selection) {
  super(wizardPage);
  setTitle(Invokatron Wizard);
  setDescription(This wizard creates a new+ file with *invokatron extension);
  thisselection = selection;
  }
  
  private void updateStatus(String message) {
  setErrorMessage(message);
  setPageComplete(message == null);
  }
  
  public String getPackage() {
  return packageTextgetText();
  }
  public String getSuperclass() {
  return superclassTextgetText();
  }
  public String getInterfaces() {
  return interfacesTextgetText();
  }
  
  上面的構造函數設置了頁面的標題(在標題欄下方高亮度顯示)和描述(在頁面標題的下方顯示)我們還有一些輔助方法 updateStatus處理頁面特定的錯誤信息的顯示如果沒有錯誤信息就意味著頁面完成了因此下一步按鈕就可以使用了還有數據字段內容的getter(獲取)方法下面是createControl()方法它建立了頁面的所有可視化組件
  
  public void createControl(Composite parent) {
  Composite controls =new Composite(parent SWTNULL);
  GridLayout layout = new GridLayout();
  controlssetLayout(layout);
  layoutnumColumns = ;
  layoutverticalSpacing = ;
  
  Label label =new Label(controls SWTNULL);
  labelsetText(&Package:);
  
  packageText = new Text(controlsSWTBORDER | SWTSINGLE);
  GridData gd = new GridData(GridDataFILL_HORIZONTAL);
  packageTextsetLayoutData(gd);
  packageTextaddModifyListener(
  new ModifyListener() {
  public void modifyText(ModifyEvent e) {
  dialogChanged();
  }
  });
  
  label = new Label(controls SWTNULL);
  labelsetText(Blank = default package);
  
  label = new Label(controls SWTNULL);
  labelsetText(&Superclass:);
  
  superclassText = new Text(controlsSWTBORDER | SWTSINGLE);
  gd = new GridData(GridDataFILL_HORIZONTAL);
  superclassTextsetLayoutData(gd);
  superclassTextaddModifyListener(new ModifyListener() {
  public void modifyText(ModifyEvent e) {
  dialogChanged();
  }
  });
  
  label = new Label(controls SWTNULL);
  labelsetText(Blank = Object);
  
  label = new Label(controls SWTNULL);
  labelsetText(&Interfaces:);
  
  interfacesText = new Text(controlsSWTBORDER | SWTSINGLE);
  gd = new GridData(GridDataFILL_HORIZONTAL);
  interfacesTextsetLayoutData(gd);
  interfacesTextaddModifyListener(
  new ModifyListener() {
  public void modifyText(ModifyEvent e) {
  dialogChanged();
  }
  });
  
  label = new Label(controls SWTNULL);
  labelsetText(Separated by );
  
  dialogChanged();
  setControl(controls);
  }
  
  為了編寫這段代碼你必須了解SWT(請你自己查看一些這方面的資料)基本上這個方法建立了標簽和字段並把它們放置到網格布局上字段發生改變的時候就調用dialogChanged()來驗證它的數據
  
  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);
  }
  
  }
  
  這個工作是在三個工具類PackageValidatorSuperclassValidator和 InterfacesValidator的幫助下完成的接下來我們編寫這些類
  
  驗證類
  
  驗證可以在插件的用戶輸入數據的任何部分中進行因此把驗證代碼放入可重復使用的類中是有意義的這樣就不用把它復制到多個位置下面是一個驗證類的例子
  
  public class InterfacesValidator implements ICellEditorValidator
  {
  public String isValid(Object value)
  {
  if( !( value instanceof String) )
  return null;
  
  String interfaces = ((String)value)trim();
  if( interfacesequals())
  return null;
  
  String[] interfaceArray = interfacessplit();
  for (int i = ; i < interfaceArraylength; i++)
  {
  IStatus status = JavaConventionsvalidateJavaTypeName(interfaceArray[i]);
  if (statusgetCode() != IStatusOK)
  return Validation of interface + interfaceArray[i] + : + statusgetMessage();
  }
  return null;
  }
  }
  
  其它的驗證類與它非常類似
  
  Eclipse類庫中的另外一個極好的類是JavaConventions它為我們驗證數據!它包含了很多驗證方法例如
  
  · validateJavaTypeName() 檢查類和接口的名稱
  
  · validatePackageName() 檢查程序包的名稱
  
  · validateFieldName() 檢查數據成員的名稱
  
  · validateMethodName() 檢查方法的名稱
  
  · validateIdentifierName() 檢查變量的名稱
  
  現在我們不需要ICellEditorValidator接口但是在以後的文章中我們是需要它的
  
  結果
  
  到目前為止我們擁有了一個可以工作的向導它擁有一張圖片和兩個頁面第二個頁面建立了原來的Invokatron文檔顯示了結果
  
 

  定制的向導
  
  閃亮的發明
  
  我們可以看到通常是數據驅動應用程序的外表(Presentation)也是很重要的丑陋的發明難以出售但是閃亮的發明可能容易出售但是數據是我們這些程序員實現的非常本質的東西
  
  在本文中我們首先決定了自己將處理的數據然後我們以定制向導的方式來獲取這些數據下一篇文章將繼續講解顯示的問題包括定制的編輯器和屬性頁面
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28130.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.