【摘 要】本文通過開發(fā)一個JSP 編輯器插件的示例,介紹了 Eclipse 中設(shè)置 JSP 斷點(diǎn)的方法,以及如何遠(yuǎn)程調(diào)試 JSP。作為基礎(chǔ)知識,本文的前兩部分描述了 JAVA Debug 和 JSR-45 的基本原理。
本文通過開發(fā)一個JSP 編輯器插件的示例,介紹了 Eclipse 中設(shè)置 JSP 斷點(diǎn)的方法,以及如何遠(yuǎn)程調(diào)試 JSP。作為基礎(chǔ)知識,本文的前兩部分描述了 JAVA Debug 和 JSR-45 的基本原理。
環(huán)境要求: 本文的代碼是在 Eclipse3.0.0,JDK1.4.2 和 Tomcat5.0.5 上測試過的。
JAVA 調(diào)試框架(JPDA)簡介
JPDA 是一個多層的調(diào)試框架,包括 JVMDI、JDWP、JDI 三個層次。JAVA 虛擬機(jī)提供了 JPDA 的實(shí)現(xiàn)。其開發(fā)工具作為調(diào)試客戶端,可以方便的與虛擬機(jī)通訊,進(jìn)行調(diào)試。Eclipse 正是利用 JPDA 調(diào)試 JAVA 應(yīng)用,事實(shí)上,所有 JAVA 開發(fā)工具都是這樣做的。SUN JDK 還帶了一個比較簡單的調(diào)試工具以及示例。
參見:http://java.sun.com/j2se/1.4.2/docs/guide/jpda/architecture.html
Eclipse作為一個基于 JAVA 的調(diào)試客戶端,利用 org.eclipse.jdt.debug Plugin 提供了JDI 的具體實(shí)現(xiàn)。JDI 接口主要包含下面 4 個包
JSR-45(Debugging Support for Other Languages)為那些非 JAVA 語言寫成,卻需要編譯成 JAVA 代碼,運(yùn)行在 JVM 中的程序,提供了一個進(jìn)行調(diào)試的標(biāo)準(zhǔn)機(jī)制。也許字面的意思有點(diǎn)不好理解,什么算是非 JAVA 語言呢?其實(shí) JSP 就是一個再好不過的例子,JSR-45 的樣例就是一個 JSP。
JSP的調(diào)試一直依賴于具體應(yīng)用服務(wù)器的實(shí)現(xiàn),沒有一個統(tǒng)一的模式,JSR-45 針對這種情況,提供了一個標(biāo)準(zhǔn)的模式。我們知道,JAVA 的調(diào)試中,主要根據(jù)行號作為標(biāo)志,進(jìn)行定位。但是 JSP 被編譯為 JAVA 代碼之后,JAVA 行號與 JSP 行號無法一一對應(yīng),怎樣解決呢?
JSR-45 是這樣規(guī)定的:JSP 被編譯成 JAVA 代碼時,同時生成一份 JSP 文件名和行號與 JAVA 行號之間的對應(yīng)表(SMAP)。JVM 在接受到調(diào)試客戶端請求后,可以根據(jù)這個對應(yīng)表(SMAP),從 JSP 的行號轉(zhuǎn)換到 JAVA 代碼的行號;JVM 發(fā)出事件通知前, 也根據(jù)對應(yīng)表(SMAP)進(jìn)行轉(zhuǎn)化,直接將 JSP 的文件名和行號通知調(diào)試客戶端。
我們用 Tomcat 5.0 做個測試,有兩個 JSP,Hello.jsp 和 greeting.jsp,前者 include 后者。Tomcat會將他們編譯成 JAVA 代碼(Hello_jsp.java),JAVA Class(Hello_jsp.class) 以及 JSP 文件名/行號和 JAVA 行號之間的對應(yīng)表(SMAP)。
Hello.jsp:
greeting.jsp:
1 Hello There!<P> 2 Goodbye on <%= new java.util.Date() %>
JSP 編譯后產(chǎn)生的Hello_jsp.java 如下:
Tomcat 又將這個 JAVA 代碼編譯為 Hello_jsp.class,他們位于: $Tomcat_install_path$\work\Standalone\localhost\_ 目錄下。但是 JSP 文件名/行號和 JAVA 行號的對應(yīng)表(以下簡稱SMAP) 在哪里呢?答案是,它保存在 Class 中。如果用 UltraEdit 打開這個 Class 文件,就可以找到 SourceDebugExtension 屬性,這個屬性用來保存 SMAP。
JVM 規(guī)范定義了 ClassFile 中可以包含 SourceDebugExtension 屬性,保存 SMAP:
我用 javassist 做了一個測試(javassist可是一個好東東,它可以動態(tài)改變Class的結(jié)構(gòu),JBOSS 的 AOP就利用了javassist,這里我們只使用它讀取ClassFile的屬性)
這段代碼顯示了SourceDebugExtension 屬性,你可以看到SMAP 的內(nèi)容。編譯JSP后,SMAP 就被寫入 Class 中, 你也可以利用 javassist 修改 ClassFile 的屬性。
下面就是 Hello_jsp.class 中保存的 SMAP 內(nèi)容:
首先注明JAVA代碼的名稱:Hello_jsp.java,然后是 stratum 名稱: JSP。隨后是兩個JSP文件的名稱 :Hello.jsp、greeting.jsp。兩個JSP文件共10行,產(chǎn)生的Hello_jsp共69行代碼。最后也是最重要的內(nèi)容就是源文件文件名/行號和目標(biāo)文件行號的對應(yīng)關(guān)系(*L 與 *E之間的部分)
在規(guī)范定義了這樣的格式:
源文件行號 # 源文件代號,重復(fù)次數(shù) : 目標(biāo)文件開始行號,目標(biāo)文件行號每次增加的數(shù)量
(InputStartLine # LineFileID , RepeatCount : OutputStartLine , OutputLineIncrement)
源文件行號(InputStartLine) 目標(biāo)文件開始行號(OutputStartLine) 是必須的。下面是對這個SMAP具體的說明:
開發(fā)一個JSP編輯器
Eclipse 提供了 TextEditor,作為文本編輯器的父類。由于 Editor 的開發(fā)不是本文的重點(diǎn),不做具體論述。我們可以利用 Eclipse 的 Plugin 項(xiàng)目向?qū)В梢粋€簡單的 JSP 編輯器:
(1)點(diǎn)擊 File 菜單,New -> Project -> Plug-in Project ;
(2)輸入項(xiàng)目名稱 JSP_DEBUG,下一步;
(3)輸入 plugin ID : com.jsp.debug
Plugin Class name : com.jsp.debug.JSP_DebugPlugin
(4)選擇用模板創(chuàng)建
使用 Plug-in with editor,輸入
Java Package Name :com.jsp.editors
Editor Class Name :JSPEditor
File extension :jsp
一個 jsp editor 就產(chǎn)生了。
運(yùn)行這個Plugin,新建一個JAVA項(xiàng)目,新建一個 Hello.jsp 和 greeting.jsp,在 Navigator 視圖雙擊 jsp,這個editor就打開了。
在JSP編輯器中設(shè)置斷點(diǎn)
在編輯器中添加斷點(diǎn)的操作方式有兩種,一種是在編輯器左側(cè)垂直標(biāo)尺上雙擊,另一種是在左側(cè)垂直標(biāo)尺上點(diǎn)擊鼠標(biāo)右鍵,選擇菜單"添加/刪除斷點(diǎn)"。
在 Eclipse 的實(shí)現(xiàn)中,添加斷點(diǎn)實(shí)際上就是為 IFile 添加一個marker ,類型是IBreakpoint.BREAKPOINT_MARKER,然后將斷點(diǎn)注冊到 BreakpointManager。
BreakpointManager 將產(chǎn)生一個 BreakpointRequest,通知正在運(yùn)行的JVM Target,如果此時還沒有啟動 JVM,會在 JVM 啟動的時候,將所有斷點(diǎn)一起通知 JVM Target。
添加斷點(diǎn)使用一個 AbstractRulerActionDelegate,重載 createAction 方法,返回一個 IAction ManageBreakpointRulerAction動作:
為了將 ManageBreakpointRulerActionDelegate 添加到文本編輯器左側(cè)標(biāo)尺的鼠標(biāo)右鍵菜單,并且能夠處理左側(cè)標(biāo)尺的鼠標(biāo)雙擊事件,在 plugin.xml 中加入定義。
處理雙擊事件:
添加右鍵菜單:
ManageBreakpointRulerAction 是實(shí)際添加斷點(diǎn)的Action,實(shí)現(xiàn)了 IUpdate 接口,這個Action的工作,就是判斷當(dāng)前選中行是否存在斷點(diǎn)類型的 Marker,如果不存在創(chuàng)建一個,如果存在,將它刪除。
環(huán)境要求: 本文的代碼是在 Eclipse3.0.0,JDK1.4.2 和 Tomcat5.0.5 上測試過的。
JAVA 調(diào)試框架(JPDA)簡介
JPDA 是一個多層的調(diào)試框架,包括 JVMDI、JDWP、JDI 三個層次。JAVA 虛擬機(jī)提供了 JPDA 的實(shí)現(xiàn)。其開發(fā)工具作為調(diào)試客戶端,可以方便的與虛擬機(jī)通訊,進(jìn)行調(diào)試。Eclipse 正是利用 JPDA 調(diào)試 JAVA 應(yīng)用,事實(shí)上,所有 JAVA 開發(fā)工具都是這樣做的。SUN JDK 還帶了一個比較簡單的調(diào)試工具以及示例。
- JVMDI 定義了虛擬機(jī)需要實(shí)現(xiàn)的本地接口
- JDWP 定義了JVM與調(diào)試客戶端之間的通訊協(xié)議
調(diào)試客戶端和JVM 既可以在同一臺機(jī)器上,也可以遠(yuǎn)程調(diào)試。JDK 會包含一個默認(rèn)的實(shí)現(xiàn) jdwp.dll,JVM 允許靈活的使用其他協(xié)議代替 JDWP。SUN JDK 有兩種方式傳輸通訊協(xié)議:Socket 和共享內(nèi)存(后者僅僅針對 Windows),一般我們都采用 Socket 方式。
你可以用下面的參數(shù),以調(diào)試模式啟動JVM
-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n -Xrunjdwp JVM 加載 jdwp.dll transport=dt_socket 使用 Socket 傳輸 address 表示調(diào)試端口號 server=y 表示 JVM 作為服務(wù)器,建立 Socket suspend=n 表示啟動過程中,JVM 不會掛起去等待調(diào)試客戶端連接
- JDI 則是一組JAVA接口
如果是一個 JAVA 的調(diào)試客戶端,只要實(shí)現(xiàn) JDI 接口,利用JDWP協(xié)議,與虛擬機(jī)通訊,就可以調(diào)用JVMDI了。
Components Debugger Interface / |-----------------------| / | VM | debuggee ----( |-----------------------| <------- JVMDI - Java VM Debug Interface \ | back-end | \ |-----------------------| / | comm channel -( | <--------------- JDWP - Java Debug Wire Protocol \ | |---------------------| | front-end | |---------------------| <------- JDI - Java Debug Interface | UI | |---------------------|
參見:http://java.sun.com/j2se/1.4.2/docs/guide/jpda/architecture.html
Eclipse作為一個基于 JAVA 的調(diào)試客戶端,利用 org.eclipse.jdt.debug Plugin 提供了JDI 的具體實(shí)現(xiàn)。JDI 接口主要包含下面 4 個包
com.sun.jdi com.sun.jdi.connect com.sun.jdi.event com.sun.jdi.request本文不對 JDI 進(jìn)行深入闡述,這里重點(diǎn)介紹 JDI 中與斷點(diǎn)相關(guān)的接口。
- com.sun.jdi
主要是JVM(VirtualMachine) 線程(ThreadReference) 調(diào)用棧(StackFrame) 以及類型、實(shí)例的描述。利用這組接口,調(diào)試客戶端可以用類似類反射的方式,得到所有類型的定義,動態(tài)調(diào)用 Class 的方法。 - com.sun.jdi.event
封裝了JVM 產(chǎn)生的事件, JVM 正是將這些事件通知給調(diào)試客戶端的。例如 BreakpointEvent 就是 JVM 執(zhí)行到斷點(diǎn)的時候,發(fā)出的事件;ClassPrepareEvent就是 Class 被加載時發(fā)出的事件。
- com.sun.jdi.request
封裝了調(diào)試客戶端可以向 JVM發(fā)起的請求。例如 BreakpointRequest 向 JVM 發(fā)起一個添加斷點(diǎn)的請求;ClassPrepareRequest 向 JVM 注冊一個類加載請求,JVM 在加載指定 Class 的時候,就會發(fā)出一個 ClassPrepareEvent 事件。
JSR-45(Debugging Support for Other Languages)為那些非 JAVA 語言寫成,卻需要編譯成 JAVA 代碼,運(yùn)行在 JVM 中的程序,提供了一個進(jìn)行調(diào)試的標(biāo)準(zhǔn)機(jī)制。也許字面的意思有點(diǎn)不好理解,什么算是非 JAVA 語言呢?其實(shí) JSP 就是一個再好不過的例子,JSR-45 的樣例就是一個 JSP。
JSP的調(diào)試一直依賴于具體應(yīng)用服務(wù)器的實(shí)現(xiàn),沒有一個統(tǒng)一的模式,JSR-45 針對這種情況,提供了一個標(biāo)準(zhǔn)的模式。我們知道,JAVA 的調(diào)試中,主要根據(jù)行號作為標(biāo)志,進(jìn)行定位。但是 JSP 被編譯為 JAVA 代碼之后,JAVA 行號與 JSP 行號無法一一對應(yīng),怎樣解決呢?
JSR-45 是這樣規(guī)定的:JSP 被編譯成 JAVA 代碼時,同時生成一份 JSP 文件名和行號與 JAVA 行號之間的對應(yīng)表(SMAP)。JVM 在接受到調(diào)試客戶端請求后,可以根據(jù)這個對應(yīng)表(SMAP),從 JSP 的行號轉(zhuǎn)換到 JAVA 代碼的行號;JVM 發(fā)出事件通知前, 也根據(jù)對應(yīng)表(SMAP)進(jìn)行轉(zhuǎn)化,直接將 JSP 的文件名和行號通知調(diào)試客戶端。
我們用 Tomcat 5.0 做個測試,有兩個 JSP,Hello.jsp 和 greeting.jsp,前者 include 后者。Tomcat會將他們編譯成 JAVA 代碼(Hello_jsp.java),JAVA Class(Hello_jsp.class) 以及 JSP 文件名/行號和 JAVA 行號之間的對應(yīng)表(SMAP)。
Hello.jsp:
1 <HTML> 2 <HEAD> 3 <TITLE>Hello Example</TITLE> 4 </HEAD> 5 <BODY> 6 <%@ include file="greeting.jsp" %> 7 </BODY> 8 </HTML>
greeting.jsp:
1 Hello There!<P> 2 Goodbye on <%= new java.util.Date() %>
JSP 編譯后產(chǎn)生的Hello_jsp.java 如下:
Hello_jsp.java: 1 package org.apache.jsp; 2 3 import javax.servlet.*; 4 import javax.servlet.http.*; 5 import javax.servlet.jsp.*; 6 7 public final class Hello_jsp extends org.apache.jasper.runtime.HttpJspBase 8 implements org.apache.jasper.runtime.JspSourceDependent { 9 10 private static java.util.Vector _jspx_dependants; 11 12 static { 13 _jspx_dependants = new java.util.Vector(1); 14 _jspx_dependants.add("/greeting.jsp"); 15 } 16 17 public java.util.List getDependants() { 18 return _jspx_dependants; 19 } 20 21 public void _jspService(HttpServletRequest request, HttpServletResponse response) 22 throws java.io.IOException, ServletException { 23 24 JspFactory _jspxFactory = null; 25 PageContext pageContext = null; 26 HttpSession session = null; 27 ServletContext application = null; 28 ServletConfig config = null; 29 JspWriter out = null; 30 Object page = this; 31 JspWriter _jspx_out = null; 32 33 34 try { 35 _jspxFactory = JspFactory.getDefaultFactory(); 36 response.setContentType("text/html"); 37 pageContext = _jspxFactory.getPageContext(this, request, response, 38 null, true, 8192, true); 39 application = pageContext.getServletContext(); 40 config = pageContext.getServletConfig(); 41 session = pageContext.getSession(); 42 out = pageContext.getOut(); 43 _jspx_out = out; 44 45 out.write("<HTML> \r\n"); 46 out.write("<HEAD> \r\n"); 47 out.write("<TITLE>Hello Example"); 48 out.write("</TITLE> \r\n"); 49 out.write("</HEAD> \r\n"); 50 out.write("<BODY> \r\n"); 51 out.write("Hello There!"); 52 out.write("<P> \r\nGoodbye on "); 53 out.write(String.valueOf( new java.util.Date() )); 54 out.write(" \r\n"); 55 out.write(" \r\n"); 56 out.write("</BODY> \r\n"); 57 out.write("</HTML> \r\n"); 58 } catch (Throwable t) { 59 if (!(t instanceof javax.servlet.jsp.SkipPageException)){ 60 out = _jspx_out; 61 if (out != null && out.getBufferSize() != 0) 62 out.clearBuffer(); 63 if (pageContext != null) pageContext.handlePageException(t); 64 } 65 } finally { 66 if (_jspxFactory != null) _jspxFactory.releasePageContext ( pageContext); 67 } 68 } 69 }
Tomcat 又將這個 JAVA 代碼編譯為 Hello_jsp.class,他們位于: $Tomcat_install_path$\work\Standalone\localhost\_ 目錄下。但是 JSP 文件名/行號和 JAVA 行號的對應(yīng)表(以下簡稱SMAP) 在哪里呢?答案是,它保存在 Class 中。如果用 UltraEdit 打開這個 Class 文件,就可以找到 SourceDebugExtension 屬性,這個屬性用來保存 SMAP。
JVM 規(guī)范定義了 ClassFile 中可以包含 SourceDebugExtension 屬性,保存 SMAP:
SourceDebugExtension_attribute { u2 attribute_name_index; u4 attribute_length; u1 debug_extension[attribute_length]; }
我用 javassist 做了一個測試(javassist可是一個好東東,它可以動態(tài)改變Class的結(jié)構(gòu),JBOSS 的 AOP就利用了javassist,這里我們只使用它讀取ClassFile的屬性)
public static void main(String[] args) throws Exception{ String[]files = { "E:\\Tomcat5_0_5\\work\\Catalina\\localhost\\_\\org\\apache\\jsp\\Hello_jsp.class", }; for(int k = 0; k < files.length; k++){ String file = files[k]; System.out.println("Class : " + file); ClassFile classFile = new ClassFile(new DataInputStream(new FileInputStream(file))); AttributeInfo attributeInfo = classFile.getAttribute("SourceDebugExtension"); System.out.println("attribute name :" + attributeInfo.getName() + "]\n\n"); byte[]bytes = attributeInfo.get(); String str = new String(bytes); System.out.println(str); } }
這段代碼顯示了SourceDebugExtension 屬性,你可以看到SMAP 的內(nèi)容。編譯JSP后,SMAP 就被寫入 Class 中, 你也可以利用 javassist 修改 ClassFile 的屬性。
下面就是 Hello_jsp.class 中保存的 SMAP 內(nèi)容:
SMAP E:\Tomcat5_0_5\work\Catalina\localhost\_\org\apache\jsp\Hello_jsp.java JSP *S JSP *F + 0 Hello.jsp /Hello.jsp + 1 greeting.jsp /greeting.jsp *L 1:45 2:46 3:47 3:48 4:49 5:50 1#1:51 1:52 2:53 7#0:56 8:57 *E
首先注明JAVA代碼的名稱:Hello_jsp.java,然后是 stratum 名稱: JSP。隨后是兩個JSP文件的名稱 :Hello.jsp、greeting.jsp。兩個JSP文件共10行,產(chǎn)生的Hello_jsp共69行代碼。最后也是最重要的內(nèi)容就是源文件文件名/行號和目標(biāo)文件行號的對應(yīng)關(guān)系(*L 與 *E之間的部分)
在規(guī)范定義了這樣的格式:
源文件行號 # 源文件代號,重復(fù)次數(shù) : 目標(biāo)文件開始行號,目標(biāo)文件行號每次增加的數(shù)量
(InputStartLine # LineFileID , RepeatCount : OutputStartLine , OutputLineIncrement)
源文件行號(InputStartLine) 目標(biāo)文件開始行號(OutputStartLine) 是必須的。下面是對這個SMAP具體的說明:
1:45 2:46 3:47 3:48 4:49 5:50(沒有源文件代號,默認(rèn)為Hello.jsp) 開始行號 結(jié)束行號 Hello.jsp: 1 -> Hello_jsp.java: 45 2 -> 46 3 -> 47 48 4 -> 49 5 -> 50 1#1:51 1:52 2:53(1#1表示 greeting.jsp 的第1行) greeting.jsp: 1 -> Hello_jsp.java: 51 52 2 -> 53 7#0:56 8:57(7#0表示 Hello.jsp 的第7行) Hello.jsp: 7 -> Hello_jsp.java: 56 8 -> 57
Eclipse 提供了 TextEditor,作為文本編輯器的父類。由于 Editor 的開發(fā)不是本文的重點(diǎn),不做具體論述。我們可以利用 Eclipse 的 Plugin 項(xiàng)目向?qū)В梢粋€簡單的 JSP 編輯器:
(1)點(diǎn)擊 File 菜單,New -> Project -> Plug-in Project ;
(2)輸入項(xiàng)目名稱 JSP_DEBUG,下一步;
(3)輸入 plugin ID : com.jsp.debug
Plugin Class name : com.jsp.debug.JSP_DebugPlugin
(4)選擇用模板創(chuàng)建
使用 Plug-in with editor,輸入
Java Package Name :com.jsp.editors
Editor Class Name :JSPEditor
File extension :jsp
一個 jsp editor 就產(chǎn)生了。
運(yùn)行這個Plugin,新建一個JAVA項(xiàng)目,新建一個 Hello.jsp 和 greeting.jsp,在 Navigator 視圖雙擊 jsp,這個editor就打開了。
在JSP編輯器中設(shè)置斷點(diǎn)
在編輯器中添加斷點(diǎn)的操作方式有兩種,一種是在編輯器左側(cè)垂直標(biāo)尺上雙擊,另一種是在左側(cè)垂直標(biāo)尺上點(diǎn)擊鼠標(biāo)右鍵,選擇菜單"添加/刪除斷點(diǎn)"。
在 Eclipse 的實(shí)現(xiàn)中,添加斷點(diǎn)實(shí)際上就是為 IFile 添加一個marker ,類型是IBreakpoint.BREAKPOINT_MARKER,然后將斷點(diǎn)注冊到 BreakpointManager。
BreakpointManager 將產(chǎn)生一個 BreakpointRequest,通知正在運(yùn)行的JVM Target,如果此時還沒有啟動 JVM,會在 JVM 啟動的時候,將所有斷點(diǎn)一起通知 JVM Target。
添加斷點(diǎn)使用一個 AbstractRulerActionDelegate,重載 createAction 方法,返回一個 IAction ManageBreakpointRulerAction動作:
public class ManageBreakpointRulerActionDelegate extends AbstractRulerActionDelegate{ protected IAction createAction(ITextEditor editor, IVerticalRulerInfo rulerInfo) { return new ManageBreakpointRulerAction(rulerInfo, editor); } }
為了將 ManageBreakpointRulerActionDelegate 添加到文本編輯器左側(cè)標(biāo)尺的鼠標(biāo)右鍵菜單,并且能夠處理左側(cè)標(biāo)尺的鼠標(biāo)雙擊事件,在 plugin.xml 中加入定義。
處理雙擊事件:
<extension point="org.eclipse.ui.editorActions"> <editorContribution targetID="com.jiaoly.editors.JSPEditor" id="com.jiaoly.debug.ManageBreakpointRulerActionDelegate"> <action label="添加/刪除斷點(diǎn)" class="com.jiaoly.debug.ManageBreakpointRulerActionDelegate" actionID="RulerDoubleClick" id="com.jiaoly.debug.ManageBreakpointRulerActionDelegate"> </action> </editorContribution> </extension>
添加右鍵菜單:
<extension point="org.eclipse.ui.popupMenus"> <viewerContribution targetID="#TextRulerContext" id="com.jiaoly.debug.ManageBreakpointRulerActionDelegate"> <action label="添加/刪除斷點(diǎn)" class="com.jiaoly.debug.ManageBreakpointRulerActionDelegate" menubarPath="addition" id="com.jiaoly.debug.ManageBreakpointRulerActionDelegate"> </action> </viewerContribution> </extension>
ManageBreakpointRulerAction 是實(shí)際添加斷點(diǎn)的Action,實(shí)現(xiàn)了 IUpdate 接口,這個Action的工作,就是判斷當(dāng)前選中行是否存在斷點(diǎn)類型的 Marker,如果不存在創(chuàng)建一個,如果存在,將它刪除。
public class ManageBreakpointRulerAction extends Action implements IUpdate{ private IVerticalRulerInfo rulerInfo; private ITextEditor textEditor; private String BPmarkerType ; //當(dāng)點(diǎn)Marker的類型 private List allMarkers; //當(dāng)前鼠標(biāo)點(diǎn)擊行所有的Marker private String addBP; //Action 的顯示名稱 public ManageBreakpointRulerAction(IVerticalRulerInfo ruler, ITextEditor editor){ this.rulerInfo = ruler; this.textEditor = editor; BPmarkerType = IBreakpoint.BREAKPOINT_MARKER; addBP = "添加/刪除斷點(diǎn)"; //$NON-NLS-1$ setText(this.addBP); } public void update() { this.allMarkers = this.fetchBPMarkerList(); } public void run(){ if(this.allMarkers.isEmpty()) this.addMarker(); else this.removeMarkers(this.allMarkers); } }