DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> Eclipse 任務列表
Eclipse 任務列表
編輯:XML詳解     

一年之後,BenoÎt 又回到了 XM (XSLT Make) 項目。他將介紹 Eclipse 平台的變化,並著手對 Eclipse 作一次較大的更新,使其與 XML 更緊密地集成在一起。首先他將考察一種簡單的界面增強,用戶經常提出這類請求,即支持問題和任務列表,更確切地說是支持做標記。正如您將看到的,您需要間接地使用這些列表。他還將考察 Eclipse 自身的資源管理,討論編寫在 Eclipse 和命令行中同樣也能運行的代碼的技術。

  在這個新的系列中,我將重溫 使用 XML 專欄的一位老朋友:XSLT Make 或 XM。它也可以算做使用 Eclipse 插件的一個理由。2001 年 7 月,我在 使用 XML 專欄中介紹的第一個項目就是 XM。它是一種輕量級的、價格低廉的、使用 XML 和 XSLT 發布文檔的工具。

  2002 年 10 月,我決定為 XM 工具添加圖形用戶界面。我沒有從頭開發整個界面,而是求助於剛剛出現的一種 IDE:Eclipse。之所以選擇 Eclipse,是因為它是可擴展的、用 Java 編寫的,並且提供了一個神奇的小部件庫。

  重溫 XM,使我有機會從兩個方面改進 Eclipse 集成:我將修正一個討厭的用戶界面限制(本文中),並重新編寫核心 XM 引擎,以便與 Eclipse 更好地集成。通過改進,我還將提高 Eclipse 的擴展性和功能。關於 XM 引擎的工作計劃,將在後面的兩篇文章中闡述。

  簡要的歷史回顧

  XM 已經存在一段時間了。從我的咨詢經驗和讀者的反映來看,它在很多項目中證明了自身的價值。比如,我使用 XM 作為一種教學工具,為客戶管理 Web 站點,並以 Html 和 PDF 格式發表了大量的文檔。

  XM 的優點

  開發 XM 的最初原因是使 XML 和 XSLT 的使用更方便。我需要一種簡單而有效地解決方案,依靠小型或中等大小的團隊維護 Web 站點。我知道 XML 和 XSLT 提供了一個很好的基礎,但當時我沒有找到合適的工具。最後我卷起袖子自己做了一個這樣的工具。2001 年那時出現的工具不是太簡單(只能用於單個文件,而不是整個網站),就是太復雜(以大型團隊為目標)。

XM 的功能非常強大(我曾經將其用於包含數千頁面的項目),但有足夠簡單,能適應中小型團隊的需要。

  XM 有兩個最重要的特性:

  它是開箱即用的,不需要准備復雜的腳本,也不需要編寫高級的配置文件。使用 XM,只要將文檔放在一個目錄中,把樣式表放在另一個目錄中, 好了,這樣就可以發布文檔了。

  它生成靜態的站點,同時又提供了動態站點的大多數管理優勢。比方說,修改站點的布局只需要編輯一個樣式表即可 。

  第二點可能更容易引起爭議,但根據我的經驗,維護靜態站點的工作量更小,效率也更高。一些站點需要結合靜態和動態網頁,但是以靜態方式為主維護站點可以避免很多問題:使用的軟件包更少,因而減少了失效的機會。此外,因為可以使用更成熟的緩沖技術,站點的響應速度也更快。

  從 XM 的角度看

  兩年之中情況發生了很多變化。現在,有大量的開源項目能夠滿足您的需要。我曾經用過其中的一些項目,雖然不敢說有廣泛的經驗,但確實發現其中一些項目的功能比 XM 更強大,但沒有一種像 XM 那樣易於使用。

  Eclipse 平台也發生了根本的變化。現在,Eclipse 是最受人矚目的開源 IDE 之一,擁有上千種插件。更重要的是,文檔得到了更新,提供了更多的例子。我還記得當時和源代碼與調試器搏斗以便獲得特定效果的情景,因為當時還沒有文檔,那種情形不復存在了。

  從技術上說,Eclipse 項目從 2.0 發展到了 3.0。新的 API 預計將為今後的很長時間奠定基礎。所幸的是,不同的版本在很大程度上都是兼容的(事實上為 Eclipse 2.0 編寫的 XM 插件在 3.0 中也能很好地工作),但有些變化不是向後兼容的。一個好的辦法是清理代碼,盡可能地使用新的 API。

本系列文章有兩個目標:

  改進 Eclipse 集成。雖然 Eclipse 的功能很齊全,但原來的插件還有一些粗糙之處。通過與 Eclipse 資源管理更緊密地集成在一起,我希望能夠稍微緩解一下不足之處。

  重寫核心引擎。我曾經在很多項目中使用過 XM,在一些項目中遇到了核心引擎最初設計中的一些局限,不得不臨時改變實現。現在是時候將這些修改加入到項目中了。

  Eclipse 資源管理

  在以前的專欄文章中,我曾多次提到,Eclipse 不僅僅是一種 IDE。最好將其看作是構建 IDE 的平台。Eclipse 可以歸結為管理插件的一個系統。它提供了諸如加載插件、管理插件之間的聯系和依賴性、管理插件之間的接口(通過擴展點)等服務。

  顯然,一些插件提供的服務是每個應用程序都需要的,所以可以將它們作為核心的一部分。部件庫 SWT 就是其中之一。另一些插件,如 XM 插件,具有更強的專用性,則由用戶在需要的時候安裝。

  還有一種核心服務是資源管理,該服務由 org.eclipse.core.resources 插件提供。對於 Eclipse 來說,工作區之下的一切都是資源。資源的基本接口是 IResource(非常明確)。最常用的後代有 IFile、 IFolder 和 IProject,分別代表文件、文件夾和項目。

  雖然有一定的關系,但 IResource 和 JDK 中的 File 對象實際上是兩碼事。JDK File 代表文件系統中的一個記錄,而 Eclipse IResource 在文件系統之上又添加了幾層抽象。首先,資源有屬性,屬性代表關於資源的信息,幫助插件處理資源。比如,插件可以把 <?XML-stylesheet?> 處理指令的內容作為屬性來進行緩沖。同時將數據緩沖在屬性中,這樣就避免了每次運行插件時都需要解析文件。屬性可以存儲在內存中(用戶退出編輯器時將丟失)或者持久存儲到文件系統中。

 此外,當添加、刪除或編輯資源時,資源和文件系統就不再同步。 IResource 記錄資源的狀態,並提供與文件系統同步的方法。更重要的是,Eclipse 可以通知插件資源和文件系統的變化。當資源與文件系統同步時,Eclipse 將傳遞給插件一個 delta,即上一次同步之後的變動列表。顯然,這樣就能夠進行智能構建,也就是說僅對修改過的資源進行重新編譯。

  記號和任務列表

  從用戶的觀點看,Eclipse 支持有兩個問題:XM 有自己的項目重建邏輯和錯誤報告邏輯。最終,這兩個問題表現為 XM 忽略了 Eclipse 的資源管理。

  我准備在本系列的後兩篇文章中討論構建過程,現在主要解決錯誤報告的問題。

  記號

  Eclipse 為構建人員和編譯人員提供了任務列表和問題列表來報告錯誤,如圖 1 所示。當用戶雙擊其中的錯誤項時,編輯器就會打開有問題的文件。不幸的是,編寫 XM 插件的第一個版本時,我沒有找到如何添加列表項的文檔,所以忽略了它。結果,該插件有自己的控制台,但不支持雙擊。


圖 1. 任務和問題列表
使用 XML: Eclipse 任務列表

  查看原圖(大圖)

  最後發現,向標准列表中添加消息並不難,但是不能直接添加。一開始,我試圖尋找一個任務列表對象,但是沒有發現添加列表項的方法。最後發現,無法添加或者至少無法 直接添加列表項。要添加錯誤消息,需要在資源上創建一個 記號(接口 IMarker)。從列表中刪除一個消息,也要從資源上去掉記號。列表會自動更新翻譯記號的變化。

createMarker() 方法用於創建記號。該方法以記號 ID 作為參數。平台中定義了以幾種標准的記號 ID:

  org.eclipse.core.resources.marker —— 記號層次結構的根。

  org.eclipse.core.resources.problemmarker —— 表示問題或錯誤消息,出現在問題列表中。

  org.eclipse.core.resources.taskmarker —— 表示待辦事項,出現在任務列表中。

  org.eclipse.core.resources.bookmark —— 表示文件,比如搜索結果。

  org.eclipse.core.resources.textmarker —— 表示文件的位置,比如出現錯誤的位置。

  定義插件專用的記號是一種不錯的選擇。新記號的 ID 在 plugin.XML 文件(與 Eclipse 中的其他聲明一樣)重定義。清單 1 顯示了一個記號聲明,定義了記號 ID( org.eclipse.core.resources.markers)的一個擴展。它還聲明了新的記號,這些記號分別從 problemmarker(顯示在問題列表中)和 textmarker(為了記錄行號)中繼承而來。將記號聲明為持久的是為了在會話之間保存這些記號。


清單 1. 記號聲明
<extension id="marker" 
      name="XM Message" 
      point="org.eclipse.core.resources.markers"> 
  <super type="org.eclipse.core.resources.problemmarker"/> 
  <super type="org.eclipse.core.resources.textmarker"/> 
  <persistent value="true"/> 
</extension> 

  用戶可以根據不同的條件過濾消息,比如問題的類型(警告、錯誤)、優先級和記號 ID。定義插件專用的記號可以幫助用戶對插件消息應用專門的過濾規則。

警告:Eclipse 有可能過濾掉插件消息。如果沒有看到 XM 的任何消息,應該看看這些消息是不是過濾掉了。要改變過濾器,請在任務列表或問題列表中單擊過濾器圖標,一定要選中 XM 記號。

  了解其中的竅門之後,集成到 XM 中並不難。從一開始,就通過 Messenger 接口將用戶界面抽象化了。Messenger 定義了核心需要報告錯誤或進程信息的方法。為了支持方法列表,只需要編寫新的 Messager 實現來創建適當的記號,如清單 2 所示。注意, begin() 方法將刪除所有的記號,以便在構建之前清除問題列表。


清單 2. 記號的 Messenger 實現
package org.ananas.xm.eclipse; 
import Java.text.MessageFormat; 
import org.eclipse.ui.IWorkbench; 
import org.ananas.xm.core.Filename; 
import org.ananas.xm.core.Location; 
import org.ananas.xm.core.Messenger; 
import org.eclipse.ui.IWorkbenchPage; 
import org.ananas.xm.core.XMException; 
import org.eclipse.swt.widgets.Display; 
import org.eclipse.ui.IWorkbenchWindow; 
import org.eclipse.core.resources.IMarker; 
import org.eclipse.core.resources.IProject; 
import org.eclipse.core.resources.IResource; 
import org.eclipse.core.runtime.CoreException; 
import org.eclipse.ui.views.markers.MarkerVIEwUtil; 
public class MessengerTaskList 
 implements Messenger, EclipseConstants 
{ 
 private IProject project = null; 
 private IWorkbench workbench = null; 
 private boolean noMarkerSoFar = true; 
 private static class ShowMarkerVIEw 
  implements Runnable 
 { 
  private IWorkbench workbench; 
  private IMarker marker; 
  public ShowMarkerVIEw(IWorkbench workbench,IMarker marker) 
  { 
   this.workbench = workbench; 
   this.marker = marker; 
  } 
  public void run() 
  { 
   IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); 
   if(window == null) 
   { 
    IWorkbenchWindow[] windows = workbench.getWorkbenchWindows(); 
    if(windows != null && Windows.length > 0) 
      window = Windows[0]; 
    else 
      return; 
   } 
   IWorkbenchPage page = window.getActivePage(); 
   if(page != null) 
    MarkerVIEwUtil.showMarker(page,marker,true); 
  } 
 } 
 public MessengerTaskList(IWorkbench workbench,IProject project) 
 { 
  if(null == project || null == workbench) 
   throw new NullPointerException("null argument in TaskListMessenger constructor"); 
  this.project = project; 
  this.workbench = workbench; 
 } 
 protected void addMarker(String msg,Location location,int severity,int priority) 
  throws XMException 
 { 
  IResource resource = null; 
  if(null == location || location.equals(Location.UNKNOWN)) 
   resource = project; 
  else 
   resource = (IResource)location.getFilename().ASPlatformSpecific(); 
  try 
  { 
   IMarker marker = resource.createMarker(MARKER_ID); 
   if(null != location && Location.UNKNOWN_POSITION != location.getLine()) 
     marker.setAttribute(IMarker.LINE_NUMBER,location.getLine()); 
   if(null != msg) 
     marker.setAttribute(IMarker.MESSAGE,msg); 
   marker.setAttribute(IMarker.SEVERITY,severity); 
   marker.setAttribute(IMarker.PRIORITY,priority); 
   if(noMarkerSoFar) 
     showMarkerVIEw(marker); 
   else 
     noMarkerSoFar = false; 
  } 
  catch(CoreException e) 
  { 
   throw new XMException(e,location); 
  } 
 } 
 public void error(XMException x) 
  throws XMException 
 { 
  addMarker(x.getMessage(),x.getLocation(),IMarker.SEVERITY_ERROR,IMarker.PRIORITY_NORMAL); 
 } 
 public void fatal(XMException x) 
  throws XMException 
 { 
  addMarker(x.getMessage(),x.getLocation(),IMarker.SEVERITY_ERROR,IMarker.PRIORITY_HIGH); 
 } 
 public void warning(XMException x) 
  throws XMException 
 { 
  addMarker(x.getMessage(),x.getLocation(),IMarker.SEVERITY_WARNING,IMarker.PRIORITY_LOW); 
 } 
 public boolean progress(Filename sourceFile,Filename resultFile) 
 { 
  return true; 
 } 
 public void info(String msg,Location location) 
  throws XMException 
 { 
  addMarker(msg,location,IMarker.SEVERITY_INFO,IMarker.PRIORITY_NORMAL); 
 } 
 public void info(String pattern,Object[] arguments,Location location) 
  throws XMException 
 { 
  info(MessageFormat.format(pattern,arguments),location); 
 } 
 public void begin(String source,String target) 
  throws XMException 
 { 
  try 
  { 
   project.deleteMarkers(MARKER_ID,true,IResource.DEPTH_INFINITE); 
   noMarkerSoFar = true; 
  } 
  catch(CoreException e) 
  { 
   throw new XMException(e); 
  } 
 } 
 public void end() 
 { 
 } 
 protected void showMarkerVIEw(IMarker marker) 
 { 
  Display display = Display.getCurrent(); 
  if(display == null) 
   display = Display.getDefault(); 
  ShowMarkerView showMarkerView = new ShowMarkerVIEw(workbench,marker); 
  display.syncExec(showMarkerVIEw); 
 } 
} 


 進一步抽象

  XM 一直圍繞這兩個組件來組織:核心,獨立於 Eclipse 並提供命令行界面;Eclipse 插件。要將 XM 移植到其他界面,只需要像 Messenger(請參閱 上一節)那樣抽象用戶界面的接口即可。我曾經為一些項目定義了 Eclipse 之上的 servlet 用戶界面。

  雖然我計劃進一步加強 XM 與 Eclipse 的集成,但是也想保留命令行選項。兩種界面各有自己的用途。對於日常操作而言,我多數時間都在用 Eclipse 環境,但是命令行版本對於 crontab(計劃工作執行的一種 UNIX 工具)非常方便。為了同時支持兩種方式,我抽象了 XM 核心引擎中的資源和文件。

  最初的 XM 使用的是 JDK File 對象,以後您會看到它是造成多數集成問題的根源,Eclipse 沒有使用 File 對象。相反,它使用了自己的 IResource 接口。此外,經驗告訴我,依靠 File 有很大的局限性。Eclipse 不是惟一沒有使用文件的軟件包,SAX 使用 InputSource,而 JAXP 使用 Source。

  如果代碼需要和幾種不同的庫進行交互該怎麼辦?可以使用代理模式來抽象各個庫。在代理模式中,由一個(或多個)對象為底層的庫提供通用的接口。可以實例化該對象,把請求轉發給任何一個庫。采用這種模式的好處是,調用代碼時無需擔心代理要轉發的庫。

  XM 引入 Filename 接口來抽象文件或資源的概念。 Filename 已經在 Eclipse IResource(為了在 Eclipse 內使用)和 JDK File 對象(為了在命令行中使用)上得以實現。清單 3 是 Filename 的聲明。Eclipse 專用版提供了源代碼。


清單 3. 文件和資源的抽象
package org.ananas.xm.core; 
import Java.io.File; 
import org.XML.sax.InputSource; 
import org.ananas.xm.core.XMException; 
public interface Filename 
  extends CoreConstants 
{ 
  public boolean isRoot() 
   throws XMException; 
  public boolean isFile(); 
  public boolean isFolder() 
   throws XMException; 
  public boolean exists() 
   throws XMException; 
  public String getName() 
   throws XMException; 
  public String getShortName() 
   throws XMException; 
  public String getSuffix() 
   throws XMException; 
  public String getProjectPath() 
   throws XMException; 
  public Filename getParent() 
   throws XMException; 
  public Filename[] getChildren() 
   throws XMException; 
  public void setPersistentMetadata(String key,String value) 
   throws XMException; 
  public void setPersistentMetadata(String key,String[] values) 
   throws XMException; 
  public void setTransIEntMetadata(String key,Object value) 
   throws XMException; 
  public Object getMetadata(String key) 
   throws XMException; 
  public String getMetadataAsString(String key) 
   throws XMException, ClassCastException; 
  public String[] getMetadataAsArray(String key) 
   throws XMException, ClassCastException; 
  public File asFile() 
   throws XMException; 
  public InputSource asInputSource() 
   throws XMException; 
  public Object ASPlatformSpecific() 
   throws XMException; 
  
  public boolean hasSamePath(Filename document) 
   throws XMException; 
  public boolean isDescendantOf(Filename document) 
   throws XMException; 
  public boolean remove() 
   throws XMException; 
} 

  XM 核心中的所有類(如 Messenger)都使用 Filename 進行了改寫。

  結束語

  這兩年中,Eclipse 已經成為 Java 平台事實上的標准開源 IDE,因此加強 Eclipse 對 XM 的支持非常必要。

  更多采用 Eclipse 的好處之一是,能夠使現有的更多文檔可用,使編寫插件更容易。在贊美 Eclipse 的同時,我仍然相信抽象插件的核心是值得的。對於 XM,我選擇了抽象用戶界面和資源管理。在下一期文章中,我將開始討論 XM 用戶界面的另一個主要問題:Eclipse 構建。

 

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved